Added MD5 (md5mesh works, md5anim has not yet been tested) and STL. new unittests, although not yet complete (material system, normals). Bugfixes (GFn and MDL7). Added HMP5 support. Rewrote MD2 and MD3 to be more stable.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@77 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
pull/1/head
aramis_acg 2008-08-06 23:01:38 +00:00
parent b17a5e3b69
commit 2f36cc5f5f
45 changed files with 3625 additions and 932 deletions

View File

@ -94,7 +94,7 @@ bool ASEImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
// NOTE: Sometimes the extension .ASK is also used
// however, often it only contains static animation skeletons
// without the real animations.
// without real animations.
if (extension[3] != 'e' && extension[3] != 'E' &&
extension[3] != 'k' && extension[3] != 'K')return false;
@ -114,14 +114,9 @@ void ASEImporter::InternReadFile(
}
size_t fileSize = file->FileSize();
std::string::size_type pos = pFile.find_last_of('.');
std::string extension = pFile.substr( pos);
if(extension[3] == 'k' || extension[3] == 'K')
{
this->mIsAsk = true;
}
else this->mIsAsk = false;
this->mIsAsk = (extension[3] == 'k' || extension[3] == 'K');
// allocate storage and copy the contents of the file to a memory buffer
// (terminate it with zero)
@ -132,61 +127,69 @@ void ASEImporter::InternReadFile(
// construct an ASE parser and parse the file
this->mParser = new ASE::Parser((const char*)this->mBuffer);
this->mParser->Parse();
// if absolutely no material has been loaded from the file
// we need to generate a default material
this->GenerateDefaultMaterial();
// process all meshes
std::vector<aiMesh*> avOutMeshes;
avOutMeshes.reserve(this->mParser->m_vMeshes.size()*2);
for (std::vector<ASE::Mesh>::iterator
i = this->mParser->m_vMeshes.begin();
i != this->mParser->m_vMeshes.end();++i)
try
{
if ((*i).bSkip)continue;
this->mParser->Parse();
// transform all vertices into worldspace
// world2obj transform is specified in the
// transformation matrix of a scenegraph node
this->TransformVertices(*i);
// if absolutely no material has been loaded from the file
// we need to generate a default material
this->GenerateDefaultMaterial();
// now we need to create proper meshes from the import
// we need to split them by materials, build valid vertex/face lists ...
this->BuildUniqueRepresentation(*i);
// process all meshes
std::vector<aiMesh*> avOutMeshes;
avOutMeshes.reserve(this->mParser->m_vMeshes.size()*2);
for (std::vector<ASE::Mesh>::iterator
i = this->mParser->m_vMeshes.begin();
i != this->mParser->m_vMeshes.end();++i)
{
if ((*i).bSkip)continue;
// need to generate proper vertex normals if necessary
this->GenerateNormals(*i);
// transform all vertices into worldspace
// world2obj transform is specified in the
// transformation matrix of a scenegraph node
this->TransformVertices(*i);
// now we need to create proper meshes from the import we need to
// split them by materials, build valid vertex/face lists ...
this->BuildUniqueRepresentation(*i);
// need to generate proper vertex normals if necessary
this->GenerateNormals(*i);
// convert all meshes to aiMesh objects
this->ConvertMeshes(*i,avOutMeshes);
}
// now build the output mesh list. remove dummies
pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
for (std::vector<aiMesh*>::const_iterator
i = avOutMeshes.begin();
i != avOutMeshes.end();++i)
{
if (!(*i)->mNumFaces)continue;
*pp++ = *i;
}
pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
// buil final material indices (remove submaterials and make the final list)
this->BuildMaterialIndices();
// build the final node graph
this->BuildNodes();
// build output animations
this->BuildAnimations();
// convert all meshes to aiMesh objects
this->ConvertMeshes(*i,avOutMeshes);
}
// now build the output mesh list. remove dummies
pScene->mNumMeshes = (unsigned int)avOutMeshes.size();
aiMesh** pp = pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
for (std::vector<aiMesh*>::const_iterator
i = avOutMeshes.begin();
i != avOutMeshes.end();++i)
catch (ImportErrorException* ex)
{
if (!(*i)->mNumFaces)continue;
*pp++ = *i;
delete this->mParser; AI_DEBUG_INVALIDATE_PTR( this->mParser );
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR( this->mBuffer );
throw ex;
}
pScene->mNumMeshes = (unsigned int)(pp - pScene->mMeshes);
// buil final material indices (remove submaterials and make the final list)
this->BuildMaterialIndices();
// build the final node graph
this->BuildNodes();
// build output animations
this->BuildAnimations();
// delete the ASE parser
delete this->mParser;
this->mParser = NULL;
delete this->mParser; AI_DEBUG_INVALIDATE_PTR( this->mParser );
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR( this->mBuffer );
return;
}
// ------------------------------------------------------------------------------------------------
@ -333,11 +336,7 @@ void ASEImporter::AddNodes(aiNode* pcParent,const char* szName,
aiMatrix4x4 mParentAdjust = decompTrafo.mMatrix;
mParentAdjust.Inverse();
//if(ComputeLocalToWorldShift(mParentAdjust, decompTrafo, mesh.inherit))
{
node->mTransformation = mParentAdjust*mesh.mTransform;
}
//else node->mTransformation = mesh.mTransform;
node->mTransformation = mParentAdjust*mesh.mTransform;
// Transform all vertices of the mesh back into their local space ->
// at the moment they are pretransformed
@ -352,8 +351,6 @@ void ASEImporter::AddNodes(aiNode* pcParent,const char* szName,
pvCurPtr++;
}
//pcMesh->mColors[2] = NULL;
// add sub nodes
aiMatrix4x4 mNewAbs = decompTrafo.mMatrix * node->mTransformation;
ASE::DecompTransform dec( mNewAbs);
@ -1242,8 +1239,7 @@ void ASEImporter::GenerateNormals(ASE::Mesh& mesh)
vNormals += mesh.mNormals[(*a)];
fDiv += 1.0f;
}
vNormals.x /= fDiv;vNormals.y /= fDiv;vNormals.z /= fDiv;
vNormals.Normalize();
vNormals /= fDiv;
avNormals[(*i).mIndices[c]] = vNormals;
poResult.clear();
}

View File

@ -55,9 +55,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
// boost headers
#include <boost/scoped_ptr.hpp>
using namespace Assimp;
using namespace Assimp::ASE;

View File

@ -43,11 +43,16 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
* tangents and bitangents for all imported meshes
*/
// STL headers
#include <vector>
#include <assert.h>
#include "../include/DefaultLogger.h"
// internal headers
#include "CalcTangentsProcess.h"
#include "SpatialSort.h"
// public ASSIMP headers
#include "../include/DefaultLogger.h"
#include "../include/aiPostProcess.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
@ -85,7 +90,7 @@ void CalcTangentsProcess::Execute( aiScene* pScene)
for( unsigned int a = 0; a < pScene->mNumMeshes; a++)
if(ProcessMesh( pScene->mMeshes[a]))bHas = true;
if (bHas)DefaultLogger::get()->debug("CalcTangentsProcess finished. There was much work to do ...");
if (bHas)DefaultLogger::get()->debug("CalcTangentsProcess finished. Tangents have been calculated");
else DefaultLogger::get()->debug("CalcTangentsProcess finished");
}

View File

@ -50,25 +50,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenFaceNormalsProcess::GenFaceNormalsProcess()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenFaceNormalsProcess::~GenFaceNormalsProcess()
{
// nothing to do here
}
// -------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenFaceNormalsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_GenNormals) != 0;
}
// -------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenFaceNormalsProcess::Execute( aiScene* pScene)
{
@ -86,10 +88,10 @@ void GenFaceNormalsProcess::Execute( aiScene* pScene)
"Face normals have been calculated");
}
else DefaultLogger::get()->debug("GenFaceNormalsProcess finished. "
"There was nothing to do");
"Normals are already there");
}
// -------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
{
@ -109,8 +111,8 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh)
aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2;
if (face.mIndices[1] > face.mIndices[2])
vNor *= -1.0f;
//if (face.mIndices[1] > face.mIndices[2])
// vNor *= -1.0f;
for (unsigned int i = 0;i < face.mNumIndices;++i)
{

View File

@ -51,25 +51,27 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
GenVertexNormalsProcess::GenVertexNormalsProcess()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
GenVertexNormalsProcess::~GenVertexNormalsProcess()
{
// nothing to do here
}
// -------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Returns whether the processing step is present in the given flag field.
bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const
{
return (pFlags & aiProcess_GenSmoothNormals) != 0;
}
// -------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
void GenVertexNormalsProcess::Execute( aiScene* pScene)
{
@ -88,10 +90,10 @@ void GenVertexNormalsProcess::Execute( aiScene* pScene)
"Vertex normals have been calculated");
}
else DefaultLogger::get()->debug("GenVertexNormalsProcess finished. "
"There was nothing to do");
"Normals are already there");
}
// -------------------------------------------------------------------
// ------------------------------------------------------------------------------------------------
// Executes the post processing step on the given imported data.
bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
{
@ -110,8 +112,8 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
aiVector3D pDelta2 = *pV3 - *pV1;
aiVector3D vNor = pDelta1 ^ pDelta2;
if (face.mIndices[1] > face.mIndices[2])
vNor *= -1.0f;
/*if (face.mIndices[1] > face.mIndices[2])
vNor *= -1.0f;*/
for (unsigned int i = 0;i < face.mNumIndices;++i)
{
@ -154,7 +156,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh)
pcNor /= (float) verticesFound.size();
pcNew[i] = pcNor;
}
delete pMesh->mNormals;
delete[] pMesh->mNormals;
pMesh->mNormals = pcNew;
return true;

View File

@ -44,8 +44,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "BaseProcess.h"
#include "../include/aiMesh.h"
namespace Assimp
{
class GenNormalsTest;
namespace Assimp {
// ---------------------------------------------------------------------------
/** The GenFaceNormalsProcess computes vertex normals for all vertizes of all meshes
@ -53,6 +53,7 @@ namespace Assimp
class ASSIMP_API GenVertexNormalsProcess : public BaseProcess
{
friend class Importer;
friend class ::GenNormalsTest;
protected:
/** Constructor to be privately used by Importer */

View File

@ -41,9 +41,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/** @file Implementation of the MDL importer class */
// internal headers
#include "MaterialSystem.h"
#include "HMPLoader.h"
#include "MD2FileData.h"
// public ASSIMP headers
#include "../include/DefaultLogger.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
@ -51,12 +54,11 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
// boost headers
#include <boost/scoped_ptr.hpp>
using namespace Assimp;
extern float g_avNormals[162][3];
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
HMPImporter::HMPImporter()
@ -103,12 +105,12 @@ void HMPImporter::InternReadFile(
throw new ImportErrorException( "Failed to open HMP file " + pFile + ".");
}
// check whether the ply file is large enough to contain
// check whether the HMP file is large enough to contain
// at least the file header
size_t fileSize = file->FileSize();
if( fileSize < 50)
{
throw new ImportErrorException( ".hmp File is too small.");
throw new ImportErrorException( "HMP File is too small.");
}
// allocate storage and copy the contents of the file to a memory buffer
@ -147,9 +149,17 @@ void HMPImporter::InternReadFile(
}
else
{
// print the magic word to the logger
char szBuffer[5];
szBuffer[0] = ((char*)&iMagic)[0];
szBuffer[1] = ((char*)&iMagic)[1];
szBuffer[2] = ((char*)&iMagic)[2];
szBuffer[3] = ((char*)&iMagic)[3];
szBuffer[4] = '\0';
// we're definitely unable to load this file
throw new ImportErrorException( "Unknown HMP subformat " + pFile +
". Magic word is not known");
". Magic word (" + szBuffer + ") is not known");
}
} catch (ImportErrorException* ex) {
@ -161,39 +171,42 @@ void HMPImporter::InternReadFile(
delete[] this->mBuffer;
return;
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::ValidateHeader_HMP457( )
{
const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)this->mBuffer;
if (120 > this->iFileSize)
{
throw new ImportErrorException("HMP file is too small (header size is "
"120 bytes, this file is smaller)");
}
if (!pcHeader->ftrisize_x || !pcHeader->ftrisize_y)
{
throw new ImportErrorException("Size of triangles in either x or y direction is zero");
}
if(pcHeader->fnumverts_x < 1.0f || (pcHeader->numverts/pcHeader->fnumverts_x) < 1.0f)
{
throw new ImportErrorException("Number of triangles in either x or y direction is zero");
}
if(!pcHeader->numframes)
{
throw new ImportErrorException("There are no frames. At least one should be there");
}
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::InternReadFile_HMP4( )
{
throw new ImportErrorException("HMP4 is currently not supported");
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::InternReadFile_HMP5( )
{
throw new ImportErrorException("HMP4 is currently not supported");
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::InternReadFile_HMP7( )
{
if (120 > this->iFileSize)
{
throw new ImportErrorException("HMP7 file is too small (header size is "
"120 bytes, this file is smaller)");
}
// read the file header and skip everything to byte 84
const HMP::Header_HMP5* pcHeader = (const HMP::Header_HMP5*)this->mBuffer;
const unsigned char* szCurrent = (const unsigned char*)(this->mBuffer+84);
if (!pcHeader->ftrisize_x || !pcHeader->ftrisize_y ||
pcHeader->fnumverts_x < 1.0f || (pcHeader->numverts/pcHeader->fnumverts_x) < 1.0f ||
!pcHeader->numframes)
{
throw new ImportErrorException("One or more of the values in the HMP7 "
"file header are invalid.");
}
this->ValidateHeader_HMP457();
// generate an output mesh
this->pScene->mNumMeshes = 1;
@ -207,6 +220,120 @@ void HMPImporter::InternReadFile_HMP7( )
const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
// generate/load a material for the terrain
this->CreateMaterial(szCurrent,&szCurrent);
// goto offset 120, I don't know why ...
// (fixme) is this the frame header? I assume yes since it starts with 2.
szCurrent += 36;
this->SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
// now load all vertices from the file
aiVector3D* pcVertOut = pcMesh->mVertices;
aiVector3D* pcNorOut = pcMesh->mNormals;
const HMP::Vertex_HMP5* src = (const HMP::Vertex_HMP5*) szCurrent;
for (unsigned int y = 0; y < height;++y)
{
for (unsigned int x = 0; x < width;++x)
{
pcVertOut->x = x * pcHeader->ftrisize_x;
pcVertOut->y = y * pcHeader->ftrisize_y;
pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
MD2::LookupNormalIndex(src->normals162index, *pcNorOut );
++pcVertOut;++pcNorOut;++src;
}
}
// generate texture coordinates if necessary
if (pcHeader->numskins)this->GenerateTextureCoords(width,height);
// now build a list of faces
this->CreateOutputFaceList(width,height);
// there is no nodegraph in HMP files. Simply assign the one mesh
// (no, not the one ring) to the root node
this->pScene->mRootNode = new aiNode();
this->pScene->mRootNode->mName.Set("terrain_root");
this->pScene->mRootNode->mNumMeshes = 1;
this->pScene->mRootNode->mMeshes = new unsigned int[1];
this->pScene->mRootNode->mMeshes[0] = 0;
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::InternReadFile_HMP7( )
{
// read the file header and skip everything to byte 84
const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)this->mBuffer;
const unsigned char* szCurrent = (const unsigned char*)(this->mBuffer+84);
this->ValidateHeader_HMP457();
// generate an output mesh
this->pScene->mNumMeshes = 1;
this->pScene->mMeshes = new aiMesh*[1];
aiMesh* pcMesh = this->pScene->mMeshes[0] = new aiMesh();
pcMesh->mMaterialIndex = 0;
pcMesh->mVertices = new aiVector3D[pcHeader->numverts];
pcMesh->mNormals = new aiVector3D[pcHeader->numverts];
const unsigned int height = (unsigned int)(pcHeader->numverts / pcHeader->fnumverts_x);
const unsigned int width = (unsigned int)pcHeader->fnumverts_x;
// generate/load a material for the terrain
this->CreateMaterial(szCurrent,&szCurrent);
// goto offset 120, I don't know why ...
// (fixme) is this the frame header? I assume yes since it starts with 2.
szCurrent += 36;
this->SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
// now load all vertices from the file
aiVector3D* pcVertOut = pcMesh->mVertices;
aiVector3D* pcNorOut = pcMesh->mNormals;
const HMP::Vertex_HMP7* src = (const HMP::Vertex_HMP7*) szCurrent;
for (unsigned int y = 0; y < height;++y)
{
for (unsigned int x = 0; x < width;++x)
{
pcVertOut->x = x * pcHeader->ftrisize_x;
pcVertOut->y = y * pcHeader->ftrisize_y;
// FIXME: What exctly is the correct scaling factor to use?
// possibly pcHeader->scale_origin[2] in combination with a
// signed interpretation of src->z?
pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
pcNorOut->x = ((float)src->normal_x / 0x80 ); // * pcHeader->scale_origin[0];
pcNorOut->y = ((float)src->normal_y / 0x80 ); // * pcHeader->scale_origin[1];
pcNorOut->z = 1.0f;
pcNorOut->Normalize();
++pcVertOut;++pcNorOut;++src;
}
}
// generate texture coordinates if necessary
if (pcHeader->numskins)this->GenerateTextureCoords(width,height);
// now build a list of faces
this->CreateOutputFaceList(width,height);
// there is no nodegraph in HMP files. Simply assign the one mesh
// (no, not the One Ring) to the root node
this->pScene->mRootNode = new aiNode();
this->pScene->mRootNode->mName.Set("terrain_root");
this->pScene->mRootNode->mNumMeshes = 1;
this->pScene->mRootNode->mMeshes = new unsigned int[1];
this->pScene->mRootNode->mMeshes[0] = 0;
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::CreateMaterial(const unsigned char* szCurrent,
const unsigned char** szCurrentOut)
{
aiMesh* const pcMesh = this->pScene->mMeshes[0];
const HMP::Header_HMP5* const pcHeader = (const HMP::Header_HMP5*)this->mBuffer;
// we don't need to generate texture coordinates if
// we have no textures in the file ...
if (pcHeader->numskins)
@ -225,7 +352,7 @@ void HMPImporter::InternReadFile_HMP7( )
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.7f;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
@ -241,45 +368,14 @@ void HMPImporter::InternReadFile_HMP7( )
this->pScene->mMaterials = new aiMaterial*[1];
this->pScene->mMaterials[0] = pcHelper;
}
*szCurrentOut = szCurrent;
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::CreateOutputFaceList(unsigned int width,unsigned int height)
{
aiMesh* const pcMesh = this->pScene->mMeshes[0];
// goto offset 120, I don't know why ...
// (fixme) is this the frame header? I assume yes since it starts
// with 2.
szCurrent += 36;
this->SizeCheck(szCurrent + sizeof(const HMP::Vertex_HMP7)*height*width);
// now load all vertices from the file
aiVector3D* pcVertOut = pcMesh->mVertices;
aiVector3D* pcNorOut = pcMesh->mNormals;
const HMP::Vertex_HMP7* src = (const HMP::Vertex_HMP7*) szCurrent;
for (unsigned int y = 0; y < height;++y)
{
for (unsigned int x = 0; x < width;++x)
{
pcVertOut->x = x * pcHeader->ftrisize_x;
pcVertOut->y = y * pcHeader->ftrisize_y;
// FIXME: What exctly is the correct scaling factor to use?
// possibly pcHeader->scale_origin[2] in combination with a
// signed interpretation of src->z?
pcVertOut->z = (((float)src->z / 0xffff)-0.5f) * pcHeader->ftrisize_x * 8.0f;
pcNorOut->x = ((float)src->normal_x / 0x80 ); // * pcHeader->scale_origin[0];
pcNorOut->y = ((float)src->normal_y / 0x80 ); // * pcHeader->scale_origin[1];
pcNorOut->z = 1.0f;
pcNorOut->Normalize();
++pcVertOut;++pcNorOut;++src;
}
}
// generate texture coordinates if necessary
if (pcHeader->numskins)
{
this->GenerateTextureCoords(width,height);
}
// now build a list of faces
// allocate enough storage
const unsigned int iNumSquares = (width-1) * (height-1);
pcMesh->mNumFaces = iNumSquares << 1;
pcMesh->mFaces = new aiFace[pcMesh->mNumFaces];
@ -289,12 +385,13 @@ void HMPImporter::InternReadFile_HMP7( )
aiVector3D* pcNormals = new aiVector3D[pcMesh->mNumVertices];
aiFace* pcFaceOut(pcMesh->mFaces);
pcVertOut = pcVertices;
pcNorOut = pcNormals;
aiVector3D* pcVertOut = pcVertices;
aiVector3D* pcNorOut = pcNormals;
aiVector3D* pcUVs = pcMesh->mTextureCoords[0] ? new aiVector3D[pcMesh->mNumVertices] : NULL;
aiVector3D* pcUVOut(pcUVs);
// build the terrain square
unsigned int iCurrent = 0;
for (unsigned int y = 0; y < height-1;++y)
{
@ -360,16 +457,6 @@ void HMPImporter::InternReadFile_HMP7( )
delete[] pcMesh->mTextureCoords[0];
pcMesh->mTextureCoords[0] = pcUVs;
}
// there is no nodegraph in HMP files. Simply assign the one mesh
// (no, not the one ring) to the root node
this->pScene->mRootNode = new aiNode();
this->pScene->mRootNode->mName.Set("terrain_root");
this->pScene->mRootNode->mNumMeshes = 1;
this->pScene->mRootNode->mMeshes = new unsigned int[1];
this->pScene->mRootNode->mMeshes[0] = 0;
return;
}
// ------------------------------------------------------------------------------------------------
void HMPImporter::ReadFirstSkin(unsigned int iNumSkins, const unsigned char* szCursor,
@ -433,15 +520,16 @@ void HMPImporter::GenerateTextureCoords(
aiVector3D* uv = this->pScene->mMeshes[0]->mTextureCoords[0];
const float fX = (1.0f / height) + (1.0f / height) / (height-1);
const float fY = (1.0f / width) + (1.0f / width) / (width-1);
const float fY = (1.0f / height) + (1.0f / height) / (height-1);
const float fX = (1.0f / width) + (1.0f / width) / (width-1);
for (unsigned int y = 0; y < height;++y)
{
for (unsigned int x = 0; x < width;++x)
{
uv->x = fX*x;
uv->y = 1.0f-fY*y;
uv->x = fX*x;
uv->z = 0.0f;
++uv;
}
}

View File

@ -70,6 +70,16 @@ class MaterialHelper;
namespace HMP
{
// ugly compiler dependent packing stuff
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack(push,1)
# define PACK_STRUCT
#elif defined( __GNUC__ )
# define PACK_STRUCT __attribute__((packed))
#else
# error Compiler not supported. Never do this again.
#endif
// ---------------------------------------------------------------------------
/** Data structure for the header of a HMP5 file.
* This is also used by HMP4 and HMP7, but with modifications
@ -111,8 +121,27 @@ struct Header_HMP5
int32_t num_stverts;
int32_t flags;
float size;
};
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** Data structure for a terrain vertex in a HMP4 file
*/
struct Vertex_HMP4
{
uint16_t p_pos[3];
uint8_t normals162index;
uint8_t pad;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** Data structure for a terrain vertex in a HMP5 file
*/
struct Vertex_HMP5
{
uint16_t z;
uint8_t normals162index;
uint8_t pad;
} PACK_STRUCT;
// ---------------------------------------------------------------------------
/** Data structure for a terrain vertex in a HMP7 file
@ -121,7 +150,13 @@ struct Vertex_HMP7
{
uint16_t z;
int8_t normal_x,normal_y;
};
} PACK_STRUCT;
// reset packing to the original value
#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__)
# pragma pack( pop )
#endif
#undef PACK_STRUCT
}; //! namespace HMP
@ -182,6 +217,25 @@ protected:
*/
void InternReadFile_HMP7( );
// -------------------------------------------------------------------
/** Validate a HMP 5,4,7 file header
*/
void ValidateHeader_HMP457( );
// -------------------------------------------------------------------
/** Try to load one material from the file, if this fails create
* a default material
*/
void CreateMaterial(const unsigned char* szCurrent,
const unsigned char** szCurrentOut);
// -------------------------------------------------------------------
/** Build a list of output faces and vertices. The function
* triangulates the height map read from the file
* \param width Width of the height field
* \param width Height of the height field
*/
void CreateOutputFaceList(unsigned int width,unsigned int height);
// -------------------------------------------------------------------
/** Generate planar texture coordinates for a terrain

67
code/Hash.h 100644
View File

@ -0,0 +1,67 @@
#ifndef AI_HASH_H_INCLUDED
#define AI_HASH_H_INCLUDED
// ------------------------------------------------------------------------------------------------
// hashing function taken from
// http://www.azillionmonkeys.com/qed/hash.html
// (incremental version of the hashing function)
// (stdint.h should have been been included here)
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
// ------------------------------------------------------------------------------------------------
inline uint32_t SuperFastHash (const char * data, int len, uint32_t hash = 0) {
uint32_t tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += get16bits (data);
tmp = (get16bits (data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof (uint16_t);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += get16bits (data);
hash ^= hash << 16;
hash ^= data[sizeof (uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += get16bits (data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
#endif // !! AI_HASH_H_INCLUDED

View File

@ -92,6 +92,12 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#if (!defined AI_BUILD_NO_SMD_IMPORTER)
# include "SMDLoader.h"
#endif
#if (!defined AI_BUILD_NO_MD5_IMPORTER)
# include "MD5Loader.h"
#endif
#if (!defined AI_BUILD_NO_STL_IMPORTER)
# include "STLLoader.h"
#endif
// PostProcess-Steps
#if (!defined AI_BUILD_NO_CALCTANGENTS_PROCESS)
@ -177,12 +183,18 @@ Importer::Importer() :
#if (!defined AI_BUILD_NO_ASE_IMPORTER)
mImporter.push_back( new ASEImporter());
#endif
#if (!defined AI_BUILD_NO_HMP_IMPORTER)
#if (!defined AI_BUILD_NO_HMP_IMPORTER)
mImporter.push_back( new HMPImporter());
#endif
#if (!defined AI_BUILD_NO_SMD_IMPORTER)
#if (!defined AI_BUILD_NO_SMD_IMPORTER)
mImporter.push_back( new SMDImporter());
#endif
#if (!defined AI_BUILD_NO_MD5_IMPORTER)
mImporter.push_back( new MD5Importer());
#endif
#if (!defined AI_BUILD_NO_STL_IMPORTER)
mImporter.push_back( new STLImporter());
#endif
// add an instance of each post processing step here in the order
// of sequence it is executed

View File

@ -39,8 +39,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the post processing step to join identical vertices
* for all imported meshes
/** @file Implementation of the post processing to improve the
* cache locality of a mesh.
* <br>
* The algorithm is roughly basing on this paper:
* http://www.cs.princeton.edu/gfx/pubs/Sander_2007_%3ETR/tipsy.pdf
@ -167,6 +167,17 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
}
delete[] piFIFOStack;
float fACMR = (float)iCacheMisses / pMesh->mNumFaces;
if (3.0 == fACMR)
{
char szBuff[128]; // should be sufficiently large in every case
// the JoinIdenticalVertices process has not been executed on this
// mesh, otherwise this value would normally be at least minimally#
// smaller than 3.0 ...
::sprintf(szBuff,"Mesh %i: JIV-Step has not been executed properly (precondition)",meshNum);
DefaultLogger::get()->warn(szBuff);
return;
}
// first we need to build a vertex-triangle adjacency list
VertexTriangleAdjacency adj(pMesh->mFaces,pMesh->mNumFaces, pMesh->mNumVertices,true);
@ -351,7 +362,7 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN
char szBuff[128]; // should be sufficiently large in every case
float fACMR2 = (float)iCacheMisses / pMesh->mNumFaces;
sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
::sprintf(szBuff,"Mesh %i | ACMR in: %f out: %f | ~%.1f%%",meshNum,fACMR,fACMR2,
((fACMR - fACMR2) / fACMR) * 100.f);
DefaultLogger::get()->info(szBuff);
}

View File

@ -129,7 +129,6 @@ void MD2Importer::ValidateHeader( )
this->m_pcHeader->offsetEnd > this->fileSize)
{
throw new ImportErrorException("Invalid MD2 header: some offsets are outside the file");
delete[] this->mBuffer;
}
if (this->m_pcHeader->numSkins > AI_MD2_MAX_SKINS)
@ -142,7 +141,7 @@ void MD2Importer::ValidateHeader( )
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void MD2Importer::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile));
@ -160,160 +159,159 @@ void MD2Importer::InternReadFile(
throw new ImportErrorException( "md2 File is too small.");
}
// allocate storage and copy the contents of the file to a memory buffer
this->mBuffer = new unsigned char[fileSize];
file->Read( (void*)mBuffer, 1, fileSize);
this->m_pcHeader = (const MD2::Header*)this->mBuffer;
// check magic number
if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
try
{
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md2 file: Magic bytes not found");
}
// allocate storage and copy the contents of the file to a memory buffer
this->mBuffer = new unsigned char[fileSize];
file->Read( (void*)mBuffer, 1, fileSize);
// check file format version
if (this->m_pcHeader->version != 8)
{
DefaultLogger::get()->warn( "Unsupported md2 file version. Continuing happily ...");
}
this->ValidateHeader();
this->m_pcHeader = (const MD2::Header*)this->mBuffer;
// check some values whether they are valid
if (0 == this->m_pcHeader->numFrames)
{
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
}
if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
{
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md2 file: File is too small");
}
// there won't be more than one mesh inside the file
pScene->mNumMaterials = 1;
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = new MaterialHelper();
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
// navigate to the begin of the frame data
const MD2::Frame* pcFrame = (const MD2::Frame*) (
(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetFrames);
// navigate to the begin of the triangle data
MD2::Triangle* pcTriangles = (MD2::Triangle*) (
(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetTriangles);
// navigate to the begin of the tex coords data
const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) (
(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetTexCoords);
// navigate to the begin of the vertex data
const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
pcMesh->mNumFaces = this->m_pcHeader->numTriangles;
pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
// allocate output storage
pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
// not sure whether there are MD2 files without texture coordinates
// NOTE: texture coordinates can be there without a texture,
// but a texture can't be there without a valid UV channel
if (this->m_pcHeader->numTexCoords && this->m_pcHeader->numSkins)
{
// navigate to the first texture associated with the mesh
const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader +
this->m_pcHeader->offsetSkins);
const int iMode = (int)aiShadingMode_Gouraud;
MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 1.0f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
if (pcSkins->name[0])
// check magic number
if (this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_BE &&
this->m_pcHeader->magic != AI_MD2_MAGIC_NUMBER_LE)
{
aiString szString;
const size_t iLen = ::strlen(pcSkins->name);
::memcpy(szString.data,pcSkins->name,iLen);
szString.data[iLen] = '\0';
szString.length = iLen;
throw new ImportErrorException( "Invalid md2 file: Magic bytes not found");
}
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
// check file format version
if (this->m_pcHeader->version != 8)
{
DefaultLogger::get()->warn( "Unsupported md2 file version. Continuing happily ...");
}
this->ValidateHeader();
// check some values whether they are valid
if (0 == this->m_pcHeader->numFrames)
{
throw new ImportErrorException( "Invalid md2 file: NUM_FRAMES is 0");
}
if (this->m_pcHeader->offsetEnd > (int32_t)fileSize)
{
throw new ImportErrorException( "Invalid md2 file: File is too small");
}
// there won't be more than one mesh inside the file
pScene->mNumMaterials = 1;
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = new MaterialHelper();
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
aiMesh* pcMesh = pScene->mMeshes[0] = new aiMesh();
// navigate to the begin of the frame data
const MD2::Frame* pcFrame = (const MD2::Frame*) (
(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetFrames);
// navigate to the begin of the triangle data
MD2::Triangle* pcTriangles = (MD2::Triangle*) (
(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetTriangles);
// navigate to the begin of the tex coords data
const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) (
(unsigned char*)this->m_pcHeader + this->m_pcHeader->offsetTexCoords);
// navigate to the begin of the vertex data
const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices);
pcMesh->mNumFaces = this->m_pcHeader->numTriangles;
pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles];
// allocate output storage
pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3;
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
// not sure whether there are MD2 files without texture coordinates
// NOTE: texture coordinates can be there without a texture,
// but a texture can't be there without a valid UV channel
if (this->m_pcHeader->numTexCoords && this->m_pcHeader->numSkins)
{
// navigate to the first texture associated with the mesh
const MD2::Skin* pcSkins = (const MD2::Skin*) ((unsigned char*)this->m_pcHeader +
this->m_pcHeader->offsetSkins);
const int iMode = (int)aiShadingMode_Gouraud;
MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 1.0f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
if (pcSkins->name[0])
{
aiString szString;
const size_t iLen = ::strlen(pcSkins->name);
::memcpy(szString.data,pcSkins->name,iLen);
szString.data[iLen] = '\0';
szString.length = iLen;
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else
{
DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
}
}
else
{
DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped.");
// apply a default material
const int iMode = (int)aiShadingMode_Gouraud;
MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
}
}
else
{
// apply a default material
const int iMode = (int)aiShadingMode_Gouraud;
MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0];
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
}
// now read all triangles of the first frame, apply scaling and translation
unsigned int iCurrent = 0;
if (this->m_pcHeader->numTexCoords)
{
// allocate storage for texture coordinates, too
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
// now read all triangles of the first frame, apply scaling and translation
unsigned int iCurrent = 0;
// check whether the skin width or height are zero (this would
// cause a division through zero)
float fDivisorU;
if (!this->m_pcHeader->skinWidth)
float fDivisorU,fDivisorV;
if (this->m_pcHeader->numTexCoords)
{
DefaultLogger::get()->error("Skin width is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorU = 1.0f;
}
else fDivisorU = (float)this->m_pcHeader->skinWidth;
// allocate storage for texture coordinates, too
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
float fDivisorV;
if (!this->m_pcHeader->skinHeight)
{
DefaultLogger::get()->error("Skin height is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorV = 1.0f;
// check whether the skin width or height are zero (this would
// cause a division through zero)
if (!this->m_pcHeader->skinWidth)
{
DefaultLogger::get()->error("Skin width is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorU = 1.0f;
}
else fDivisorU = (float)this->m_pcHeader->skinWidth;
if (!this->m_pcHeader->skinHeight)
{
DefaultLogger::get()->error("Skin height is zero but there are "
"valid absolute texture coordinates. Unable to compute "
"relative texture coordinates ranging from 0 to 1");
fDivisorV = 1.0f;
}
else fDivisorV = (float)this->m_pcHeader->skinHeight;
}
else fDivisorV = (float)this->m_pcHeader->skinHeight;
for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
{
@ -357,22 +355,25 @@ void MD2Importer::InternReadFile(
LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
vNormal.y *= -1.0f;
// validate texture coordinates
if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
if (this->m_pcHeader->numTexCoords)
{
DefaultLogger::get()->error("UV index is outside the allowed range");
pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
// validate texture coordinates
if (pcTriangles[iIndex].textureIndices[c] >= this->m_pcHeader->numTexCoords)
{
DefaultLogger::get()->error("UV index is outside the allowed range");
pcTriangles[iIndex].textureIndices[c] = this->m_pcHeader->numTexCoords-1;
}
aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
float u,v;
// the texture coordinates are absolute values but we
// need relative values between 0 and 1
u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / fDivisorU;
v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / fDivisorV;
pcOut.x = u;
pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2?
}
aiVector3D& pcOut = pcMesh->mTextureCoords[0][iCurrent];
float u,v;
// the texture coordinates are absolute values but we
// need relative values between 0 and 1
u = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].s / fDivisorU;
v = (float)pcTexCoords[pcTriangles[i].textureIndices[c]].t / fDivisorV;
pcOut.x = u;
pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2?
}
// FIX: flip the face order for use with OpenGL
pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
@ -380,53 +381,10 @@ void MD2Importer::InternReadFile(
pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
}
}
else
catch (ImportErrorException* ex)
{
for (unsigned int i = 0; i < (unsigned int)this->m_pcHeader->numTriangles;++i)
{
// allocate the face
pScene->mMeshes[0]->mFaces[i].mIndices = new unsigned int[3];
pScene->mMeshes[0]->mFaces[i].mNumIndices = 3;
// copy texture coordinates
// check whether they are different from the previous value at this index.
// In this case, create a full separate set of vertices/normals/texcoords
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent)
{
// validate vertex indices
if (pcTriangles[i].vertexIndices[c] >= this->m_pcHeader->numVertices)
{
DefaultLogger::get()->error("Vertex index is outside the allowed range");
pcTriangles[i].vertexIndices[c] = this->m_pcHeader->numVertices-1;
}
// copy face indices
unsigned int iIndex = (unsigned int)pcTriangles[i].vertexIndices[c];
// read x,y, and z component of the vertex
aiVector3D& vec = pcMesh->mVertices[iCurrent];
vec.x = (float)pcVerts[iIndex].vertex[0] * pcFrame->scale[0];
vec.x += pcFrame->translate[0];
vec.z = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1];
vec.z += pcFrame->translate[1];
vec.y = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2];
vec.y += pcFrame->translate[2];
vec.y *= -1.f;
// read the normal vector from the precalculated normal table
aiVector3D& vNormal = pcMesh->mNormals[iCurrent];
LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal);
vNormal.y *= -1.0f;
}
// FIX: flip the face order for use with OpenGL
pScene->mMeshes[0]->mFaces[i].mIndices[0] = iTemp+2;
pScene->mMeshes[0]->mFaces[i].mIndices[1] = iTemp+1;
pScene->mMeshes[0]->mFaces[i].mIndices[2] = iTemp+0;
}
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw ex;
}
// delete the file buffer and return
delete[] this->mBuffer;
return;
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
}

View File

@ -90,11 +90,20 @@ bool MD3Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
// ------------------------------------------------------------------------------------------------
void MD3Importer::ValidateHeaderOffsets()
{
// check file format version
if (this->m_pcHeader->VERSION > 15)
DefaultLogger::get()->warn( "Unsupported md3 file version. Continuing happily ...");
// check some values whether they are valid
if (0 == this->m_pcHeader->NUM_FRAMES)
throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
if (0 == this->m_pcHeader->NUM_SURFACES)
throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0");
if (this->m_pcHeader->OFS_FRAMES >= this->fileSize ||
this->m_pcHeader->OFS_SURFACES >= this->fileSize ||
this->m_pcHeader->OFS_EOF > this->fileSize)
{
delete[] this->mBuffer;
throw new ImportErrorException("Invalid MD3 header: some offsets are outside the file");
}
}
@ -109,7 +118,6 @@ void MD3Importer::ValidateSurfaceHeaderOffsets(const MD3::Surface* pcSurf)
pcSurf->OFS_ST + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::TexCoord) > this->fileSize ||
pcSurf->OFS_XYZNORMAL + ofs + pcSurf->NUM_VERTICES * sizeof(MD3::Vertex) > this->fileSize)
{
delete[] this->mBuffer;
throw new ImportErrorException("Invalid MD3 surface header: some offsets are outside the file");
}
@ -147,277 +155,259 @@ void MD3Importer::InternReadFile(
this->mBuffer = new unsigned char[fileSize];
file->Read( (void*)mBuffer, 1, fileSize);
this->m_pcHeader = (const MD3::Header*)this->mBuffer;
// check magic number
if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
try
{
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md3 file: Magic bytes not found");
}
// check file format version
if (this->m_pcHeader->VERSION > 15)
{
DefaultLogger::get()->warn( "Unsupported md3 file version. Continuing happily ...");
}
this->m_pcHeader = (const MD3::Header*)this->mBuffer;
// check some values whether they are valid
if (0 == this->m_pcHeader->NUM_FRAMES)
{
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md3 file: NUM_FRAMES is 0");
}
if (0 == this->m_pcHeader->NUM_SURFACES)
{
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md3 file: NUM_SURFACES is 0");
}
this->ValidateHeaderOffsets();
// now navigate to the list of surfaces
const MD3::Surface* pcSurfaces = (const MD3::Surface*)
(this->mBuffer + this->m_pcHeader->OFS_SURFACES);
// allocate output storage
pScene->mNumMeshes = this->m_pcHeader->NUM_SURFACES;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
// if an exception is thrown before the meshes are allocated ->
// otherwise the pointer value would be invalid and delete would crash
::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
unsigned int iNum = this->m_pcHeader->NUM_SURFACES;
unsigned int iNumMaterials = 0;
unsigned int iDefaultMatIndex = 0xFFFFFFFF;
while (iNum-- > 0)
{
// validate the surface
this->ValidateSurfaceHeaderOffsets(pcSurfaces);
// navigate to the vertex list of the surface
const MD3::Vertex* pcVertices = (const MD3::Vertex*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
// navigate to the triangle list of the surface
const MD3::Triangle* pcTriangles = (const MD3::Triangle*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
// navigate to the texture coordinate list of the surface
const MD3::TexCoord* pcUVs = (const MD3::TexCoord*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_ST);
// navigate to the shader list of the surface
const MD3::Shader* pcShaders = (const MD3::Shader*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
// if the submesh is empty ignore it
if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
// check magic number
if (this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_BE &&
this->m_pcHeader->IDENT != AI_MD3_MAGIC_NUMBER_LE)
{
pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
pScene->mNumMeshes--;
continue;
throw new ImportErrorException( "Invalid md3 file: Magic bytes not found");
}
// validate the header
this->ValidateHeaderOffsets();
// allocate the output mesh
pScene->mMeshes[iNum] = new aiMesh();
aiMesh* pcMesh = pScene->mMeshes[iNum];
// now navigate to the list of surfaces
const MD3::Surface* pcSurfaces = (const MD3::Surface*)
(this->mBuffer + this->m_pcHeader->OFS_SURFACES);
pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
//pcMesh->mNumBones = 0;
//pcMesh->mColors[0] = pcMesh->mColors[1] = pcMesh->mColors[2] = pcMesh->mColors[3] = NULL;
pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
//pcMesh->mTextureCoords[1] = pcMesh->mTextureCoords[2] = pcMesh->mTextureCoords[3] = NULL;
pcMesh->mNumUVComponents[0] = 2;
// allocate output storage
pScene->mNumMeshes = this->m_pcHeader->NUM_SURFACES;
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
// fill in all triangles
unsigned int iCurrent = 0;
for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i)
pScene->mNumMaterials = this->m_pcHeader->NUM_SURFACES;
pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
// if an exception is thrown before the meshes are allocated ->
// otherwise the pointer value would be invalid and delete would crash
::memset(pScene->mMeshes,0,pScene->mNumMeshes*sizeof(aiMesh*));
::memset(pScene->mMaterials,0,pScene->mNumMaterials*sizeof(aiMaterial*));
unsigned int iNum = this->m_pcHeader->NUM_SURFACES;
unsigned int iNumMaterials = 0;
unsigned int iDefaultMatIndex = 0xFFFFFFFF;
while (iNum-- > 0)
{
pcMesh->mFaces[i].mIndices = new unsigned int[3];
pcMesh->mFaces[i].mNumIndices = 3;
// validate the surface
this->ValidateSurfaceHeaderOffsets(pcSurfaces);
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent)
// navigate to the vertex list of the surface
const MD3::Vertex* pcVertices = (const MD3::Vertex*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_XYZNORMAL);
// navigate to the triangle list of the surface
const MD3::Triangle* pcTriangles = (const MD3::Triangle*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_TRIANGLES);
// navigate to the texture coordinate list of the surface
const MD3::TexCoord* pcUVs = (const MD3::TexCoord*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_ST);
// navigate to the shader list of the surface
const MD3::Shader* pcShaders = (const MD3::Shader*)
(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_SHADERS);
// if the submesh is empty ignore it
if (0 == pcSurfaces->NUM_VERTICES || 0 == pcSurfaces->NUM_TRIANGLES)
{
// read vertices
pcMesh->mVertices[iCurrent].x = pcVertices[ pcTriangles->INDEXES[c]].X;
pcMesh->mVertices[iCurrent].y = pcVertices[ pcTriangles->INDEXES[c]].Y*-1.0f;
pcMesh->mVertices[iCurrent].z = pcVertices[ pcTriangles->INDEXES[c]].Z;
// convert the normal vector to uncompressed float3 format
LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,
(float*)&pcMesh->mNormals[iCurrent]);
pcMesh->mNormals[iCurrent].y *= -1.0f;
// read texture coordinates
pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
}
// FIX: flip the face ordering for use with OpenGL
pcMesh->mFaces[i].mIndices[0] = iTemp+2;
pcMesh->mFaces[i].mIndices[1] = iTemp+1;
pcMesh->mFaces[i].mIndices[2] = iTemp+0;
pcTriangles++;
}
// get the first shader (= texture?) assigned to the surface
if (0 != pcSurfaces->NUM_SHADER)
{
// make a relative path.
// if the MD3's internal path itself and the given path are using
// the same directory remove it
const char* szEndDir1 = ::strrchr((const char*)this->m_pcHeader->NAME,'\\');
if (!szEndDir1)szEndDir1 = ::strrchr((const char*)this->m_pcHeader->NAME,'/');
const char* szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'\\');
if (!szEndDir2)szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'/');
if (szEndDir1 && szEndDir2)
{
// both of them are valid
const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)this->m_pcHeader->NAME);
const unsigned int iLen2 = std::min (iLen1, (unsigned int)(szEndDir2 - (const char*)pcShaders->NAME) );
bool bSuccess = true;
for (unsigned int a = 0; a < iLen2;++a)
{
char sz = tolower ( pcShaders->NAME[a] );
char sz2 = tolower ( this->m_pcHeader->NAME[a] );
if (sz != sz2)
{
bSuccess = false;
break;
}
}
if (bSuccess)
{
// use the file name only
szEndDir2++;
}
else
{
// use the full path
szEndDir2 = (const char*)pcShaders->NAME;
}
pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
pScene->mNumMeshes--;
continue;
}
// now try to find out whether we have this shader already
bool bHave = false;
for (unsigned int p = 0; p < iNumMaterials;++p)
{
if (iDefaultMatIndex == p)continue;
// allocate the output mesh
pScene->mMeshes[iNum] = new aiMesh();
aiMesh* pcMesh = pScene->mMeshes[iNum];
aiString szOut;
if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p],
AI_MATKEY_TEXTURE_DIFFUSE(0),&szOut))
pcMesh->mNumVertices = pcSurfaces->NUM_TRIANGLES*3;
pcMesh->mNumFaces = pcSurfaces->NUM_TRIANGLES;
pcMesh->mFaces = new aiFace[pcSurfaces->NUM_TRIANGLES];
pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices];
pcMesh->mNumUVComponents[0] = 2;
// fill in all triangles
unsigned int iCurrent = 0;
for (unsigned int i = 0; i < (unsigned int)pcSurfaces->NUM_TRIANGLES;++i)
{
pcMesh->mFaces[i].mIndices = new unsigned int[3];
pcMesh->mFaces[i].mNumIndices = 3;
unsigned int iTemp = iCurrent;
for (unsigned int c = 0; c < 3;++c,++iCurrent)
{
if (0 == ASSIMP_stricmp(szOut.data,szEndDir2))
{
// equal. reuse this material (texture)
bHave = true;
pcMesh->mMaterialIndex = p;
break;
}
// read vertices
pcMesh->mVertices[iCurrent].x = pcVertices[ pcTriangles->INDEXES[c]].X;
pcMesh->mVertices[iCurrent].y = pcVertices[ pcTriangles->INDEXES[c]].Y*-1.0f;
pcMesh->mVertices[iCurrent].z = pcVertices[ pcTriangles->INDEXES[c]].Z;
// convert the normal vector to uncompressed float3 format
LatLngNormalToVec3(pcVertices[pcTriangles->INDEXES[c]].NORMAL,
(float*)&pcMesh->mNormals[iCurrent]);
pcMesh->mNormals[iCurrent].y *= -1.0f;
// read texture coordinates
pcMesh->mTextureCoords[0][iCurrent].x = pcUVs[ pcTriangles->INDEXES[c]].U;
pcMesh->mTextureCoords[0][iCurrent].y = 1.0f-pcUVs[ pcTriangles->INDEXES[c]].V;
}
// FIX: flip the face ordering for use with OpenGL
pcMesh->mFaces[i].mIndices[0] = iTemp+2;
pcMesh->mFaces[i].mIndices[1] = iTemp+1;
pcMesh->mFaces[i].mIndices[2] = iTemp+0;
pcTriangles++;
}
if (!bHave)
// get the first shader (= texture?) assigned to the surface
if (0 != pcSurfaces->NUM_SHADER)
{
MaterialHelper* pcHelper = new MaterialHelper();
// make a relative path.
// if the MD3's internal path itself and the given path are using
// the same directory remove it
const char* szEndDir1 = ::strrchr((const char*)this->m_pcHeader->NAME,'\\');
if (!szEndDir1)szEndDir1 = ::strrchr((const char*)this->m_pcHeader->NAME,'/');
if (szEndDir2)
const char* szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'\\');
if (!szEndDir2)szEndDir2 = ::strrchr((const char*)pcShaders->NAME,'/');
if (szEndDir1 && szEndDir2)
{
if (szEndDir2[0])
{
aiString szString;
const size_t iLen = ::strlen(szEndDir2);
::memcpy(szString.data,szEndDir2,iLen);
szString.data[iLen] = '\0';
szString.length = iLen;
// both of them are valid
const unsigned int iLen1 = (unsigned int)(szEndDir1 - (const char*)this->m_pcHeader->NAME);
const unsigned int iLen2 = std::min (iLen1, (unsigned int)(szEndDir2 - (const char*)pcShaders->NAME) );
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else
bool bSuccess = true;
for (unsigned int a = 0; a < iLen2;++a)
{
DefaultLogger::get()->warn("Texture file name has zero length. "
"It will be skipped.");
char sz = ::tolower ( pcShaders->NAME[a] );
char sz2 = ::tolower ( this->m_pcHeader->NAME[a] );
if (sz != sz2)
{
bSuccess = false;
break;
}
}
if (bSuccess)
{
// use the file name only
szEndDir2++;
}
else
{
// use the full path
szEndDir2 = (const char*)pcShaders->NAME;
}
}
int iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
// now try to find out whether we have this shader already
bool bHave = false;
for (unsigned int p = 0; p < iNumMaterials;++p)
{
if (iDefaultMatIndex == p)continue;
// add a small ambient color value - Quake 3 seems to have one
aiColor3D clr;
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
aiString szOut;
if(AI_SUCCESS == aiGetMaterialString ( (aiMaterial*)pScene->mMaterials[p],
AI_MATKEY_TEXTURE_DIFFUSE(0),&szOut))
{
if (0 == ASSIMP_stricmp(szOut.data,szEndDir2))
{
// equal. reuse this material (texture)
bHave = true;
pcMesh->mMaterialIndex = p;
break;
}
}
}
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
if (!bHave)
{
MaterialHelper* pcHelper = new MaterialHelper();
pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
pcMesh->mMaterialIndex = iNumMaterials++;
}
}
else
{
if (0xFFFFFFFF != iDefaultMatIndex)
{
pcMesh->mMaterialIndex = iDefaultMatIndex;
if (szEndDir2)
{
if (szEndDir2[0])
{
aiString szString;
const size_t iLen = ::strlen(szEndDir2);
::memcpy(szString.data,szEndDir2,iLen);
szString.data[iLen] = '\0';
szString.length = iLen;
pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0));
}
else
{
DefaultLogger::get()->warn("Texture file name has zero length. "
"It will be skipped.");
}
}
int iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
// add a small ambient color value - Quake 3 seems to have one
aiColor3D clr;
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
pcMesh->mMaterialIndex = iNumMaterials++;
}
}
else
{
MaterialHelper* pcHelper = new MaterialHelper();
if (0xFFFFFFFF != iDefaultMatIndex)
{
pcMesh->mMaterialIndex = iDefaultMatIndex;
}
else
{
MaterialHelper* pcHelper = new MaterialHelper();
// fill in a default material
int iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
// fill in a default material
int iMode = (int)aiShadingMode_Gouraud;
pcHelper->AddProperty<int>(&iMode, 1, AI_MATKEY_SHADING_MODEL);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
aiColor3D clr;
clr.b = clr.g = clr.r = 0.6f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_DIFFUSE);
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_SPECULAR);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
clr.b = clr.g = clr.r = 0.05f;
pcHelper->AddProperty<aiColor3D>(&clr, 1,AI_MATKEY_COLOR_AMBIENT);
pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
pcMesh->mMaterialIndex = iNumMaterials++;
pScene->mMaterials[iNumMaterials] = (aiMaterial*)pcHelper;
pcMesh->mMaterialIndex = iNumMaterials++;
}
}
// go to the next surface
pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
}
// go to the next surface
pcSurfaces = (const MD3::Surface*)(((unsigned char*)pcSurfaces) + pcSurfaces->OFS_END);
}
if (0 == pScene->mNumMeshes)
if (0 == pScene->mNumMeshes)
throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh");
pScene->mNumMaterials = iNumMaterials;
// now we need to generate an empty node graph
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mRootNode->mMeshes[i] = i;
}
catch (ImportErrorException* ex)
{
// cleanup before returning
delete[] this->mBuffer;
throw new ImportErrorException( "Invalid md3 file: File contains no valid mesh");
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw ex;
}
pScene->mNumMaterials = iNumMaterials;
// now we need to generate an empty node graph
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = pScene->mNumMeshes;
pScene->mRootNode->mMeshes = new unsigned int[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
pScene->mRootNode->mMeshes[i] = i;
// delete the file buffer and return
delete[] this->mBuffer;
return;
delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
}

561
code/MD5Loader.cpp 100644
View File

@ -0,0 +1,561 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the MD5 importer class */
// internal headers
#include "MaterialSystem.h"
#include "RemoveComments.h"
#include "MD5Loader.h"
#include "StringComparison.h"
#include "fast_atof.h"
// public headers
#include "../include/DefaultLogger.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
// boost headers
#include <boost/scoped_ptr.hpp>
using namespace Assimp;
// we're just doing this with static buffers whose size is known at
// compile time, so the compiler should automatically expand to
// sprintf<array_length>(...)
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
MD5Importer::MD5Importer()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
MD5Importer::~MD5Importer()
{
// nothing to do here
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool MD5Importer::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
{
// simple check of file extension is enough for the moment
std::string::size_type pos = pFile.find_last_of('.');
// no file extension - can't read
if( pos == std::string::npos)
return false;
std::string extension = pFile.substr( pos);
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
if (extension[1] != 'm' && extension[1] != 'M')return false;
if (extension[2] != 'd' && extension[2] != 'D')return false;
if (extension[3] != '5')return false;
return true;
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void MD5Importer::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
// remove the file extension
std::string::size_type pos = pFile.find_last_of('.');
this->mFile = pFile.substr(0,pos+1);
this->pIOHandler = pIOHandler;
this->pScene = pScene;
bHadMD5Mesh = bHadMD5Anim = false;
// load the animation keyframes
this->LoadMD5AnimFile();
// load the mesh vertices and bones
this->LoadMD5MeshFile();
// make sure we return no incomplete data
if (!bHadMD5Mesh && !bHadMD5Anim)
{
throw new ImportErrorException("Failed to read valid data from this MD5");
}
if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY;
}
// ------------------------------------------------------------------------------------------------
void MD5Importer::LoadFileIntoMemory (IOStream* file)
{
ai_assert(NULL != file);
this->fileSize = (unsigned int)file->FileSize();
// allocate storage and copy the contents of the file to a memory buffer
this->pScene = pScene;
this->mBuffer = new char[this->fileSize+1];
file->Read( (void*)mBuffer, 1, this->fileSize);
this->iLineNumber = 1;
// append a terminal 0
this->mBuffer[this->fileSize] = '\0';
// now remove all line comments from the file
CommentRemover::RemoveLineComments("//",this->mBuffer,' ');
}
// ------------------------------------------------------------------------------------------------
void MD5Importer::UnloadFileFromMemory ()
{
// delete the file buffer
delete[] this->mBuffer;
this->mBuffer = NULL;
this->fileSize = 0;
}
// ------------------------------------------------------------------------------------------------
void MakeDataUnique (MD5::MeshDesc& meshSrc)
{
std::vector<bool> abHad(meshSrc.mVertices.size(),false);
// allocate enough storage to keep the output structures
const unsigned int iNewNum = (unsigned int)meshSrc.mFaces.size()*3;
unsigned int iNewIndex = (unsigned int)meshSrc.mVertices.size();
meshSrc.mVertices.resize(iNewNum);
// try to guess how much storage we'll need for new weights
const float fWeightsPerVert = meshSrc.mWeights.size() / (float)iNewIndex;
const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum);
meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();
iter != iterEnd;++iter)
{
const aiFace& face = *iter;
for (unsigned int i = 0; i < 3;++i)
{
if (abHad[face.mIndices[i]])
{
// generate a new vertex
meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
face.mIndices[i] = iNewIndex++;
// FIX: removed this ...
#if 0
// the algorithm in MD5Importer::LoadMD5MeshFile() doesn't work if
// a weight is referenced by more than one vertex. This shouldn't
// occur in MD5 files, but we must take care that we generate new
// weights now, too.
vertNew.mFirstWeight = (unsigned int)meshSrc.mWeights.size();
meshSrc.mWeights.resize(vertNew.mFirstWeight+vertNew.mNumWeights);
for (unsigned int q = 0; q < vertNew.mNumWeights;++q)
{
meshSrc.mWeights[vertNew.mFirstWeight+q] = meshSrc.mWeights[vertOld.mFirstWeight+q];
}
#endif
}
else abHad[face.mIndices[i]] = true;
}
}
}
// ------------------------------------------------------------------------------------------------
void AttachChilds(int iParentID,aiNode* piParent,BoneList& bones)
{
ai_assert(NULL != piParent && !piParent->mNumChildren);
for (unsigned int i = 0; i < bones.size();++i)
{
// (avoid infinite recursion)
if (iParentID != i && bones[i].mParentIndex == iParentID)
{
// have it ...
++piParent->mNumChildren;
}
}
if (piParent->mNumChildren)
{
piParent->mChildren = new aiNode*[piParent->mNumChildren];
for (unsigned int i = 0; i < bones.size();++i)
{
// (avoid infinite recursion)
if (iParentID != i && bones[i].mParentIndex == iParentID)
{
aiNode* pc;
*piParent->mChildren++ = pc = new aiNode();
pc->mName = aiString(bones[i].mName);
pc->mParent = piParent;
// get the transformation matrix from rotation and translational components
aiQuaternion quat = aiQuaternion ( bones[i].mRotationQuat );
//quat.w *= -1.0f; // DX to OGL
pc->mTransformation = aiMatrix4x4 ( quat.GetMatrix());
aiMatrix4x4 mTranslate;
mTranslate.a4 = bones[i].mPositionXYZ.x;
mTranslate.b4 = bones[i].mPositionXYZ.y;
mTranslate.c4 = bones[i].mPositionXYZ.z;
pc->mTransformation = pc->mTransformation*mTranslate;
// store it for later use
bones[i].mTransform = bones[i].mInvTransform = pc->mTransformation;
bones[i].mInvTransform.Inverse();
// the transformations for each bone are absolute,
// so we need to multiply them with the inverse
// of the absolut matrix of the parent
if (-1 != iParentID)
{
pc->mTransformation = bones[iParentID].mInvTransform*pc->mTransformation;
}
// add children to this node, too
AttachChilds( i, pc, bones);
}
}
// undo our nice shift
piParent->mChildren -= piParent->mNumChildren;
}
}
// ------------------------------------------------------------------------------------------------
void MD5Importer::LoadMD5MeshFile ()
{
std::string pFile = this->mFile + "MD5MESH";
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file
if( file.get() == NULL)
{
DefaultLogger::get()->warn("Failed to read MD5 mesh file: " + pFile);
return;
}
bHadMD5Mesh = true;
// now load the file into memory
this->LoadFileIntoMemory(file.get());
// now construct a parser and parse the file
MD5::MD5Parser parser(mBuffer,fileSize);
// load the mesh information from it
MD5::MD5MeshParser meshParser(parser.mSections);
// create the bone hierarchy - first the root node
// and dummy nodes for all meshes
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumChildren = 2;
pScene->mRootNode->mChildren = new aiNode*[2];
aiNode* pcNode = pScene->mRootNode->mChildren[0] = new aiNode();
pcNode->mNumMeshes = (unsigned int)meshParser.mMeshes.size();
pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes];
pcNode->mName.Set("MD5Mesh");
pcNode->mParent = pScene->mRootNode;
for (unsigned int i = 0; i < pcNode->mNumMeshes;++i)
{
pcNode->mMeshes[i] = i;
}
// now create the hierarchy of animated bones
pcNode = pScene->mRootNode->mChildren[1] = new aiNode();
pcNode->mName.Set("MD5Anim");
pcNode->mParent = pScene->mRootNode;
AttachChilds(-1,pcNode,meshParser.mJoints);
// generate all meshes
pScene->mNumMeshes = pScene->mNumMaterials = (unsigned int)meshParser.mMeshes.size();
pScene->mMeshes = new aiMesh*[pScene->mNumMeshes];
pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes];
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
{
aiMesh* mesh = pScene->mMeshes[i] = new aiMesh();
MD5::MeshDesc& meshSrc = meshParser.mMeshes[i];
// generate unique vertices in our internal verbose format
MakeDataUnique(meshSrc);
mesh->mNumVertices = (unsigned int) meshSrc.mVertices.size();
mesh->mVertices = new aiVector3D[mesh->mNumVertices];
mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices];
mesh->mNumUVComponents[0] = 2;
// copy texture coordinates
aiVector3D* pv = mesh->mTextureCoords[0];
for (MD5::VertexList::const_iterator
iter = meshSrc.mVertices.begin();
iter != meshSrc.mVertices.end();++iter,++pv)
{
pv->x = (*iter).mUV.x;
pv->y = 1.0f-(*iter).mUV.y; // D3D to OpenGL
pv->z = 0.0f;
}
// sort all bone weights - per bone
unsigned int* piCount = new unsigned int[meshParser.mJoints.size()];
::memset(piCount,0,sizeof(unsigned int)*meshParser.mJoints.size());
for (MD5::VertexList::const_iterator
iter = meshSrc.mVertices.begin();
iter != meshSrc.mVertices.end();++iter,++pv)
{
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
{
MD5::WeightDesc& desc = meshSrc.mWeights[w];
++piCount[desc.mBone];
}
}
// check how many we will need
for (unsigned int p = 0; p < meshParser.mJoints.size();++p)
if (piCount[p])mesh->mNumBones++;
if (mesh->mNumBones) // just for safety
{
mesh->mBones = new aiBone*[mesh->mNumBones];
for (unsigned int q = 0,h = 0; q < meshParser.mJoints.size();++q)
{
if (!piCount[q])continue;
aiBone* p = mesh->mBones[h] = new aiBone();
p->mNumWeights = piCount[q];
p->mWeights = new aiVertexWeight[p->mNumWeights];
p->mName = aiString(meshParser.mJoints[q].mName);
// store the index for later use
meshParser.mJoints[q].mMap = h++;
}
unsigned int g = 0;
pv = mesh->mVertices;
for (MD5::VertexList::const_iterator
iter = meshSrc.mVertices.begin();
iter != meshSrc.mVertices.end();++iter,++pv)
{
// compute the final vertex position from all single weights
*pv = aiVector3D();
// there are models which have weights which don't sum to 1 ...
// granite.md5mesh for example
float fSum = 0.0f;
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
fSum += meshSrc.mWeights[w].mWeight;
if (!fSum)throw new ImportErrorException("The sum of all vertex bone weights is 0");
// process bone weights
for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
{
MD5::WeightDesc& desc = meshSrc.mWeights[w];
float fNewWeight = desc.mWeight / fSum;
// transform the local position into worldspace
MD5::BoneDesc& boneSrc = meshParser.mJoints[desc.mBone];
aiVector3D v = desc.vOffsetPosition;
aiQuaternion quat = aiQuaternion( boneSrc.mRotationQuat );
//quat.w *= -1.0f;
v = quat.GetMatrix() * v;
v += boneSrc.mPositionXYZ;
// use the original weight to compute the vertex position
// (some MD5s seem to depend on the invalid weight values ...)
pv->operator +=(v * desc.mWeight);
aiBone* bone = mesh->mBones[boneSrc.mMap];
*bone->mWeights++ = aiVertexWeight((unsigned int)(pv-mesh->mVertices),fNewWeight);
}
// convert from DOOM coordinate system to OGL
std::swap(pv->z,pv->y);
}
// undo our nice offset tricks ...
for (unsigned int p = 0; p < mesh->mNumBones;++p)
mesh->mBones[p]->mWeights -= mesh->mBones[p]->mNumWeights;
}
delete[] piCount;
// now setup all faces - we can directly copy the list
// (however, take care that the aiFace destructor doesn't delete the mIndices array)
mesh->mNumFaces = (unsigned int)meshSrc.mFaces.size();
mesh->mFaces = new aiFace[mesh->mNumFaces];
for (unsigned int c = 0; c < mesh->mNumFaces;++c)
{
mesh->mFaces[c].mNumIndices = 3;
mesh->mFaces[c].mIndices = meshSrc.mFaces[c].mIndices;
meshSrc.mFaces[c].mIndices = NULL;
}
// generate a material for the mesh
MaterialHelper* mat = new MaterialHelper();
pScene->mMaterials[i] = mat;
mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0));
mesh->mMaterialIndex = i;
}
// delete the file again
this->UnloadFileFromMemory();
}
// ------------------------------------------------------------------------------------------------
void MD5Importer::LoadMD5AnimFile ()
{
std::string pFile = this->mFile + "MD5ANIM";
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file
if( file.get() == NULL)
{
DefaultLogger::get()->warn("Failed to read MD5 anim file: " + pFile);
return;
}
bHadMD5Anim = true;
// now load the file into memory
this->LoadFileIntoMemory(file.get());
// now construct a parser and parse the file
MD5::MD5Parser parser(mBuffer,fileSize);
// load the animation information from it
MD5::MD5AnimParser animParser(parser.mSections);
// generate and fill the output animation
if (!animParser.mAnimatedBones.empty())
{
pScene->mNumAnimations = 1;
pScene->mAnimations = new aiAnimation*[1];
aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
anim->mNumBones = (unsigned int)animParser.mAnimatedBones.size();
anim->mBones = new aiBoneAnim*[anim->mNumBones];
for (unsigned int i = 0; i < anim->mNumBones;++i)
{
aiBoneAnim* bone = anim->mBones[i] = new aiBoneAnim();
bone->mBoneName = aiString( animParser.mAnimatedBones[i].mName );
// allocate storage for the keyframes
bone->mNumPositionKeys = bone->mNumRotationKeys = (unsigned int)animParser.mFrames.size();
bone->mPositionKeys = new aiVectorKey[bone->mNumPositionKeys];
bone->mRotationKeys = new aiQuatKey[bone->mNumPositionKeys];
}
// 1 tick == 1 frame
anim->mTicksPerSecond = animParser.fFrameRate;
for (FrameList::const_iterator iter = animParser.mFrames.begin(),
iterEnd = animParser.mFrames.end();iter != iterEnd;++iter)
{
double dTime = (double)(*iter).iIndex;
if (!(*iter).mValues.empty())
{
// now process all values in there ... read all joints
aiBoneAnim** pcAnimBone = anim->mBones;
MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
for (AnimBoneList::const_iterator
iter2 = animParser.mAnimatedBones.begin(),
iterEnd2 = animParser.mAnimatedBones.end();
iter2 != iterEnd2;++iter2,++pcAnimBone,++pcBaseFrame)
{
if((*iter2).iFirstKeyIndex >= (*iter).mValues.size())
{
// TODO: add proper array checks for all cases here ...
DefaultLogger::get()->error("Keyframe index is out of range. ");
continue;
}
const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex];
aiBoneAnim* pcCurAnimBone = *pcAnimBone;
aiVectorKey* vKey = pcCurAnimBone->mPositionKeys++;
// translation on the x axis
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_TRANSLATE_X)
vKey->mValue.x = *fpCur++;
else vKey->mValue.x = pcBaseFrame->vPositionXYZ.x;
// translation on the y axis
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_TRANSLATE_Y)
vKey->mValue.y = *fpCur++;
else vKey->mValue.y = pcBaseFrame->vPositionXYZ.y;
// translation on the z axis
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_TRANSLATE_Z)
vKey->mValue.z = *fpCur++;
else vKey->mValue.z = pcBaseFrame->vPositionXYZ.z;
// rotation quaternion, x component
aiQuatKey* qKey = pcCurAnimBone->mRotationKeys++;
aiVector3D vTemp;
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_ROTQUAT_X)
vTemp.x = *fpCur++;
else vTemp.x = pcBaseFrame->vRotationQuat.x;
// rotation quaternion, y component
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_ROTQUAT_Y)
vTemp.y = *fpCur++;
else vTemp.y = pcBaseFrame->vRotationQuat.y;
// rotation quaternion, z component
if ((*iter2).iFlags & AI_MD5_ANIMATION_FLAG_ROTQUAT_Z)
vTemp.z = *fpCur++;
else vTemp.z = pcBaseFrame->vRotationQuat.z;
// compute the w component of the quaternion - invert it (DX to OGL)
qKey->mValue = aiQuaternion(vTemp);
//qKey->mValue.w *= -1.0f;
qKey->mTime = dTime;
vKey->mTime = dTime;
}
}
// compute the duration of the animation
anim->mDuration = std::max(dTime,anim->mDuration);
}
// undo our offset computations
for (unsigned int i = 0; i < anim->mNumBones;++i)
{
aiBoneAnim* bone = anim->mBones[i];
bone->mPositionKeys -= bone->mNumPositionKeys;
bone->mRotationKeys -= bone->mNumPositionKeys;
}
}
// delete the file again
this->UnloadFileFromMemory();
}

View File

@ -39,21 +39,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file Definition of the .MD5 importer class. */
/** @file Definition of the .MD5 importer class.
http://www.modwiki.net/wiki/MD5_(file_format)
*/
#ifndef AI_MD5LOADER_H_INCLUDED
#define AI_MD5LOADER_H_INCLUDED
#include "BaseImporter.h"
#include "MD5Parser.h"
#include "../include/aiTypes.h"
struct aiNode;
#include "MD5FileData.h"
namespace Assimp {
namespace Assimp
{
class MaterialHelper;
using namespace MD5;
class IOStream;
using namespace Assimp::MD5;
// ---------------------------------------------------------------------------
/** Used to load MD5 files
@ -96,11 +96,57 @@ protected:
protected:
/** Header of the MD5 file */
const MD5::Header* m_pcHeader;
// -------------------------------------------------------------------
/** Load the *.MD5MESH file.
* Must be called at first.
*/
void LoadMD5MeshFile ();
// -------------------------------------------------------------------
/** Load the *.MD5ANIM file.
*/
void LoadMD5AnimFile ();
// -------------------------------------------------------------------
/** Load the contents of a specific file into memory and
* alocates a buffer to keep it.
*
* mBuffer is changed to point to this buffer.
* Don't forget to delete it later ...
* @param pFile File stream to be read
*/
void LoadFileIntoMemory (IOStream* pFile);
void UnloadFileFromMemory ();
/** IOSystem to be used to access files */
IOSystem* mIOHandler;
/** Path to the file, excluding the file extension but
with the dot */
std::string mFile;
/** Buffer to hold the loaded file */
const unsigned char* mBuffer;
char* mBuffer;
/** Size of the file */
unsigned int fileSize;
/** Current line number. For debugging purposes */
unsigned int iLineNumber;
/** Scene to be filled */
aiScene* pScene;
/** (Custom) I/O handler implementation */
IOSystem* pIOHandler;
/** true if the MD5MESH file has already been parsed */
bool bHadMD5Mesh;
/** true if the MD5ANIM file has already been parsed */
bool bHadMD5Anim;
};
} // end of namespace Assimp

545
code/MD5Parser.cpp 100644
View File

@ -0,0 +1,545 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the MD5 parser class */
// internal headers
#include "MD5Loader.h"
#include "MaterialSystem.h"
#include "fast_atof.h"
#include "ParsingUtils.h"
#include "StringComparison.h"
// public ASSIMP headers
#include "../include/DefaultLogger.h"
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
using namespace Assimp;
using namespace Assimp::MD5;
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
// ------------------------------------------------------------------------------------------------
MD5Parser::MD5Parser(char* buffer, unsigned int fileSize)
{
ai_assert(NULL != buffer && 0 != fileSize);
this->buffer = buffer;
this->fileSize = fileSize;
this->lineNumber = 0;
DefaultLogger::get()->debug("MD5Parser begin");
// parse the file header
this->ParseHeader();
// and read all sections until we're finished
while (true)
{
this->mSections.push_back(Section());
Section& sec = this->mSections.back();
if(!this->ParseSection(sec))
{
break;
}
}
if ( !DefaultLogger::isNullLogger())
{
char szBuffer[128]; // should be sufficiently large
::sprintf(szBuffer,"MD5Parser end. Parsed %i sections",this->mSections.size());
DefaultLogger::get()->debug(szBuffer);
}
}
// ------------------------------------------------------------------------------------------------
/*static*/ void MD5Parser::ReportError (char* error, unsigned int line)
{
char szBuffer[1024]; // you, listen to me, you HAVE TO BE sufficiently large
::sprintf(szBuffer,"Line %i: %s",line,error);
throw new ImportErrorException(szBuffer);
}
// ------------------------------------------------------------------------------------------------
/*static*/ void MD5Parser::ReportWarning (char* warn, unsigned int line)
{
char szBuffer[1024]; // you, listen to me, you HAVE TO BE sufficiently large
::sprintf(szBuffer,"Line %i: %s",line,warn);
DefaultLogger::get()->warn(szBuffer);
}
// ------------------------------------------------------------------------------------------------
void MD5Parser::ParseHeader()
{
// parse and validate the file version
SkipSpaces();
if (0 != ASSIMP_strincmp(buffer,"MD5Version",10) ||
!IsSpace(*(buffer+=10)))
{
this->ReportError("Invalid MD5 file: MD5Version tag has not been found");
}
SkipSpaces();
unsigned int iVer = ::strtol10(buffer,(const char**)&buffer);
if (10 != iVer)
{
this->ReportWarning("MD5 version tag is unknown (10 is expected)");
}
this->SkipLine();
// print the command line options to the console
char* sz = buffer;
while (!IsLineEnd( *buffer++));
DefaultLogger::get()->info(std::string(sz,(uintptr_t)(buffer-sz)));
this->SkipSpacesAndLineEnd();
}
// ------------------------------------------------------------------------------------------------
bool MD5Parser::ParseSection(Section& out)
{
// store the current line number for use in error messages
out.iLineNumber = this->lineNumber;
// first parse the name of the section
char* sz = buffer;
while (!IsSpaceOrNewLine( *buffer))buffer++;
out.mName = std::string(sz,(uintptr_t)(buffer-sz));
SkipSpaces();
while (true)
{
if ('{' == *buffer)
{
// it is a normal section so read all lines
buffer++;
while (true)
{
if (!SkipSpacesAndLineEnd())
{
return false; // seems this was the last section
}
if ('}' == *buffer)
{
buffer++;
break;
}
out.mElements.push_back(Element());
Element& elem = out.mElements.back();
elem.iLineNumber = lineNumber;
elem.szStart = buffer;
// terminate the line with zero - remove all spaces at the end
while (!IsLineEnd( *buffer))buffer++;
//const char* end = buffer;
do {buffer--;}
while (IsSpace(*buffer));
buffer++;
*buffer++ = '\0';
//if (*end) ++lineNumber;
}
break;
}
else if (!IsSpaceOrNewLine(*buffer))
{
// it is an element at global scope. Parse its value and go on
// FIX: for MD5ANIm files - frame 0 {...} is allowed
sz = buffer;
while (!IsSpaceOrNewLine( *buffer++));
out.mGlobalValue = std::string(sz,(uintptr_t)(buffer-sz));
continue;
}
break;
}
return SkipSpacesAndLineEnd();
}
// ------------------------------------------------------------------------------------------------
// skip all spaces ... handle EOL correctly
#define AI_MD5_SKIP_SPACES() if(!SkipSpaces(&sz)) \
MD5Parser::ReportWarning("Unexpected end of line",(*eit).iLineNumber);
// read a triple float in brackets: (1.0 1.0 1.0)
#define AI_MD5_READ_TRIPLE(vec) \
AI_MD5_SKIP_SPACES(); \
if ('(' != *sz++) \
MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber); \
AI_MD5_SKIP_SPACES(); \
sz = fast_atof_move(sz,vec.x); \
AI_MD5_SKIP_SPACES(); \
sz = fast_atof_move(sz,vec.y); \
AI_MD5_SKIP_SPACES(); \
sz = fast_atof_move(sz,vec.z); \
AI_MD5_SKIP_SPACES(); \
if (')' != *sz++) \
MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber);
// parse a string, enclosed in quotation marks or not
#define AI_MD5_PARSE_STRING(out) \
bool bQuota = *sz == '\"'; \
const char* szStart = sz; \
while (!IsSpaceOrNewLine(*sz))++sz; \
const char* szEnd = sz; \
if (bQuota) \
{ \
szStart++; \
if ('\"' != *(szEnd-=1)) \
{ \
MD5Parser::ReportWarning("Expected closing quotation marks in string", \
(*eit).iLineNumber); \
} \
} \
out.length = (size_t)(szEnd - szStart); \
::memcpy(out.data,szStart,out.length); \
out.data[out.length] = '\0';
// ------------------------------------------------------------------------------------------------
MD5MeshParser::MD5MeshParser(SectionList& mSections)
{
DefaultLogger::get()->debug("MD5MeshParser begin");
// now parse all sections
for (SectionList::const_iterator
iter = mSections.begin(), iterEnd = mSections.end();
iter != iterEnd;++iter)
{
if ((*iter).mGlobalValue.length())
{
if ( !::strcmp("numMeshes",(*iter).mName.c_str()))
{
unsigned int iNumMeshes;
if(iNumMeshes = ::strtol10((*iter).mGlobalValue.c_str()))
{
mMeshes.reserve(iNumMeshes);
}
}
else if ( !::strcmp("numJoints",(*iter).mName.c_str()))
{
unsigned int iNumJoints;
if(iNumJoints = ::strtol10((*iter).mGlobalValue.c_str()))
{
mJoints.reserve(iNumJoints);
}
}
}
else if (!::strcmp("joints",(*iter).mName.c_str()))
{
// now read all elements
// "origin" -1 ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000000 0.707107 )
for (ElementList::const_iterator
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
eit != eitEnd; ++eit)
{
mJoints.push_back(BoneDesc());
BoneDesc& desc = mJoints.back();
const char* sz = (*eit).szStart;
AI_MD5_PARSE_STRING(desc.mName);
AI_MD5_SKIP_SPACES();
// negative values can occur here ...
bool bNeg = false;
if ('-' == *sz){sz++;bNeg = true;}
else if ('+' == *sz){sz++;}
desc.mParentIndex = (int)::strtol10(sz,&sz);
if (bNeg)desc.mParentIndex *= -1;
AI_MD5_READ_TRIPLE(desc.mPositionXYZ);
AI_MD5_READ_TRIPLE(desc.mRotationQuat); // normalized quaternion, so w is not there
}
}
else if (!::strcmp("mesh",(*iter).mName.c_str()))
{
mMeshes.push_back(MeshDesc());
MeshDesc& desc = mMeshes.back();
// now read all elements
for (ElementList::const_iterator
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
eit != eitEnd; ++eit)
{
const char* sz = (*eit).szStart;
// shader attribute
if (!ASSIMP_strincmp(sz,"shader",6) &&
IsSpaceOrNewLine(*(sz+=6)++))
{
// don't expect quotation marks
AI_MD5_SKIP_SPACES();
AI_MD5_PARSE_STRING(desc.mShader);
}
// numverts attribute
else if (!ASSIMP_strincmp(sz,"numverts",8) &&
IsSpaceOrNewLine(*(sz+=8)++))
{
// reserve enough storage
AI_MD5_SKIP_SPACES();
unsigned int iNumVertices;
if(iNumVertices = ::strtol10(sz))
desc.mVertices.resize(iNumVertices);
}
// numtris attribute
else if (!ASSIMP_strincmp(sz,"numtris",7) &&
IsSpaceOrNewLine(*(sz+=7)++))
{
// reserve enough storage
AI_MD5_SKIP_SPACES();
unsigned int iNumTris;
if(iNumTris = ::strtol10(sz))
desc.mFaces.resize(iNumTris);
}
// numweights attribute
else if (!ASSIMP_strincmp(sz,"numweights",10) &&
IsSpaceOrNewLine(*(sz+=10)++))
{
// reserve enough storage
AI_MD5_SKIP_SPACES();
unsigned int iNumWeights;
if(iNumWeights = ::strtol10(sz))
desc.mWeights.resize(iNumWeights);
}
// vert attribute
// "vert 0 ( 0.394531 0.513672 ) 0 1"
else if (!ASSIMP_strincmp(sz,"vert",4) &&
IsSpaceOrNewLine(*(sz+=4)++))
{
AI_MD5_SKIP_SPACES();
unsigned int idx = ::strtol10(sz,&sz);
AI_MD5_SKIP_SPACES();
if (idx >= desc.mVertices.size())
desc.mVertices.resize(idx+1);
VertexDesc& vert = desc.mVertices[idx];
if ('(' != *sz++)
MD5Parser::ReportWarning("Unexpected token: ( was expected",(*eit).iLineNumber);
AI_MD5_SKIP_SPACES();
sz = fast_atof_move(sz,vert.mUV.x);
AI_MD5_SKIP_SPACES();
sz = fast_atof_move(sz,vert.mUV.y);
AI_MD5_SKIP_SPACES();
if (')' != *sz++)
MD5Parser::ReportWarning("Unexpected token: ) was expected",(*eit).iLineNumber);
AI_MD5_SKIP_SPACES();
vert.mFirstWeight = ::strtol10(sz,&sz);
AI_MD5_SKIP_SPACES();
vert.mNumWeights = ::strtol10(sz,&sz);
}
// tri attribute
// "tri 0 15 13 12"
else if (!ASSIMP_strincmp(sz,"tri",3) &&
IsSpaceOrNewLine(*(sz+=3)++))
{
AI_MD5_SKIP_SPACES();
unsigned int idx = ::strtol10(sz,&sz);
if (idx >= desc.mFaces.size())
desc.mFaces.resize(idx+1);
aiFace& face = desc.mFaces[idx];
face.mIndices = new unsigned int[face.mNumIndices = 3];
for (unsigned int i = 0; i < 3;++i)
{
AI_MD5_SKIP_SPACES();
face.mIndices[i] = ::strtol10(sz,&sz);
}
}
// weight attribute
// "weight 362 5 0.500000 ( -3.553583 11.893474 9.719339 )"
else if (!ASSIMP_strincmp(sz,"weight",6) &&
IsSpaceOrNewLine(*(sz+=6)++))
{
AI_MD5_SKIP_SPACES();
unsigned int idx = ::strtol10(sz,&sz);
AI_MD5_SKIP_SPACES();
if (idx >= desc.mWeights.size())
desc.mWeights.resize(idx+1);
WeightDesc& weight = desc.mWeights[idx];
weight.mBone = ::strtol10(sz,&sz);
AI_MD5_SKIP_SPACES();
sz = fast_atof_move(sz,weight.mWeight);
AI_MD5_READ_TRIPLE(weight.vOffsetPosition);
}
}
}
}
DefaultLogger::get()->debug("MD5MeshParser end");
}
// ------------------------------------------------------------------------------------------------
MD5AnimParser::MD5AnimParser(SectionList& mSections)
{
DefaultLogger::get()->debug("MD5AnimParser begin");
fFrameRate = 24.0f;
mNumAnimatedComponents = 0xffffffff;
// now parse all sections
for (SectionList::const_iterator
iter = mSections.begin(), iterEnd = mSections.end();
iter != iterEnd;++iter)
{
if (!::strcmp("hierarchy",(*iter).mName.c_str()))
{
// now read all elements
// "sheath" 0 63 6
for (ElementList::const_iterator
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
eit != eitEnd; ++eit)
{
mAnimatedBones.push_back ( AnimBoneDesc () );
AnimBoneDesc& desc = mAnimatedBones.back();
const char* sz = (*eit).szStart;
AI_MD5_PARSE_STRING(desc.mName);
AI_MD5_SKIP_SPACES();
// parent index
// negative values can occur here ...
bool bNeg = false;
if ('-' == *sz){sz++;bNeg = true;}
else if ('+' == *sz){sz++;}
desc.mParentIndex = (int)::strtol10(sz,&sz);
if (bNeg)desc.mParentIndex *= -1;
// flags (highest is 2^6-1)
AI_MD5_SKIP_SPACES();
if(63 < (desc.iFlags = ::strtol10(sz,&sz)))
{
MD5Parser::ReportWarning("Invalid flag combination in hierarchy section",
(*eit).iLineNumber);
}
AI_MD5_SKIP_SPACES();
// index of the first animation keyframe component for this joint
desc.iFirstKeyIndex = ::strtol10(sz,&sz);
}
}
else if(!::strcmp("baseframe",(*iter).mName.c_str()))
{
// now read all elements
// ( -0.000000 0.016430 -0.006044 ) ( 0.707107 0.000242 0.707107 )
for (ElementList::const_iterator
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
eit != eitEnd; ++eit)
{
const char* sz = (*eit).szStart;
mBaseFrames.push_back ( BaseFrameDesc () );
BaseFrameDesc& desc = mBaseFrames.back();
AI_MD5_READ_TRIPLE(desc.vPositionXYZ);
AI_MD5_READ_TRIPLE(desc.vRotationQuat);
}
}
else if(!::strcmp("frame",(*iter).mName.c_str()))
{
if (!(*iter).mGlobalValue.length())
{
MD5Parser::ReportWarning("A frame section must have a frame index",
(*iter).iLineNumber);
continue;
}
mFrames.push_back ( FrameDesc () );
FrameDesc& desc = mFrames.back();
desc.iIndex = ::strtol10((*iter).mGlobalValue.c_str());
// we do already know how much storage we will presumably need
if (0xffffffff != mNumAnimatedComponents)
desc.mValues.reserve(mNumAnimatedComponents);
// now read all elements
// (continous list of float values)
for (ElementList::const_iterator
eit = (*iter).mElements.begin(), eitEnd = (*iter).mElements.end();
eit != eitEnd; ++eit)
{
const char* sz = (*eit).szStart;
while (SkipSpaces(sz,&sz))
{
float f;
sz = fast_atof_move(sz,f);
desc.mValues.push_back(f);
}
}
}
else if(!::strcmp("numFrames",(*iter).mName.c_str()))
{
unsigned int iNum;
if(iNum = ::strtol10((*iter).mGlobalValue.c_str()))
{
mFrames.reserve(iNum);
}
}
else if(!::strcmp("numJoints",(*iter).mName.c_str()))
{
unsigned int iNum;
if(iNum = ::strtol10((*iter).mGlobalValue.c_str()))
{
mAnimatedBones.reserve(iNum);
// try to guess the number of animated components if that element is not given
if (0xffffffff == mNumAnimatedComponents)
mNumAnimatedComponents = iNum * 6;
}
}
else if(!::strcmp("numAnimatedComponents",(*iter).mName.c_str()))
{
unsigned int iNum;
if(iNum = ::strtol10((*iter).mGlobalValue.c_str()))
{
mAnimatedBones.reserve(iNum);
}
}
else if(!::strcmp("frameRate",(*iter).mName.c_str()))
{
fast_atof_move((*iter).mGlobalValue.c_str(),this->fFrameRate);
}
}
DefaultLogger::get()->debug("MD5AnimParser end");
}
#undef AI_MD5_SKIP_SPACES
#undef AI_MD5_READ_TRIPLE
#undef AI_MD5_PARSE_STRING

419
code/MD5Parser.h 100644
View File

@ -0,0 +1,419 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Definition of the .MD5 parser class.
http://www.modwiki.net/wiki/MD5_(file_format)
*/
#ifndef AI_MD5PARSER_H_INCLUDED
#define AI_MD5PARSER_H_INCLUDED
#include "../include/aiTypes.h"
#include "ParsingUtils.h"
#include <vector>
struct aiFace;
namespace Assimp {
namespace MD5 {
// ---------------------------------------------------------------------------
/** Represents a single element in a MD5 file
*
* Elements are always contained in sections.
*/
struct Element
{
//! Points to the starting point of the element
//! Whitespace at the beginning and at the end have been removed,
//! Elements are terminated with \0
char* szStart;
//! Original line number (can be used in error messages
//! if a parsing error occurs)
unsigned int iLineNumber;
};
typedef std::vector< Element > ElementList;
// ---------------------------------------------------------------------------
/** Represents a section of a MD5 file (such as the mesh or the joints section)
*
* A section is always enclosed in { and } brackets.
*/
struct Section
{
//! Original line number (can be used in error messages
//! if a parsing error occurs)
unsigned int iLineNumber;
//! List of all elements which have been parsed in this section.
ElementList mElements;
//! Name of the section
std::string mName;
//! For global elements: the value of the element as string
//! Iif !length() the section is not a global element
std::string mGlobalValue;
};
typedef std::vector< Section> SectionList;
// ---------------------------------------------------------------------------
/** Represents a bone (joint) descriptor in a MD5Mesh file
*/
struct BoneDesc
{
//! Name of the bone
aiString mName;
//! Parent index of the bone
int mParentIndex;
//! Relative position of the bone
aiVector3D mPositionXYZ;
//! Relative rotation of the bone
aiVector3D mRotationQuat;
//! Absolute transformation of the bone
//! (temporary)
aiMatrix4x4 mTransform;
//! Inverse transformation of the bone
//! (temporary)
aiMatrix4x4 mInvTransform;
//! Internal
unsigned int mMap;
};
typedef std::vector< BoneDesc > BoneList;
// ---------------------------------------------------------------------------
/** Represents a bone (joint) descriptor in a MD5Anim file
*/
struct AnimBoneDesc
{
//! Name of the bone
aiString mName;
//! Parent index of the bone
int mParentIndex;
//! Flags (AI_MD5_ANIMATION_FLAG_xxx)
unsigned int iFlags;
//! Index of the first key that corresponds to this anim bone
unsigned int iFirstKeyIndex;
};
typedef std::vector< AnimBoneDesc > AnimBoneList;
// ---------------------------------------------------------------------------
/** Represents a base frame descriptor in a MD5Anim file
*/
struct BaseFrameDesc
{
aiVector3D vPositionXYZ;
aiVector3D vRotationQuat;
};
typedef std::vector< BaseFrameDesc > BaseFrameList;
// ---------------------------------------------------------------------------
/** Represents a frame descriptor in a MD5Anim file
*/
struct FrameDesc
{
//! Index of the frame
unsigned int iIndex;
//! Animation keyframes - a large blob of data at first
std::vector< float > mValues;
};
typedef std::vector< FrameDesc > FrameList;
// ---------------------------------------------------------------------------
/** Represents a vertex descriptor in a MD5 file
*/
struct VertexDesc
{
VertexDesc()
: mFirstWeight (0)
, mNumWeights (0)
{}
//! UV cordinate of the vertex
aiVector2D mUV;
//! Index of the first weight of the vertex in
//! the vertex weight list
unsigned int mFirstWeight;
//! Number of weights assigned to this vertex
unsigned int mNumWeights;
};
typedef std::vector< VertexDesc > VertexList;
// ---------------------------------------------------------------------------
/** Represents a vertex weight descriptor in a MD5 file
*/
struct WeightDesc
{
//! Index of the bone to which this weight refers
unsigned int mBone;
//! The weight value
float mWeight;
//! The offset position of this weight
// ! (in the coordinate system defined by the parent bone)
aiVector3D vOffsetPosition;
};
typedef std::vector< WeightDesc > WeightList;
typedef std::vector< aiFace > FaceList;
// ---------------------------------------------------------------------------
/** Represents a mesh in a MD5 file
*/
struct MeshDesc
{
//! Weights of the mesh
WeightList mWeights;
//! Vertices of the mesh
VertexList mVertices;
//! Faces of the mesh
FaceList mFaces;
//! Name of the shader (=texture) to be assigned to the mesh
aiString mShader;
};
typedef std::vector< MeshDesc > MeshList;
// ---------------------------------------------------------------------------
/** Parses the data sections of a MD5 mesh file
*/
class MD5MeshParser
{
public:
// -------------------------------------------------------------------
/** Constructs a new MD5MeshParser instance from an existing
* preparsed list of file sections.
*
* @param mSections List of file sections (output of MD5Parser)
*/
MD5MeshParser(SectionList& mSections);
//! List of all meshes
MeshList mMeshes;
//! List of all joints
BoneList mJoints;
};
#define AI_MD5_ANIMATION_FLAG_TRANSLATE_X 0x1
#define AI_MD5_ANIMATION_FLAG_TRANSLATE_Y 0x2
#define AI_MD5_ANIMATION_FLAG_TRANSLATE_Z 0x4
#define AI_MD5_ANIMATION_FLAG_ROTQUAT_X 0x8
#define AI_MD5_ANIMATION_FLAG_ROTQUAT_Y 0x10
#define AI_MD5_ANIMATION_FLAG_ROTQUAT_Z 0x12
// remove this flag if you need to the bounding box data
#define AI_MD5_PARSE_NO_BOUNDS
// ---------------------------------------------------------------------------
/** Parses the data sections of a MD5 animation file
*/
class MD5AnimParser
{
public:
// -------------------------------------------------------------------
/** Constructs a new MD5AnimParser instance from an existing
* preparsed list of file sections.
*
* @param mSections List of file sections (output of MD5Parser)
*/
MD5AnimParser(SectionList& mSections);
//! Output frame rate
float fFrameRate;
//! List of animation bones
AnimBoneList mAnimatedBones;
//! List of base frames
BaseFrameList mBaseFrames;
//! List of animation frames
FrameList mFrames;
//! Number of animated components
unsigned int mNumAnimatedComponents;
};
// ---------------------------------------------------------------------------
/** Parses the block structure of MD5MESH and MD5ANIM files (but does no
* further processing)
*/
class MD5Parser
{
public:
// -------------------------------------------------------------------
/** Constructs a new MD5Parser instance from an existing buffer.
*
* @param buffer File buffer
* @param fileSize Length of the file in bytes (excluding a terminal 0)
*/
MD5Parser(char* buffer, unsigned int fileSize);
// -------------------------------------------------------------------
/** Report a specific error message and throw an exception
* @param error Error message to be reported
* @param line Index of the line where the error occured
*/
static void ReportError (char* error, unsigned int line);
// -------------------------------------------------------------------
/** Report a specific warning
* @param warn Warn message to be reported
* @param line Index of the line where the error occured
*/
static void ReportWarning (char* warn, unsigned int line);
inline void ReportError (char* error)
{return ReportError(error, this->lineNumber);}
inline void ReportWarning (char* warn)
{return ReportWarning(warn, this->lineNumber);}
public:
//! List of all sections which have been read
SectionList mSections;
private:
// -------------------------------------------------------------------
/** Parses a file section. The current file pointer must be outside
* of a section.
* @param out Receives the section data
* @return true if the end of the file has been reached
* @throws ImportErrorException if an error occurs
*/
bool ParseSection(Section& out);
// -------------------------------------------------------------------
/** Parses the file header
* @throws ImportErrorException if an error occurs
*/
void ParseHeader();
// override these functions to make sure the line counter gets incremented
// -------------------------------------------------------------------
inline bool SkipLine( const char* in, const char** out)
{
++lineNumber;
return ::SkipLine(in,out);
}
// -------------------------------------------------------------------
inline bool SkipLine( )
{
return SkipLine(buffer,(const char**)&buffer);
}
// -------------------------------------------------------------------
inline bool SkipSpacesAndLineEnd( const char* in, const char** out)
{
bool bHad = false;
while (true)
{
if( *in == '\r' || *in == '\n')
{
if (!bHad) // we open files in binary mode, so there could be \r\n sequences ...
{
bHad = true;
++lineNumber;
}
}
else if (*in == '\t' || *in == ' ')bHad = false;
else break;
in++;
}
*out = in;
return *in != '\0';
}
// -------------------------------------------------------------------
inline bool SkipSpacesAndLineEnd( )
{
return SkipSpacesAndLineEnd(buffer,(const char**)&buffer);
}
// -------------------------------------------------------------------
inline bool SkipSpaces( )
{
return ::SkipSpaces((const char**)&buffer);
}
char* buffer;
unsigned int fileSize;
unsigned int lineNumber;
};
}}
#endif // AI_MD5PARSER_H_INCLUDED

View File

@ -55,6 +55,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
#include "../include/assimp.hpp"
// boost headers
#include <boost/scoped_ptr.hpp>
@ -127,6 +128,17 @@ void MDLImporter::InternReadFile(
throw new ImportErrorException( "Failed to open MDL file " + pFile + ".");
}
// The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the
// AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option.
#if 0
if(0xffffffff == (this->configFrameID = this->mImporter->GetProperty(
AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff)))
{
this->configFrameID = this->mImporter->GetProperty(
AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0);
}
#endif
// this should work for all other types of MDL files, too ...
// the quake header is one of the smallest, afaik
this->iFileSize = (unsigned int)file->FileSize();
@ -236,17 +248,11 @@ void MDLImporter::InternReadFile(
throw ex;
}
// make sure that the normals are facing outwards
// (mainly this applies to MDL7 (due to the FlipNormals option in MED).
// However there are some invalid models in other format, too)
for (unsigned int i = 0; i < pScene->mNumMeshes;++i)
this->FlipNormals(pScene->mMeshes[i]);
// delete the file buffer and cleanup
delete[] this->mBuffer;
DEBUG_INVALIDATE_PTR(this->mBuffer);
DEBUG_INVALIDATE_PTR(this->pIOHandler);
DEBUG_INVALIDATE_PTR(this->pScene);
AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
AI_DEBUG_INVALIDATE_PTR(this->pIOHandler);
AI_DEBUG_INVALIDATE_PTR(this->pScene);
return;
}
// ------------------------------------------------------------------------------------------------
@ -254,7 +260,7 @@ void MDLImporter::SizeCheck(const void* szPos)
{
if (!szPos || (const unsigned char*)szPos > this->mBuffer + this->iFileSize)
{
throw new ImportErrorException("Invalid file. The file is too small "
throw new ImportErrorException("Invalid MDL file. The file is too small "
"or contains invalid data.");
}
}
@ -278,7 +284,7 @@ void MDLImporter::SizeCheck(const void* szPos, const char* szFile, unsigned int
#else
::sprintf(szBuffer,
#endif
"Invalid file. The file is too small "
"Invalid MDL file. The file is too small "
"or contains invalid data (File: %s Line: %i)",szFilePtr,iLine);
throw new ImportErrorException(szBuffer);
@ -301,32 +307,35 @@ void MDLImporter::ValidateHeader_Quake1(const MDL::Header* pcHeader)
throw new ImportErrorException( "[Quake 1 MDL] There are no triangles in the file");
}
// check whether the maxima are exceeded ...
if (pcHeader->num_verts > AI_MDL_MAX_VERTS)
// check whether the maxima are exceeded ...however, this applies for Quake 1 MDLs only
if (!this->iGSFileVersion)
{
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices");
}
if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES)
{
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles");
}
if (pcHeader->num_frames > AI_MDL_MAX_FRAMES)
{
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames");
}
// (this does not apply for 3DGS MDLs)
if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION)
{
DefaultLogger::get()->warn("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is "
"the expected file format version");
}
if (pcHeader->num_skins)
{
if(!pcHeader->skinwidth || !pcHeader->skinheight)
if (pcHeader->num_verts > AI_MDL_MAX_VERTS)
{
DefaultLogger::get()->warn("Skin width or height are 0. Division through "
"zero would occur ...");
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_VERTS vertices");
}
if (pcHeader->num_tris > AI_MDL_MAX_TRIANGLES)
{
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_TRIANGLES triangles");
}
if (pcHeader->num_frames > AI_MDL_MAX_FRAMES)
{
DefaultLogger::get()->warn("Quake 1 MDL model has more than AI_MDL_MAX_FRAMES frames");
}
// (this does not apply for 3DGS MDLs)
if (!this->iGSFileVersion && pcHeader->version != AI_MDL_VERSION)
{
DefaultLogger::get()->warn("Quake 1 MDL model has an unknown version: AI_MDL_VERSION (=6) is "
"the expected file format version");
}
if (pcHeader->num_skins)
{
if(!pcHeader->skinwidth || !pcHeader->skinheight)
{
DefaultLogger::get()->warn("Skin width or height are 0. Division through "
"zero would occur ...");
}
}
}
}
@ -994,8 +1003,9 @@ void MDLImporter::ReadFaces_3DGS_MDL7(
unsigned int iIndex = pcGroupTris->v_index[c];
if(iIndex > (unsigned int)groupInfo.pcGroup->numverts)
{
// LOG
iIndex = groupInfo.pcGroup->numverts-1;
// (we might need to read this section a second time - to process
// frame vertices correctly)
const_cast<MDL::Triangle_MDL7*>(pcGroupTris)->v_index[c] = iIndex = groupInfo.pcGroup->numverts-1;
DefaultLogger::get()->warn("Index overflow in MDL7 vertex list");
}
@ -1022,14 +1032,6 @@ void MDLImporter::ReadFaces_3DGS_MDL7(
vNormal.x = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[0];
vNormal.z = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[1];
vNormal.y = _AI_MDL7_ACCESS_VERT(groupInfo.pcGroupVerts,iIndex,pcHeader->mainvertex_stc_size) .norm[2];
// FIX: It seems to be necessary to invert all normals
// FIX2: No, it is not necessary :-)
#if 0
vNormal.x *= -1.0f;
vNormal.y *= -1.0f;
vNormal.z *= -1.0f;
#endif
}
else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size)
{
@ -1108,6 +1110,7 @@ void MDLImporter::ReadFaces_3DGS_MDL7(
}
// ------------------------------------------------------------------------------------------------
bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
MDL::IntGroupData_MDL7& groupData,
MDL::IntSharedData_MDL7& shared,
const unsigned char* szCurrent,
const unsigned char** szCurrentOut)
@ -1119,9 +1122,10 @@ bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInf
// if we have no bones we can simply skip all frames,
// otherwise we'll need to process them.
// FIX: If we need another frame than the first we must apply frame vertex replacements ...
for(unsigned int iFrame = 0; iFrame < (unsigned int)groupInfo.pcGroup->numframes;++iFrame)
{
MDL::IntFrameInfo_MDL7 frame((const MDL::Frame_MDL7*)szCurrent,iFrame);
MDL::IntFrameInfo_MDL7 frame ((const MDL::Frame_MDL7*)szCurrent,iFrame);
const unsigned int iAdd = pcHeader->frame_stc_size +
frame.pcFrame->vertices_count * pcHeader->framevertex_stc_size +
@ -1129,15 +1133,82 @@ bool MDLImporter::ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInf
if (((const char*)szCurrent - (const char*)pcHeader) + iAdd > (unsigned int)pcHeader->data_size)
{
DefaultLogger::get()->warn("Index overflow in frame area. Ignoring all frames and "
"all further groups, too.");
DefaultLogger::get()->warn("Index overflow in frame area. "
"Ignoring all frames and all further mesh groups, too.");
// don't parse more groups if we can't even read one
// FIXME: sometimes this seems to occur even for valid files ...
*szCurrentOut = szCurrent;
return false;
}
// our output frame?
if (configFrameID == iFrame)
{
const MDL::Vertex_MDL7* pcFrameVertices = (const MDL::Vertex_MDL7*)
(szCurrent + pcHeader->framevertex_stc_size);
for (unsigned int qq = 0; qq < frame.pcFrame->vertices_count;++qq)
{
// I assume this are simple replacements for normal
// vertices, the bone index serving as the index of the
// vertex to be replaced.
uint16_t iIndex = _AI_MDL7_ACCESS(pcFrameVertices,qq,
pcHeader->framevertex_stc_size,MDL::Vertex_MDL7).vertindex;
if (iIndex >= groupInfo.pcGroup->numverts)
{
DefaultLogger::get()->warn("Invalid vertex index in frame vertex section. "
"Skipping this frame vertex");
continue;
}
aiVector3D vPosition,vNormal;
vPosition.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .x;
vPosition.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .y;
vPosition.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .z;
// now read the normal vector
if (AI_MDL7_FRAMEVERTEX030305_STCSIZE <= pcHeader->mainvertex_stc_size)
{
// read the full normal vector
vNormal.x = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[0];
vNormal.z = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[1];
vNormal.y = _AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,pcHeader->framevertex_stc_size) .norm[2];
}
else if (AI_MDL7_FRAMEVERTEX120503_STCSIZE <= pcHeader->mainvertex_stc_size)
{
// read the normal vector from Quake2's smart table
MD2::LookupNormalIndex(_AI_MDL7_ACCESS_VERT(pcFrameVertices,qq,
pcHeader->framevertex_stc_size) .norm162index,vNormal);
std::swap(vNormal.z,vNormal.y);
}
// FIXME: O(n^2) at the moment ...
// shouldn't be too worse, frame vertices aren't required more
// than once a century ...
const MDL::Triangle_MDL7* pcGroupTris = groupInfo.pcGroupTris;
unsigned int iOutIndex = 0;
for (unsigned int iTriangle = 0; iTriangle < (unsigned int)groupInfo.pcGroup->numtris; ++iTriangle)
{
// iterate through all indices of the current triangle
for (unsigned int c = 0; c < 3;++c,++iOutIndex)
{
// replace the vertex with the new data
const unsigned int iCurIndex = pcGroupTris->v_index[c];
if (iCurIndex == iIndex)
{
groupData.vPositions[iOutIndex] = vPosition;
groupData.vNormals[iOutIndex] = vNormal;
}
}
// get the next triangle in the list
pcGroupTris = (const MDL::Triangle_MDL7*)((const char*)pcGroupTris +
pcHeader->triangle_stc_size);
}
}
}
// parse bone trafo matrix keys (only if there are bones ...)
if (shared.apcOutBones)
{
@ -1331,9 +1402,13 @@ void MDLImporter::InternReadFile_3DGS_MDL7( )
}
// store the name of the group
::memcpy(&aszGroupNameBuffer[iGroup*AI_MDL7_MAX_GROUPNAMESIZE],
const unsigned int ofs = iGroup*AI_MDL7_MAX_GROUPNAMESIZE;
::memcpy(&aszGroupNameBuffer[ofs],
groupInfo.pcGroup->name,AI_MDL7_MAX_GROUPNAMESIZE);
// make sure '\0' is at the end
aszGroupNameBuffer[ofs+AI_MDL7_MAX_GROUPNAMESIZE-1] = '\0';
// read all skins
sharedData.pcMats.reserve(sharedData.pcMats.size() + groupInfo.pcGroup->numskins);
sharedData.abNeedMaterials.resize(sharedData.abNeedMaterials.size() +
@ -1362,6 +1437,8 @@ void MDLImporter::InternReadFile_3DGS_MDL7( )
aiString szName;
szName.Set(AI_DEFAULT_MATERIAL_NAME);
pcHelper->AddProperty(&szName,AI_MATKEY_NAME);
sharedData.abNeedMaterials.resize(1,false);
}
// now get a pointer to all texture coords in the group
@ -1379,10 +1456,9 @@ void MDLImporter::InternReadFile_3DGS_MDL7( )
VALIDATE_FILE_SIZE(szCurrent);
MDL::IntSplittedGroupData_MDL7 splittedGroupData(sharedData,avOutList[iGroup]);
MDL::IntGroupData_MDL7 groupData;
if (groupInfo.pcGroup->numtris && groupInfo.pcGroup->numverts)
{
MDL::IntGroupData_MDL7 groupData;
// build output vectors
const unsigned int iNumVertices = groupInfo.pcGroup->numtris*3;
groupData.vPositions.resize(iNumVertices);
@ -1426,7 +1502,7 @@ void MDLImporter::InternReadFile_3DGS_MDL7( )
"vertices or faces. It will be skipped.");
// process all frames
if(!ProcessFrames_3DGS_MDL7(groupInfo,sharedData,szCurrent,&szCurrent))
if(!ProcessFrames_3DGS_MDL7(groupInfo,groupData, sharedData,szCurrent,&szCurrent))
{
break;
}
@ -1497,8 +1573,8 @@ void MDLImporter::InternReadFile_3DGS_MDL7( )
delete[] avOutList;
delete[] aszGroupNameBuffer;
DEBUG_INVALIDATE_PTR(avOutList);
DEBUG_INVALIDATE_PTR(aszGroupNameBuffer);
AI_DEBUG_INVALIDATE_PTR(avOutList);
AI_DEBUG_INVALIDATE_PTR(aszGroupNameBuffer);
// build a final material list.
this->CopyMaterials_3DGS_MDL7(sharedData);
@ -1550,7 +1626,7 @@ void MDLImporter::CopyMaterials_3DGS_MDL7(MDL::IntSharedData_MDL7 &shared)
{
// destruction is done by the destructor of sh
delete shared.pcMats[i];
DEBUG_INVALIDATE_PTR(shared.pcMats[i]);
AI_DEBUG_INVALIDATE_PTR(shared.pcMats[i]);
continue;
}
this->pScene->mMaterials[p] = shared.pcMats[i];
@ -1955,55 +2031,6 @@ void MDLImporter::JoinSkins_3DGS_MDL7(
}
}
// ------------------------------------------------------------------------------------------------
void MDLImporter::FlipNormals(aiMesh* pcMesh)
{
ai_assert(NULL != pcMesh);
// compute the bounding box of both the model vertices + normals and
// the umodified model vertices. Then check whether the first BB
// is smaller than the second. In this case we can assume that the
// normals need to be flipped, although there are a few special cases ..
// convex, concave, planar models ...
aiVector3D vMin0(1e10f,1e10f,1e10f);
aiVector3D vMin1(1e10f,1e10f,1e10f);
aiVector3D vMax0(-1e10f,-1e10f,-1e10f);
aiVector3D vMax1(-1e10f,-1e10f,-1e10f);
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
vMin1.x = std::min(vMin1.x,pcMesh->mVertices[i].x);
vMin1.y = std::min(vMin1.y,pcMesh->mVertices[i].y);
vMin1.z = std::min(vMin1.z,pcMesh->mVertices[i].z);
vMax1.x = std::max(vMax1.x,pcMesh->mVertices[i].x);
vMax1.y = std::max(vMax1.y,pcMesh->mVertices[i].y);
vMax1.z = std::max(vMax1.z,pcMesh->mVertices[i].z);
aiVector3D vWithNormal = pcMesh->mVertices[i] + pcMesh->mNormals[i];
vMin0.x = std::min(vMin0.x,vWithNormal.x);
vMin0.y = std::min(vMin0.y,vWithNormal.y);
vMin0.z = std::min(vMin0.z,vWithNormal.z);
vMax0.x = std::max(vMax0.x,vWithNormal.x);
vMax0.y = std::max(vMax0.y,vWithNormal.y);
vMax0.z = std::max(vMax0.z,vWithNormal.z);
}
if (::fabsf((vMax0.x - vMin0.x) * (vMax0.y - vMin0.y) * (vMax0.z - vMin0.z)) <=
::fabsf((vMax1.x - vMin1.x) * (vMax1.y - vMin1.y) * (vMax1.z - vMin1.z)))
{
DefaultLogger::get()->info("The models normals are facing inwards "
"(or the model is too planar or concave). Flipping the normal set ...");
for (unsigned int i = 0; i < pcMesh->mNumVertices;++i)
{
pcMesh->mNormals[i] *= -1.0f;
}
}
}
// ------------------------------------------------------------------------------------------------
void MDLImporter::InternReadFile_HL2( )
{
const MDL::Header_HL2* pcHeader = (const MDL::Header_HL2*)this->mBuffer;

View File

@ -393,6 +393,7 @@ protected:
* some tiny and unsolved problems ... )
*/
bool ProcessFrames_3DGS_MDL7(const MDL::IntGroupInfo_MDL7& groupInfo,
MDL::IntGroupData_MDL7& groupData,
MDL::IntSharedData_MDL7& shared,
const unsigned char* szCurrent,
const unsigned char** szCurrentOut);
@ -428,17 +429,11 @@ protected:
MDL::IntGroupData_MDL7& groupData,
MDL::IntSplittedGroupData_MDL7& splittedGroupData);
// -------------------------------------------------------------------
/** Try to determine whether the normals of the model are flipped
* Some MDL7 models seem to have flipped normals (and there is also
* an option "flip normals" in MED). However, I don't see a proper
* way to read from the file whether all normals are correctly
* facing outwards ...
*/
void FlipNormals(aiMesh* pcMesh);
protected:
/** Configuration option: frame to be loaded */
unsigned int configFrameID;
/** Buffer to hold the loaded file */
unsigned char* mBuffer;

View File

@ -565,7 +565,9 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
// sometimes there are MDL7 files which have a monochrome
// texture instead of material colors ... posssible they have
// been converted to MDL7 from other formats, such as MDL5
aiColor4D clrTexture = this->ReplaceTextureWithColor(pcNew);
aiColor4D clrTexture;
if (pcNew)clrTexture = this->ReplaceTextureWithColor(pcNew);
else clrTexture.r = std::numeric_limits<float>::quiet_NaN();
// check whether a material definition is contained in the skin
if (iType & AI_MDL7_SKINTYPE_MATERIAL)
@ -654,21 +656,27 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
// data structures in the aiScene instance
if (pcNew && this->pScene->mNumTextures <= 999)
{
// place this as diffuse texture
char szCurrent[5];
::sprintf(szCurrent,"*%i",this->pScene->mNumTextures);
aiString szFile;
const size_t iLen = strlen((const char*)szCurrent);
size_t iLen2 = iLen+1;
iLen2 = iLen2 > MAXLEN ? MAXLEN : iLen2;
::memcpy(szFile.data,(const char*)szCurrent,iLen2);
szFile.length = iLen;
// place this as diffuse texture
char szCurrent[5];
::sprintf(szCurrent,"*%i",this->pScene->mNumTextures);
pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
aiString szFile;
const size_t iLen = strlen((const char*)szCurrent);
::memcpy(szFile.data,(const char*)szCurrent,iLen+1);
szFile.length = iLen;
// store the texture
pcMatOut->AddProperty(&szFile,AI_MATKEY_TEXTURE_DIFFUSE(0));
// store the texture
if (!this->pScene->mNumTextures)
{
this->pScene->mNumTextures = 1;
this->pScene->mTextures = new aiTexture*[1];
this->pScene->mTextures[0] = pcNew;
}
else
{
aiTexture** pc = this->pScene->mTextures;
this->pScene->mTextures = new aiTexture*[this->pScene->mNumTextures+1];
for (unsigned int i = 0; i < this->pScene->mNumTextures;++i)
@ -677,7 +685,7 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
this->pScene->mTextures[this->pScene->mNumTextures] = pcNew;
this->pScene->mNumTextures++;
delete[] pc;
}
}
VALIDATE_FILE_SIZE(szCurrent);
*szCurrentOut = szCurrent;
@ -716,6 +724,9 @@ void MDLImporter::SkipSkinLump_3DGS_MDL7(
tex.mWidth = iWidth;
this->ParseTextureColorData(szCurrent,iMasked,&iSkip,&tex);
// FIX: Important, otherwise the destructor will crash
tex.pcData = NULL;
// skip length of texture data
szCurrent += iSkip;
}
@ -761,12 +772,13 @@ void MDLImporter::ParseSkinLump_3DGS_MDL7(
pcSkin->typ,pcSkin->width,pcSkin->height);
// place the name of the skin in the material
const size_t iLen = strlen(pcSkin->texture_name);
if (0 != iLen)
if (pcSkin->texture_name[0])
{
// the 0 termination could be there or not - we can't know
aiString szFile;
memcpy(szFile.data,pcSkin->texture_name,sizeof(pcSkin->texture_name));
szFile.length = iLen;
::memcpy(szFile.data,pcSkin->texture_name,sizeof(pcSkin->texture_name));
szFile.data[sizeof(pcSkin->texture_name)] = '\0';
szFile.length = ::strlen(szFile.data);
pcMatOut->AddProperty(&szFile,AI_MATKEY_NAME);
}

View File

@ -40,74 +40,19 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "MaterialSystem.h"
#include "StringComparison.h"
#include "Hash.h"
#include "../include/aiMaterial.h"
#include "../include/aiAssert.h"
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// hashing function taken from
// http://www.azillionmonkeys.com/qed/hash.html
// (incremental version of the hashing function)
// (stdint.h should have been been included here)
#undef get16bits
#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \
|| defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__)
#define get16bits(d) (*((const uint16_t *) (d)))
// we are using sprintf only on fixed-size buffers, so the
// compiler should automatically expand the template sprintf_s<>
#if _MSC_VER >= 1400
# define sprintf sprintf_s
#endif
#if !defined (get16bits)
#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8)\
+(uint32_t)(((const uint8_t *)(d))[0]) )
#endif
// ------------------------------------------------------------------------------------------------
uint32_t SuperFastHash (const char * data, int len, uint32_t hash = 0) {
uint32_t tmp;
int rem;
if (len <= 0 || data == NULL) return 0;
rem = len & 3;
len >>= 2;
/* Main loop */
for (;len > 0; len--) {
hash += get16bits (data);
tmp = (get16bits (data+2) << 11) ^ hash;
hash = (hash << 16) ^ tmp;
data += 2*sizeof (uint16_t);
hash += hash >> 11;
}
/* Handle end cases */
switch (rem) {
case 3: hash += get16bits (data);
hash ^= hash << 16;
hash ^= data[sizeof (uint16_t)] << 18;
hash += hash >> 11;
break;
case 2: hash += get16bits (data);
hash ^= hash << 11;
hash += hash >> 17;
break;
case 1: hash += *data;
hash ^= hash << 10;
hash += hash >> 1;
}
/* Force "avalanching" of final 127 bits */
hash ^= hash << 3;
hash += hash >> 5;
hash ^= hash << 4;
hash += hash >> 17;
hash ^= hash << 25;
hash += hash >> 6;
return hash;
}
// ------------------------------------------------------------------------------------------------
aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
const char* pKey,
@ -121,7 +66,7 @@ aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey.data, pKey ))
{
*pPropOut = pMat->mProperties[i];
return AI_SUCCESS;
@ -145,14 +90,13 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey.data, pKey ))
{
// data is given in floats, simply copy it
if( aiPTI_Float == pMat->mProperties[i]->mType ||
aiPTI_Buffer == pMat->mProperties[i]->mType)
{
unsigned int iWrite = pMat->mProperties[i]->
mDataLength / sizeof(float);
unsigned int iWrite = pMat->mProperties[i]->mDataLength / sizeof(float);
if (NULL != pMax)
iWrite = *pMax < iWrite ? *pMax : iWrite;
@ -204,7 +148,7 @@ aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat,
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey.data, pKey ))
{
// data is given in ints, simply copy it
if( aiPTI_Integer == pMat->mProperties[i]->mType ||
@ -275,12 +219,12 @@ aiReturn aiGetMaterialString(const aiMaterial* pMat,
{
if (NULL != pMat->mProperties[i])
{
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey->data, pKey ))
if (0 == ASSIMP_stricmp( pMat->mProperties[i]->mKey.data, pKey ))
{
if( aiPTI_String == pMat->mProperties[i]->mType)
{
memcpy (pOut, pMat->mProperties[i]->mData,
sizeof(aiString));
const aiString* pcSrc = (const aiString*)pMat->mProperties[i]->mData;
::memcpy (pOut->data, pcSrc->data, (pOut->length = pcSrc->length)+1);
}
// wrong type
else return AI_FAILURE;
@ -291,6 +235,29 @@ aiReturn aiGetMaterialString(const aiMaterial* pMat,
return AI_FAILURE;
}
// ------------------------------------------------------------------------------------------------
MaterialHelper::MaterialHelper()
{
// allocate 5 entries by default
this->mNumProperties = 0;
this->mNumAllocated = 5;
this->mProperties = new aiMaterialProperty*[5];
return;
}
// ------------------------------------------------------------------------------------------------
MaterialHelper::~MaterialHelper()
{
for (unsigned int i = 0; i < this->mNumProperties;++i)
{
// be careful ...
if(NULL != this->mProperties[i])
{
delete[] this->mProperties[i]->mData;
delete this->mProperties[i];
}
}
return;
}
// ------------------------------------------------------------------------------------------------
uint32_t MaterialHelper::ComputeHash()
{
uint32_t hash = 1503; // magic start value, choosen to be my birthday :-)
@ -299,9 +266,9 @@ uint32_t MaterialHelper::ComputeHash()
aiMaterialProperty* prop;
// NOTE: We need to exclude the material name from the hash
if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey->data,AI_MATKEY_NAME))
if ((prop = this->mProperties[i]) && 0 != ::strcmp(prop->mKey.data,AI_MATKEY_NAME))
{
hash = SuperFastHash(prop->mKey->data,prop->mKey->length,hash);
hash = SuperFastHash(prop->mKey.data,prop->mKey.length,hash);
hash = SuperFastHash(prop->mData,prop->mDataLength,hash);
}
}
@ -316,7 +283,7 @@ aiReturn MaterialHelper::RemoveProperty (const char* pKey)
{
if (this->mProperties[i]) // just for safety
{
if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey->data, pKey ))
if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey.data, pKey ))
{
// delete this entry
delete[] this->mProperties[i]->mData;
@ -352,7 +319,7 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
{
if (this->mProperties[i])
{
if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey->data, pKey ))
if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey.data, pKey ))
{
// delete this entry
delete[] this->mProperties[i]->mData;
@ -365,16 +332,15 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
aiMaterialProperty* pcNew = new aiMaterialProperty();
// fill this
pcNew->mKey = new aiString();
pcNew->mType = pType;
pcNew->mDataLength = pSizeInBytes;
pcNew->mData = new char[pSizeInBytes];
memcpy (pcNew->mData,pInput,pSizeInBytes);
pcNew->mKey->length = strlen(pKey);
ai_assert ( MAXLEN > pcNew->mKey->length);
strcpy( pcNew->mKey->data, pKey );
pcNew->mKey.length = ::strlen(pKey);
ai_assert ( MAXLEN > pcNew->mKey.length);
::strcpy( pcNew->mKey.data, pKey );
if (0xFFFFFFFF != iOutIndex)
{
@ -391,7 +357,7 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
aiMaterialProperty** ppTemp = new aiMaterialProperty*[this->mNumAllocated];
if (NULL == ppTemp)return AI_OUTOFMEMORY;
memcpy (ppTemp,this->mProperties,iOld * sizeof(void*));
::memcpy (ppTemp,this->mProperties,iOld * sizeof(void*));
delete[] this->mProperties;
this->mProperties = ppTemp;
@ -404,8 +370,10 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
aiReturn MaterialHelper::AddProperty (const aiString* pInput,
const char* pKey)
{
// fix ... don't keep the whole string buffer
return this->AddBinaryProperty(pInput,
sizeof(aiString),pKey,aiPTI_String);
pInput->length+1+ (size_t)((uint8_t*)&pInput->data - (uint8_t*)&pInput->length),
pKey,aiPTI_String);
}
// ------------------------------------------------------------------------------------------------
void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest,
@ -421,21 +389,41 @@ void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest,
aiMaterialProperty** pcOld = pcDest->mProperties;
pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated];
if (pcOld)
if (iOldNum && pcOld)
{
for (unsigned int i = 0; i < iOldNum;++i)
pcDest->mProperties[i] = pcOld[i];
delete[] pcDest->mProperties;
delete[] pcOld;
}
for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i)
{
pcDest->mProperties[i]->mKey = new aiString(*pcSrc->mProperties[i]->mKey);
pcDest->mProperties[i]->mDataLength = pcSrc->mProperties[i]->mDataLength;
pcDest->mProperties[i]->mType = pcSrc->mProperties[i]->mType;
pcDest->mProperties[i]->mData = new char[pcDest->mProperties[i]->mDataLength];
memcpy(pcDest->mProperties[i]->mData,pcSrc->mProperties[i]->mData,
pcDest->mProperties[i]->mDataLength);
aiMaterialProperty* propSrc = pcSrc->mProperties[i];
// search whether we have already a property with this name
// (if yes we overwrite the old one)
aiMaterialProperty* prop;
for (unsigned int q = 0; q < iOldNum;++q)
{
prop = pcDest->mProperties[q];
if (propSrc->mKey.length == prop->mKey.length &&
!ASSIMP_stricmp(propSrc->mKey.data,prop->mKey.data))
{
delete prop;
// collapse the whole array ...
::memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q);
i--;
pcDest->mNumProperties--;
}
}
prop = pcDest->mProperties[i] = new aiMaterialProperty();
prop->mKey = propSrc->mKey;
prop->mDataLength = propSrc->mDataLength;
prop->mType = propSrc->mType;
prop->mData = new char[propSrc->mDataLength];
::memcpy(prop->mData,propSrc->mData,prop->mDataLength);
}
return;
}
@ -545,12 +533,7 @@ aiReturn aiGetMaterialTexture(const aiMaterial* pcMat,
if (iIndex > 100)return AI_FAILURE;
// get the path to the texture
#if _MSC_VER >= 1400
if(0 >= sprintf_s(szKey,"%s[%i]",szPathBase,iIndex))DummyAssertFunction();
#else
if(0 >= sprintf(szKey,"%s[%i]",szPathBase,iIndex))DummyAssertFunction();
#endif
if (AI_SUCCESS != aiGetMaterialString(pcMat,szKey,szOut))
{
return AI_FAILURE;
@ -559,11 +542,7 @@ aiReturn aiGetMaterialTexture(const aiMaterial* pcMat,
if (piUVIndex)
{
int iUV;
#if _MSC_VER >= 1400
if(0 >= sprintf_s(szKey,"%s[%i]",szUVBase,iIndex))DummyAssertFunction();
#else
if(0 >= sprintf(szKey,"%s[%i]",szUVBase,iIndex))DummyAssertFunction();
#endif
if (AI_SUCCESS != aiGetMaterialInteger(pcMat,szKey,&iUV))
iUV = 0;
@ -573,11 +552,7 @@ aiReturn aiGetMaterialTexture(const aiMaterial* pcMat,
if (pfBlendFactor)
{
float fBlend;
#if _MSC_VER >= 1400
if(0 >= sprintf_s(szKey,"%s[%i]",szBlendBase,iIndex))DummyAssertFunction();
#else
if(0 >= sprintf(szKey,"%s[%i]",szBlendBase,iIndex))DummyAssertFunction();
#endif
if (AI_SUCCESS != aiGetMaterialFloat(pcMat,szKey,&fBlend))
fBlend = 1.0f;
@ -588,11 +563,7 @@ aiReturn aiGetMaterialTexture(const aiMaterial* pcMat,
if (peTextureOp)
{
aiTextureOp op;
#if _MSC_VER >= 1400
if(0 >= sprintf_s(szKey,"%s[%i]",szOpBase,iIndex))DummyAssertFunction();
#else
if(0 >= sprintf(szKey,"%s[%i]",szOpBase,iIndex))DummyAssertFunction();
#endif
if (AI_SUCCESS != aiGetMaterialInteger(pcMat,szKey,(int*)&op))
op = aiTextureOp_Multiply;
@ -605,11 +576,7 @@ aiReturn aiGetMaterialTexture(const aiMaterial* pcMat,
aiTextureMapMode eMode;
for (unsigned int q = 0; q < 3;++q)
{
#if _MSC_VER >= 1400
if(0 >= sprintf_s(szKey,"%s[%i]",aszMapModeBase[q],iIndex))DummyAssertFunction();
#else
if(0 >= sprintf(szKey,"%s[%i]",aszMapModeBase[q],iIndex))DummyAssertFunction();
#endif
if (AI_SUCCESS != aiGetMaterialInteger(pcMat,szKey,(int*)&eMode))
{
eMode = aiTextureMapMode_Wrap;

View File

@ -55,8 +55,8 @@ class ASSIMP_API MaterialHelper : public ::aiMaterial
{
public:
inline MaterialHelper();
inline ~MaterialHelper();
MaterialHelper();
~MaterialHelper();
// -------------------------------------------------------------------
/** Add a property with a given key and type info to the material
@ -125,36 +125,6 @@ public:
};
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
inline MaterialHelper::MaterialHelper()
{
// allocate 5 entries by default
this->mNumProperties = 0;
this->mNumAllocated = 5;
this->mProperties = new aiMaterialProperty*[5];
return;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
inline MaterialHelper::~MaterialHelper()
{
for (unsigned int i = 0; i < this->mNumProperties;++i)
{
// be careful ...
if(NULL != this->mProperties[i])
{
delete[] this->mProperties[i]->mKey;
delete[] this->mProperties[i]->mData;
delete this->mProperties[i];
}
}
return;
}
// ---------------------------------------------------------------------------
// ---------------------------------------------------------------------------
template<class TYPE>

View File

@ -122,13 +122,13 @@ void RemoveRedundantMatsProcess::Execute( aiScene* pScene)
{
// generate new names for all modified materials
const unsigned int idx = aiMappingTable[p];
if (!ppcMaterials[idx])
if (ppcMaterials[idx])
{
aiString sz;
sz.length = ::sprintf(sz.data,"aiMaterial #%i",p);
ppcMaterials[idx] = pScene->mMaterials[p];
((MaterialHelper*)pScene->mMaterials[p])->AddProperty(&sz,AI_MATKEY_NAME);
((MaterialHelper*)ppcMaterials[idx])->AddProperty(&sz,AI_MATKEY_NAME);
}
else ppcMaterials[idx] = pScene->mMaterials[p];
}
// update all material indices
for (unsigned int p = 0; p < pScene->mNumMeshes;++p)

398
code/STLLoader.cpp 100644
View File

@ -0,0 +1,398 @@
/*
---------------------------------------------------------------------------
Open Asset Import Library (ASSIMP)
---------------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the following
conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
---------------------------------------------------------------------------
*/
/** @file Implementation of the STL importer class */
// internal headers
#include "STLLoader.h"
#include "MaterialSystem.h"
#include "ParsingUtils.h"
#include "fast_atof.h"
// public assimp headers
#include "../include/IOStream.h"
#include "../include/IOSystem.h"
#include "../include/aiMesh.h"
#include "../include/aiScene.h"
#include "../include/aiAssert.h"
#include "../include/DefaultLogger.h"
// boost headers
#include <boost/scoped_ptr.hpp>
using namespace Assimp;
// ------------------------------------------------------------------------------------------------
// Constructor to be privately used by Importer
STLImporter::STLImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Destructor, private as well
STLImporter::~STLImporter()
{
}
// ------------------------------------------------------------------------------------------------
// Returns whether the class can handle the format of the given file.
bool STLImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const
{
// simple check of file extension is enough for the moment
std::string::size_type pos = pFile.find_last_of('.');
// no file extension - can't read
if( pos == std::string::npos)return false;
std::string extension = pFile.substr( pos);
if (extension.length() < 4)return false;
if (extension[0] != '.')return false;
if (extension[1] != 's' && extension[1] != 'S')return false;
if (extension[2] != 't' && extension[2] != 'T')return false;
if (extension[3] != 'l' && extension[3] != 'L')return false;
return true;
}
// ------------------------------------------------------------------------------------------------
// Imports the given file into the given scene structure.
void STLImporter::InternReadFile(
const std::string& pFile, aiScene* pScene, IOSystem* pIOHandler)
{
boost::scoped_ptr<IOStream> file( pIOHandler->Open( pFile, "rb"));
// Check whether we can read from the file
if( file.get() == NULL)
{
throw new ImportErrorException( "Failed to open STL file " + pFile + ".");
}
this->fileSize = (unsigned int)file->FileSize();
// allocate storage and copy the contents of the file to a memory buffer
// (terminate it with zero)
this->mBuffer = new char[fileSize+1];
this->pScene = pScene;
file->Read( (void*)mBuffer, 1, fileSize);
const_cast<char*>(this->mBuffer)[fileSize] = '\0';
// the default vertex color is white
clrColorDefault.r = clrColorDefault.g = clrColorDefault.b = clrColorDefault.a = 1.0f;
// allocate one mesh
pScene->mNumMeshes = 1;
pScene->mMeshes = new aiMesh*[1];
aiMesh* pMesh = pScene->mMeshes[0] = new aiMesh();
pMesh->mMaterialIndex = 0;
// allocate a single node
pScene->mRootNode = new aiNode();
pScene->mRootNode->mNumMeshes = 1;
pScene->mRootNode->mMeshes = new unsigned int[1];
pScene->mRootNode->mMeshes[0] = 0;
bool bMatClr = false;
try
{
// check whether the file starts with 'solid' -
// in this case we can simply assume it IS a text file. finished.
if (!::strncmp(mBuffer,"solid",5))
this->LoadASCIIFile();
else bMatClr = this->LoadBinaryFile();
// now copy faces
pMesh->mFaces = new aiFace[pMesh->mNumFaces];
for (unsigned int i = 0, p = 0; i < pMesh->mNumFaces;++i)
{
aiFace& face = pMesh->mFaces[i];
face.mIndices = new unsigned int[face.mNumIndices = 3];
for (unsigned int o = 0; o < 3;++o,++p)
face.mIndices[o] = p;
}
}
catch (ImportErrorException* ex)
{
delete[] this->mBuffer;AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
throw ex;
}
// create a single default material - everything white, as
// we have vertex colors
MaterialHelper* pcMat = new MaterialHelper();
aiString s;
s.Set(AI_DEFAULT_MATERIAL_NAME);
pcMat->AddProperty(&s, AI_MATKEY_NAME);
aiColor4D clrDiffuse(1.0f,1.0f,1.0f,1.0f);
if (bMatClr)clrDiffuse = this->clrColorDefault;
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_DIFFUSE);
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_SPECULAR);
clrDiffuse = aiColor4D(0.05f,0.05f,0.05f,1.0f);
pcMat->AddProperty(&clrDiffuse,1,AI_MATKEY_COLOR_AMBIENT);
pScene->mNumMaterials = 1;
pScene->mMaterials = new aiMaterial*[1];
pScene->mMaterials[0] = pcMat;
delete[] this->mBuffer;AI_DEBUG_INVALIDATE_PTR(this->mBuffer);
}
// ------------------------------------------------------------------------------------------------
// Read an ASCII STL file
void STLImporter::LoadASCIIFile()
{
aiMesh* pMesh = pScene->mMeshes[0];
const char* sz = mBuffer + 5; // skip the "solid"
SkipSpaces(&sz);
const char* szMe = sz;
while (!::IsSpaceOrNewLine(*sz))sz++;
unsigned int temp;
// setup the name of the node
if (temp = unsigned int(sz-szMe))
{
pScene->mRootNode->mName.length = temp;
::memcpy(pScene->mRootNode->mName.data,szMe,temp);
pScene->mRootNode->mName.data[temp] = '\0';
}
else pScene->mRootNode->mName.Set("<STL_ASCII>");
// try to guess how many vertices we could have
// assume we'll need 160 bytes for each face
pMesh->mNumVertices = ( pMesh->mNumFaces = fileSize / 160 ) * 3;
pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
unsigned int curFace = 0, curVertex = 0;
while (true)
{
// go to the next token
if(!SkipSpacesAndLineEnd(&sz))
{
// seems we're finished although there was no end marker
DefaultLogger::get()->warn("STL: unexpected EOF. \'endsolid\' keyword was expected");
break;
}
// facet normal -0.13 -0.13 -0.98
if (!::strncmp(sz,"facet",5) && ::IsSpaceOrNewLine(*(sz+5)))
{
if (3 != curVertex)DefaultLogger::get()->warn("STL: A new facet begins but the old is not yet complete");
if (pMesh->mNumFaces == curFace)
{
// need to resize the arrays, our size estimate was wrong
unsigned int iNeededSize = unsigned int(sz-mBuffer) / pMesh->mNumFaces;
if (iNeededSize <= 160)iNeededSize >>= 1; // prevent endless looping
unsigned int add = unsigned int((mBuffer+fileSize)-sz) / iNeededSize;
add += add >> 3; // add 12.5% as buffer
iNeededSize = (pMesh->mNumFaces + add)*3;
aiVector3D* pv = new aiVector3D[iNeededSize];
::memcpy(pv,pMesh->mVertices,pMesh->mNumVertices*sizeof(aiVector3D));
delete[] pMesh->mVertices;
pMesh->mVertices = pv;
pv = new aiVector3D[iNeededSize];
::memcpy(pv,pMesh->mNormals,pMesh->mNumVertices*sizeof(aiVector3D));
delete[] pMesh->mNormals;
pMesh->mNormals = pv;
pMesh->mNumVertices = iNeededSize;
pMesh->mNumFaces += add;
}
aiVector3D* vn = &pMesh->mNormals[curFace++*3];
sz += 6;
curVertex = 0;
SkipSpaces(&sz);
if (::strncmp(sz,"normal",6))
{
DefaultLogger::get()->warn("STL: a facet normal vector was expected but not found");
}
else
{
sz += 7;
SkipSpaces(&sz);
sz = fast_atof_move(sz, vn->x );
SkipSpaces(&sz);
sz = fast_atof_move(sz, vn->y );
SkipSpaces(&sz);
sz = fast_atof_move(sz, vn->z );
*(vn+1) = *vn;
*(vn+2) = *vn;
}
}
// vertex 1.50000 1.50000 0.00000
else if (!::strncmp(sz,"vertex",6) && ::IsSpaceOrNewLine(*(sz+6)))
{
if (3 == curVertex)
{
DefaultLogger::get()->error("STL: a facet with more than 3 vertices has been found");
}
else
{
sz += 7;
SkipSpaces(&sz);
aiVector3D* vn = &pMesh->mVertices[(curFace-1)*3 + curVertex++];
sz = fast_atof_move(sz, vn->x );
SkipSpaces(&sz);
sz = fast_atof_move(sz, vn->y );
SkipSpaces(&sz);
sz = fast_atof_move(sz, vn->z );
}
}
else if (!::strncmp(sz,"endsolid",8))
{
// finished!
break;
}
// else skip the whole identifier
else while (!::IsSpaceOrNewLine(*sz))++sz;
}
if (!curFace)
{
pMesh->mNumFaces = 0;
throw new ImportErrorException("STL: ASCII file is empty or invalid; no data loaded");
}
pMesh->mNumFaces = curFace;
pMesh->mNumVertices = curFace*3;
// we are finished!
}
// ------------------------------------------------------------------------------------------------
// Read a binary STL file
bool STLImporter::LoadBinaryFile()
{
// skip the first 80 bytes
if (fileSize < 84)
throw new ImportErrorException("STL: file is too small for the header");
bool bIsMaterialise = false;
// search for an occurence of "COLOR=" in the header
const char* sz2 = (const char*)mBuffer;
const char* const szEnd = sz2+80;
while (sz2 < szEnd)
{
if ('C' == *sz2++ && 'O' == *sz2++ && 'L' == *sz2++ &&
'O' == *sz2++ && 'R' == *sz2++ && '=' == *sz2++)
{
// read the default vertex color for facets
bIsMaterialise = true;
this->clrColorDefault.r = (*sz2++) / 255.0f;
this->clrColorDefault.g = (*sz2++) / 255.0f;
this->clrColorDefault.b = (*sz2++) / 255.0f;
this->clrColorDefault.a = (*sz2++) / 255.0f;
break;
}
}
const unsigned char* sz = (const unsigned char*)mBuffer + 80;
// now read the number of facets
aiMesh* pMesh = pScene->mMeshes[0];
pScene->mRootNode->mName.Set("<STL_BINARY>");
pMesh->mNumFaces = *((uint32_t*)sz);
sz += 4;
if (fileSize < 84 + pMesh->mNumFaces*50)
throw new ImportErrorException("STL: file is too small to keep all facets");
if (!pMesh->mNumFaces)
throw new ImportErrorException("STL: file is empty. There are no facets defined");
pMesh->mNumVertices = pMesh->mNumFaces*3;
aiVector3D* vp,*vn;
vp = pMesh->mVertices = new aiVector3D[pMesh->mNumVertices];
vn = pMesh->mNormals = new aiVector3D[pMesh->mNumVertices];
for (unsigned int i = 0; i < pMesh->mNumFaces;++i)
{
*vn = *((aiVector3D*)sz);
sz += sizeof(aiVector3D);
*(vn+1) = *vn;
*(vn+2) = *vn;
*vp++ = *((aiVector3D*)sz);
sz += sizeof(aiVector3D);
*vp++ = *((aiVector3D*)sz);
sz += sizeof(aiVector3D);
*vp++ = *((aiVector3D*)sz);
sz += sizeof(aiVector3D);
uint16_t color = *((uint16_t*)sz);
sz += 2;
if (color & (1 << 15))
{
// seems we need to take the color
if (!pMesh->mColors[0])
{
pMesh->mColors[0] = new aiColor4D[pMesh->mNumVertices];
for (unsigned int i = 0; i <pMesh->mNumVertices;++i)
*pMesh->mColors[0]++ = this->clrColorDefault;
pMesh->mColors[0] -= pMesh->mNumVertices;
}
aiColor4D* clr = &pMesh->mColors[0][pMesh->mNumFaces*3];
clr->a = 1.0f;
if (bIsMaterialise) // fuck, this is reversed
{
clr->r = (color & 0x31u) / 31.0f;
clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
clr->b = ((color & (0x31u<<10))>>10u) / 31.0f;
}
else
{
clr->b = (color & 0x31u) / 31.0f;
clr->g = ((color & (0x31u<<5))>>5u) / 31.0f;
clr->r = ((color & (0x31u<<10))>>10u) / 31.0f;
}
// assign the color to all vertices of the face
*(clr+1) = *clr;
*(clr+2) = *clr;
}
}
if (bIsMaterialise && !pMesh->mColors[0])
{
// use the color was diffuse material color
return true;
}
return false;
}

119
code/STLLoader.h 100644
View File

@ -0,0 +1,119 @@
/*
Open Asset Import Library (ASSIMP)
----------------------------------------------------------------------
Copyright (c) 2006-2008, ASSIMP Development Team
All rights reserved.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of the ASSIMP team, nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of the ASSIMP Development Team.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
----------------------------------------------------------------------
*/
/** @file Declaration of the STL importer class. */
#ifndef AI_STLLOADER_H_INCLUDED
#define AI_STLLOADER_H_INCLUDED
#include "BaseImporter.h"
#include "../include/aiTypes.h"
namespace Assimp
{
// ---------------------------------------------------------------------------
/** Clas to load STL files
*/
class STLImporter : public BaseImporter
{
friend class Importer;
protected:
/** Constructor to be privately used by Importer */
STLImporter();
/** Destructor, private as well */
~STLImporter();
public:
// -------------------------------------------------------------------
/** Returns whether the class can handle the format of the given file.
* See BaseImporter::CanRead() for details. */
bool CanRead( const std::string& pFile, IOSystem* pIOHandler) const;
protected:
// -------------------------------------------------------------------
/** Called by Importer::GetExtensionList() for each loaded importer.
* See BaseImporter::GetExtensionList() for details
*/
void GetExtensionList(std::string& append)
{
append.append("*.stl");
}
// -------------------------------------------------------------------
/** Imports the given file into the given scene structure.
* See BaseImporter::InternReadFile() for details
*/
void InternReadFile( const std::string& pFile, aiScene* pScene,
IOSystem* pIOHandler);
// -------------------------------------------------------------------
/** Loads a binary .stl file
* @return true if the default vertex color should be used as material color
*/
bool LoadBinaryFile();
// -------------------------------------------------------------------
/** Loads a ASCII text .stl file
*/
void LoadASCIIFile();
protected:
/** Buffer to hold the loaded file */
const char* mBuffer;
/** Size of the file, in bytes */
unsigned int fileSize;
/** Output scene */
aiScene* pScene;
/** Default vertex color */
aiColor4D clrColorDefault;
};
} // end of namespace Assimp
#endif // AI_3DSIMPORTER_H_IN

View File

@ -265,7 +265,7 @@ void SplitLargeMeshesProcess_Triangle::SplitMesh(
aiBone* pc = new aiBone();
pcMesh->mBones[pcMesh->mNumBones++] = pc;
pc->mName = aiString(bone->mName);
pc->mNumWeights = avTempWeights.size();
pc->mNumWeights = (unsigned int)avTempWeights.size();
pc->mOffsetMatrix = bone->mOffsetMatrix;
// no need to reallocate the array for the last submesh.
@ -615,7 +615,7 @@ void SplitLargeMeshesProcess_Vertex::SplitMesh(
*ppCurrent++ = pcOut = new aiBone();
pcOut->mName = aiString(pcOldBone->mName);
pcOut->mOffsetMatrix = pcOldBone->mOffsetMatrix;
pcOut->mNumWeights = pcWeightList->size();
pcOut->mNumWeights = (unsigned int)pcWeightList->size();
pcOut->mWeights = new aiVertexWeight[pcOut->mNumWeights];
// copy the vertex weights

View File

@ -108,6 +108,7 @@ void ValidateDSProcess::ReportError(const char* msg,...)
throw new ImportErrorException("Idiot ... learn coding!");
}
va_end(args);
ai_assert(false);
throw new ImportErrorException("Validation failed: " + std::string(szBuffer,iLen));
}
// ------------------------------------------------------------------------------------------------
@ -357,6 +358,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
{
if (!pMesh->mBones[i])
{
delete[] afSum;
this->ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)",
i,pMesh->mNumBones);
}
@ -366,6 +368,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
{
if (pMesh->mBones[i]->mName == pMesh->mBones[a]->mName)
{
delete[] afSum;
this->ReportError("aiMesh::mBones[%i] has the same name as "
"aiMesh::mBones[%i]",i,a);
}
@ -376,9 +379,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh)
{
if (afSum[i] && (afSum[i] <= 0.995 || afSum[i] >= 1.005))
{
delete[] afSum;
this->ReportError("aiMesh::mVertices[%i]: The sum of all bone "
"weights isn't 1.0f (sum is %f)",i,afSum[i]);
this->ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]);
}
}
delete[] afSum;
@ -457,9 +458,9 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
{
aiMaterialProperty* prop = pMaterial->mProperties[i];
if (0 == ASSIMP_strincmp( prop->mKey->data, szBaseBuf, iLen ))
if (0 == ASSIMP_strincmp( prop->mKey.data, szBaseBuf, iLen ))
{
const char* sz = &prop->mKey->data[iLen];
const char* sz = &prop->mKey.data[iLen];
if (*sz)
{
++sz;
@ -488,12 +489,12 @@ void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
for (unsigned int i = 0; i < pMaterial->mNumProperties;++i)
{
aiMaterialProperty* prop = pMaterial->mProperties[i];
if (0 == ASSIMP_strincmp( prop->mKey->data, szBaseBuf, iLen ))
if (0 == ASSIMP_strincmp( prop->mKey.data, szBaseBuf, iLen ))
{
if (aiPTI_Integer != prop->mType || sizeof(int) > prop->mDataLength)
this->ReportError("Material property %s is expected to be an integer",prop->mKey);
const char* sz = &prop->mKey->data[iLen];
const char* sz = &prop->mKey.data[iLen];
if (*sz)
{
++sz;
@ -546,7 +547,8 @@ void ValidateDSProcess::Validate( const aiMaterial* pMaterial)
// check all predefined types
if (aiPTI_String == prop->mType)
{
if (prop->mDataLength < sizeof(aiString))
// FIX: strings are now stored in a less expensive way ...
if (prop->mDataLength < sizeof(size_t) + ((const aiString*)prop->mData)->length + 1)
{
this->ReportError("aiMaterial::mProperties[%i].mDataLength is "
"too small to contain a string (%i, needed: %i)",

View File

@ -12,7 +12,7 @@ void Assimp::aiAssert (bool expression, const std::string &message, unsigned int
{
if (!expression)
{
std::cerr << "File :" << file << ", line " << uiLine << " : " << message << std::endl;
std::cout << "File :" << file << ", line " << uiLine << " : " << message << std::endl;
#ifdef _WIN32
#ifndef __GNUC__

View File

@ -48,6 +48,25 @@ inline unsigned int strtol10( const char* in, const char** out=0)
return value;
}
// specal version of the function, providing higher accuracy
inline uint64_t strtol10_64( const char* in, const char** out=0)
{
uint64_t value = 0;
while ( 1 )
{
if ( *in < '0' || *in > '9' )
break;
value = ( value * 10 ) + ( *in - '0' );
++in;
}
if (out)
*out = in;
return value;
}
//! Provides a fast function for converting a string into a float,
//! about 6 times faster than atof in win32.
// If you find any bugs, please send them to me, niko (at) irrlicht3d.org.
@ -64,17 +83,24 @@ inline const char* fast_atof_move( const char* c, float& out)
}
//f = (float)strtol(c, &t, 10);
f = (float) strtol10 ( c, &c );
f = (float) strtol10_64 ( c, &c );
if (*c == '.')
{
++c;
// NOTE: The original implementation is highly unaccurate here
// The precision of a single IEEE 754 float is not high enough
// everything behind the 6th digit tends to be more inaccurate
// than it would need to be.
// Casting to double seems to solve the problem.
// strtol_64 is used to prevent integer overflow.
//float pl = (float)strtol(c, &t, 10);
float pl = (float) strtol10 ( c, &t );
double pl = (double) strtol10_64 ( c, &t );
pl *= fast_atof_table[t-c];
f += pl;
f += (float)pl;
c = t;

View File

@ -94,4 +94,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# endif
#endif
#ifdef _DEBUG
# define AI_DEBUG_INVALIDATE_PTR(ptr) ptr = 0;
#else
# define AI_DEBUG_INVALIDATE_PTR(ptr)
#endif
#endif // !! AI_DEFINES_H_INC

View File

@ -213,7 +213,7 @@ struct aiMaterialProperty
*
* Keys are case insensitive.
*/
C_STRUCT aiString* mKey;
C_STRUCT aiString mKey;
/** Size of the buffer mData is pointing to, in bytes
* This value may not be 0.

View File

@ -66,6 +66,9 @@ struct aiQuaternion
/** Construct from an axis angle pair */
aiQuaternion( aiVector3D axis, float angle);
/** Construct from a normalized quaternion stored in a vec3 */
aiQuaternion( aiVector3D normalized);
/** Returns a matrix representation of the quaternion */
aiMatrix3x3 GetMatrix() const;
@ -170,7 +173,22 @@ inline aiQuaternion::aiQuaternion( aiVector3D axis, float angle)
z = axis.z * sin_a;
w = cos_a;
}
// ---------------------------------------------------------------------------
// Construction from am existing, normalized quaternion
inline aiQuaternion::aiQuaternion( aiVector3D normalized)
{
x = normalized.x;
y = normalized.y;
z = normalized.z;
float t = 1.0f - (normalized.x * normalized.x) -
(normalized.y * normalized.y) - (normalized.z * normalized.z);
if (t < 0.0f)
w = 0.0f;
else w = sqrt (t);
}

View File

@ -0,0 +1,9 @@
solid testTriangle
facet normal 0.0 0.0 1.0
outer loop
vertex 1.0 1.0 0.0
vertex -1.0 1.0 0.0
vertex 0.0 -1.0 0.0
endloop
endfacet
endsolid

View File

@ -0,0 +1,34 @@
#include "utGenNormals.h"
CPPUNIT_TEST_SUITE_REGISTRATION (GenNormalsTest);
void GenNormalsTest :: setUp (void)
{
this->piProcess = new GenVertexNormalsProcess();
this->pcMesh = new aiMesh();
pcMesh->mNumFaces = 1;
pcMesh->mFaces = new aiFace[1];
pcMesh->mFaces[0].mIndices = new unsigned int[pcMesh->mFaces[0].mNumIndices = 3];
pcMesh->mFaces[0].mIndices[0] = 0;
pcMesh->mFaces[0].mIndices[1] = 1;
pcMesh->mFaces[0].mIndices[2] = 1;
pcMesh->mNumVertices = 3;
pcMesh->mVertices = new aiVector3D[3];
pcMesh->mVertices[0] = aiVector3D(0.0f,1.0f,6.0f);
pcMesh->mVertices[1] = aiVector3D(2.0f,3.0f,1.0f);
pcMesh->mVertices[2] = aiVector3D(3.0f,2.0f,4.0f);
}
void GenNormalsTest :: tearDown (void)
{
delete this->pcMesh;
delete this->piProcess;
}
void GenNormalsTest :: testSimpleTriangle (void)
{
this->piProcess->GenMeshVertexNormals(pcMesh);
CPPUNIT_ASSERT(0 != pcMesh->mNormals);
}

View File

@ -0,0 +1,37 @@
#ifndef TESTNORMALS_H
#define TESTNORMALS_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <aiTypes.h>
#include <aiMesh.h>
#include <aiScene.h>
#include <GenVertexNormalsProcess.h>
using namespace std;
using namespace Assimp;
class GenNormalsTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (GenNormalsTest);
CPPUNIT_TEST (testSimpleTriangle);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testSimpleTriangle (void);
private:
aiMesh* pcMesh;
GenVertexNormalsProcess* piProcess;
};
#endif

View File

@ -0,0 +1,82 @@
#include "utMaterialSystem.h"
CPPUNIT_TEST_SUITE_REGISTRATION (MaterialSystemTest);
void MaterialSystemTest :: setUp (void)
{
this->pcMat = new MaterialHelper();
}
void MaterialSystemTest :: tearDown (void)
{
delete this->pcMat;
}
void MaterialSystemTest :: testFloatProperty (void)
{
float pf = 150392.63f;
this->pcMat->AddProperty(&pf,1,"testKey1");
pf = 0.0f;
CPPUNIT_ASSERT(AI_SUCCESS == pcMat->Get("testKey1",pf));
CPPUNIT_ASSERT(pf == 150392.63f);
}
void MaterialSystemTest :: testFloatArrayProperty (void)
{
float pf[] = {0.0f,1.0f,2.0f,3.0f};
unsigned int pMax = sizeof(pf) / sizeof(float);
this->pcMat->AddProperty(&pf,pMax,"testKey2");
pf[0] = pf[1] = pf[2] = pf[3] = 12.0f;
CPPUNIT_ASSERT(AI_SUCCESS == pcMat->Get("testKey2",pf,&pMax));
CPPUNIT_ASSERT(pMax == sizeof(pf) / sizeof(float));
CPPUNIT_ASSERT(!pf[0] && 1.0f == pf[1] && 2.0f == pf[2] && 3.0f == pf[3] );
}
void MaterialSystemTest :: testIntProperty (void)
{
int pf = 15039263;
this->pcMat->AddProperty(&pf,1,"testKey3");
pf = 12;
CPPUNIT_ASSERT(AI_SUCCESS == pcMat->Get("testKey3",pf));
CPPUNIT_ASSERT(pf == 15039263);
}
void MaterialSystemTest :: testIntArrayProperty (void)
{
int pf[] = {0,1,2,3};
unsigned int pMax = sizeof(pf) / sizeof(int);
this->pcMat->AddProperty(&pf,pMax,"testKey4");
pf[0] = pf[1] = pf[2] = pf[3] = 12;
CPPUNIT_ASSERT(AI_SUCCESS == pcMat->Get("testKey4",pf,&pMax));
CPPUNIT_ASSERT(pMax == sizeof(pf) / sizeof(int));
CPPUNIT_ASSERT(!pf[0] && 1 == pf[1] && 2 == pf[2] && 3 == pf[3] );
}
void MaterialSystemTest :: testColorProperty (void)
{
aiColor4D clr;
clr.r = 2.0f;clr.g = 3.0f;clr.b = 4.0f;clr.a = 5.0f;
this->pcMat->AddProperty(&clr,1,"testKey5");
clr.b = 1.0f;
clr.a = clr.g = clr.r = 0.0f;
CPPUNIT_ASSERT(AI_SUCCESS == pcMat->Get("testKey5",clr));
CPPUNIT_ASSERT(clr.r == 2.0f && clr.g == 3.0f && clr.b == 4.0f && clr.a == 5.0f);
}
void MaterialSystemTest :: testStringProperty (void)
{
aiString s;
s.Set("Hello, this is a small test");
this->pcMat->AddProperty(&s,"testKey6");
s.Set("358358");
CPPUNIT_ASSERT(AI_SUCCESS == pcMat->Get("testKey6",s));
CPPUNIT_ASSERT(!::strcmp(s.data,"Hello, this is a small test"));
}

View File

@ -0,0 +1,45 @@
#ifndef TESTMATERIALS_H
#define TESTMATERIALS_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <aiTypes.h>
#include <aiMesh.h>
#include <aiScene.h>
#include <MaterialSystem.h>
using namespace std;
using namespace Assimp;
class MaterialSystemTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (MaterialSystemTest);
CPPUNIT_TEST (testFloatProperty);
CPPUNIT_TEST (testFloatArrayProperty);
CPPUNIT_TEST (testIntProperty);
CPPUNIT_TEST (testIntArrayProperty);
CPPUNIT_TEST (testColorProperty);
CPPUNIT_TEST (testStringProperty);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testFloatProperty (void);
void testFloatArrayProperty (void);
void testIntProperty (void);
void testIntArrayProperty (void);
void testColorProperty (void);
void testStringProperty (void);
private:
MaterialHelper* pcMat;
};
#endif

View File

@ -0,0 +1,101 @@
#include "utRemoveRedundantMaterials.h"
#include "aiPostProcess.h"
#include <math.h>
CPPUNIT_TEST_SUITE_REGISTRATION (RemoveRedundantMatsTest);
aiMaterial* getUniqueMaterial1()
{
// setup an unique name for each material - this shouldn't care
aiString mTemp;
mTemp.Set("UniqueMat1");
MaterialHelper* pcMat = new MaterialHelper();
pcMat->AddProperty(&mTemp,AI_MATKEY_NAME);
float f = 2.0f;
pcMat->AddProperty<float>(&f, 1, AI_MATKEY_BUMPSCALING);
pcMat->AddProperty<float>(&f, 1, AI_MATKEY_SHININESS_STRENGTH);
return pcMat;
}
aiMaterial* getUniqueMaterial2()
{
// setup an unique name for each material - this shouldn't care
aiString mTemp;
mTemp.Set("UniqueMat2");
MaterialHelper* pcMat = new MaterialHelper();
pcMat->AddProperty(&mTemp,AI_MATKEY_NAME);
float f = 4.0f;int i = 1;
pcMat->AddProperty<float>(&f, 1, AI_MATKEY_BUMPSCALING);
pcMat->AddProperty<int>(&i, 1, AI_MATKEY_ENABLE_WIREFRAME);
return pcMat;
}
aiMaterial* getUniqueMaterial3()
{
// setup an unique name for each material - this shouldn't care
aiString mTemp;
mTemp.Set("UniqueMat3");
MaterialHelper* pcMat = new MaterialHelper();
pcMat->AddProperty(&mTemp,AI_MATKEY_NAME);
return pcMat;
}
void RemoveRedundantMatsTest :: setUp (void)
{
// construct the process
this->piProcess = new RemoveRedundantMatsProcess();
// create a scene with 5 materials (2 is a duplicate of 0, 3 of 1)
this->pcScene1 = new aiScene();
this->pcScene1->mNumMaterials = 5;
this->pcScene1->mMaterials = new aiMaterial*[5];
this->pcScene1->mMaterials[0] = getUniqueMaterial1();
this->pcScene1->mMaterials[1] = getUniqueMaterial2();
this->pcScene1->mMaterials[4] = getUniqueMaterial3();
// setup an unique name for each material - this shouldn't care
aiString mTemp;
mTemp.length = 1;
mTemp.data[0] = 48;
mTemp.data[1] = 0;
MaterialHelper* pcMat;
this->pcScene1->mMaterials[2] = pcMat = new MaterialHelper();
MaterialHelper::CopyPropertyList(pcMat,(const MaterialHelper*)this->pcScene1->mMaterials[0]);
pcMat->AddProperty(&mTemp,AI_MATKEY_NAME);
mTemp.data[0]++;
this->pcScene1->mMaterials[3] = pcMat = new MaterialHelper();
MaterialHelper::CopyPropertyList(pcMat,(const MaterialHelper*)this->pcScene1->mMaterials[1]);
pcMat->AddProperty(&mTemp,AI_MATKEY_NAME);
mTemp.data[0]++;
}
void RemoveRedundantMatsTest :: tearDown (void)
{
delete this->piProcess;
delete this->pcScene1;
}
void RemoveRedundantMatsTest :: testRedundantMaterials (void)
{
this->piProcess->Execute(this->pcScene1);
CPPUNIT_ASSERT_EQUAL(this->pcScene1->mNumMaterials,3u);
CPPUNIT_ASSERT(0 != this->pcScene1->mMaterials &&
0 != this->pcScene1->mMaterials[0] &&
0 != this->pcScene1->mMaterials[1] &&
0 != this->pcScene1->mMaterials[2]);
aiString sName;
CPPUNIT_ASSERT(AI_SUCCESS == aiGetMaterialString(this->pcScene1->mMaterials[2],
AI_MATKEY_NAME,&sName));
CPPUNIT_ASSERT(!::strcmp( sName.data, "UniqueMat3" ));
}

View File

@ -0,0 +1,39 @@
#ifndef TESTRRM_H
#define TESTRRM_H
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>
#include <aiTypes.h>
#include <aiMesh.h>
#include <aiScene.h>
#include <RemoveRedundantMaterials.h>
#include <MaterialSystem.h>
using namespace std;
using namespace Assimp;
class RemoveRedundantMatsTest : public CPPUNIT_NS :: TestFixture
{
CPPUNIT_TEST_SUITE (RemoveRedundantMatsTest);
CPPUNIT_TEST (testRedundantMaterials);
CPPUNIT_TEST_SUITE_END ();
public:
void setUp (void);
void tearDown (void);
protected:
void testRedundantMaterials (void);
private:
RemoveRedundantMatsProcess* piProcess;
aiScene* pcScene1;
aiScene* pcScene2;
};
#endif

View File

@ -475,7 +475,7 @@ int CDisplay::AddTextureToDisplayList(unsigned int iType,
// find out whether this is the default texture or not
if (piTexture)
if (piTexture && *piTexture)
{
// {9785DA94-1D96-426b-B3CB-BADC36347F5E}
static const GUID guidPrivateData =

View File

@ -683,10 +683,6 @@
RelativePath="..\..\test\unit\utGenNormals.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utGenSmoothNormals.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utImproveCacheLocality.cpp"
>
@ -711,6 +707,10 @@
RelativePath="..\..\test\unit\utRemoveComments.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utRemoveRedundantMaterials.cpp"
>
</File>
<File
RelativePath="..\..\test\unit\utSplitLargeMeshes.cpp"
>
@ -729,14 +729,30 @@
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
>
<File
RelativePath="..\..\test\unit\utGenNormals.h"
>
</File>
<File
RelativePath="..\..\test\unit\utLimitBoneWeights.h"
>
</File>
<File
RelativePath="..\..\test\unit\utMaterialSystem.h"
>
</File>
<File
RelativePath="..\..\test\unit\utPretransformVertices.h"
>
</File>
<File
RelativePath="..\..\test\unit\utRemoveComments.h"
>
</File>
<File
RelativePath="..\..\test\unit\utRemoveRedundantMaterials.h"
>
</File>
<File
RelativePath="..\..\test\unit\utSplitLargeMeshes.h"
>

View File

@ -730,6 +730,10 @@
RelativePath="..\..\code\GenVertexNormalsProcess.h"
>
</File>
<File
RelativePath="..\..\code\Hash.h"
>
</File>
<File
RelativePath="..\..\code\ImproveCacheLocality.h"
>
@ -799,7 +803,7 @@
>
</File>
<Filter
Name="ObjLoader"
Name="Obj"
>
<File
RelativePath="..\..\code\ObjFileData.h"
@ -823,7 +827,7 @@
</File>
</Filter>
<Filter
Name="3DSLoader"
Name="3DS"
>
<File
RelativePath="..\..\code\3DSHelper.h"
@ -839,7 +843,7 @@
</File>
</Filter>
<Filter
Name="MD3Loader"
Name="MD3"
>
<File
RelativePath="..\..\code\MD3FileData.h"
@ -851,7 +855,7 @@
</File>
</Filter>
<Filter
Name="MD2Loader"
Name="MD2"
>
<File
RelativePath="..\..\code\MD2FileData.h"
@ -867,7 +871,7 @@
</File>
</Filter>
<Filter
Name="MD4Loader"
Name="MD4"
>
<File
RelativePath="..\..\code\MD4FileData.h"
@ -879,7 +883,7 @@
</File>
</Filter>
<Filter
Name="XLoader"
Name="X"
>
<File
RelativePath="..\..\code\XFileHelper.h"
@ -895,7 +899,7 @@
</File>
</Filter>
<Filter
Name="PlyLoader"
Name="Ply"
>
<File
RelativePath="..\..\code\PlyLoader.h"
@ -907,15 +911,19 @@
</File>
</Filter>
<Filter
Name="MD5Loader"
Name="MD5"
>
<File
RelativePath="..\..\code\MD5Loader.h"
>
</File>
<File
RelativePath="..\..\code\MD5Parser.h"
>
</File>
</Filter>
<Filter
Name="MDLLoader"
Name="MDL"
>
<File
RelativePath="..\..\code\HalfLifeFileData.h"
@ -935,7 +943,7 @@
</File>
</Filter>
<Filter
Name="ASELoader"
Name="ASE"
>
<File
RelativePath="..\..\code\ASELoader.h"
@ -1019,13 +1027,21 @@
</File>
</Filter>
<Filter
Name="SMDLoader"
Name="SMD"
>
<File
RelativePath="..\..\code\SMDLoader.h"
>
</File>
</Filter>
<Filter
Name="STL"
>
<File
RelativePath="..\..\code\STLLoader.h"
>
</File>
</Filter>
</Filter>
<Filter
Name="sources"
@ -1127,7 +1143,7 @@
>
</File>
<Filter
Name="ObjLoader"
Name="Obj"
>
<File
RelativePath="..\..\code\ObjFileImporter.cpp"
@ -1143,7 +1159,7 @@
</File>
</Filter>
<Filter
Name="3DSLoader"
Name="3DS"
>
<File
RelativePath="..\..\code\3DSConverter.cpp"
@ -1163,7 +1179,7 @@
</File>
</Filter>
<Filter
Name="MD3Loader"
Name="MD3"
>
<File
RelativePath="..\..\code\MD3Loader.cpp"
@ -1171,7 +1187,7 @@
</File>
</Filter>
<Filter
Name="MD2Loader"
Name="MD2"
>
<File
RelativePath="..\..\code\MD2Loader.cpp"
@ -1179,7 +1195,7 @@
</File>
</Filter>
<Filter
Name="MD4Loader"
Name="MD4"
>
<File
RelativePath="..\..\code\MD4Loader.cpp"
@ -1187,7 +1203,7 @@
</File>
</Filter>
<Filter
Name="XLoader"
Name="X"
>
<File
RelativePath="..\..\code\XFileImporter.cpp"
@ -1199,7 +1215,7 @@
</File>
</Filter>
<Filter
Name="PlyLoader"
Name="Ply"
>
<File
RelativePath="..\..\code\PlyLoader.cpp"
@ -1211,7 +1227,7 @@
</File>
</Filter>
<Filter
Name="MDLLoader.cpp"
Name="MDL"
>
<File
RelativePath="..\..\code\MDLLoader.cpp"
@ -1223,7 +1239,7 @@
</File>
</Filter>
<Filter
Name="ASELoader"
Name="ASE"
>
<File
RelativePath="..\..\code\ASELoader.cpp"
@ -1267,7 +1283,7 @@
</File>
</Filter>
<Filter
Name="HMPLoader"
Name="HMP"
>
<File
RelativePath="..\..\code\HMPLoader.cpp"
@ -1275,13 +1291,33 @@
</File>
</Filter>
<Filter
Name="SMDLoader"
Name="SMD"
>
<File
RelativePath="..\..\code\SMDLoader.cpp"
>
</File>
</Filter>
<Filter
Name="MD5"
>
<File
RelativePath="..\..\code\MD5Loader.cpp"
>
</File>
<File
RelativePath="..\..\code\MD5Parser.cpp"
>
</File>
</Filter>
<Filter
Name="STL"
>
<File
RelativePath="..\..\code\STLLoader.cpp"
>
</File>
</Filter>
</Filter>
<Filter
Name="doc"