diff --git a/code/3DSConverter.cpp b/code/3DSConverter.cpp index ed1a84680..8e53585f4 100644 --- a/code/3DSConverter.cpp +++ b/code/3DSConverter.cpp @@ -59,10 +59,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -#ifdef _MSC_VER -# define sprintf sprintf_s -#endif - // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ReplaceDefaultMaterial() { @@ -130,70 +126,66 @@ void Dot3DSImporter::ReplaceDefaultMaterial() } return; } + // ------------------------------------------------------------------------------------------------ -void Dot3DSImporter::CheckIndices(Dot3DS::Mesh* sMesh) +void Dot3DSImporter::CheckIndices(Dot3DS::Mesh& sMesh) { for (std::vector< Dot3DS::Face >::iterator - i = sMesh->mFaces.begin(); - i != sMesh->mFaces.end();++i) + i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) { // check whether all indices are in range - if ((*i).mIndices[0] >= sMesh->mPositions.size()) + for (unsigned int a = 0; a < 3;++a) { - DefaultLogger::get()->warn("Face index overflow in 3DS file (#1)"); - (*i).mIndices[0] = (uint32_t)sMesh->mPositions.size()-1; - } - if ((*i).mIndices[1] >= sMesh->mPositions.size()) - { - DefaultLogger::get()->warn("Face index overflow in 3DS file (#2)"); - (*i).mIndices[1] = (uint32_t)sMesh->mPositions.size()-1; - } - if ((*i).mIndices[2] >= sMesh->mPositions.size()) - { - DefaultLogger::get()->warn("Face index overflow in 3DS file (#3)"); - (*i).mIndices[2] = (uint32_t)sMesh->mPositions.size()-1; + if ((*i).mIndices[a] >= sMesh.mPositions.size()) + { + DefaultLogger::get()->warn("3DS: Face index overflow)"); + (*i).mIndices[a] = (uint32_t)sMesh.mPositions.size()-1; + } } } return; } + // ------------------------------------------------------------------------------------------------ -void Dot3DSImporter::MakeUnique(Dot3DS::Mesh* sMesh) +void Dot3DSImporter::MakeUnique(Dot3DS::Mesh& sMesh) { unsigned int iBase = 0; std::vector vNew; std::vector vNew2; - vNew.resize(sMesh->mFaces.size() * 3); - if (sMesh->mTexCoords.size())vNew2.resize(sMesh->mFaces.size() * 3); + vNew.resize(sMesh.mFaces.size() * 3); + if (sMesh.mTexCoords.size())vNew2.resize(sMesh.mFaces.size() * 3); - for (unsigned int i = 0; i < sMesh->mFaces.size();++i) + for (unsigned int i = 0; i < sMesh.mFaces.size();++i) { uint32_t iTemp1,iTemp2; // positions - vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].mIndices[2]]; + vNew[iBase] = sMesh.mPositions[sMesh.mFaces[i].mIndices[2]]; iTemp1 = iBase++; - vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].mIndices[1]]; + vNew[iBase] = sMesh.mPositions[sMesh.mFaces[i].mIndices[1]]; iTemp2 = iBase++; - vNew[iBase] = sMesh->mPositions[sMesh->mFaces[i].mIndices[0]]; + vNew[iBase] = sMesh.mPositions[sMesh.mFaces[i].mIndices[0]]; // texture coordinates - if (sMesh->mTexCoords.size()) + if (sMesh.mTexCoords.size()) { - vNew2[iTemp1] = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[2]]; - vNew2[iTemp2] = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[1]]; - vNew2[iBase] = sMesh->mTexCoords[sMesh->mFaces[i].mIndices[0]]; + vNew2[iTemp1] = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[2]]; + vNew2[iTemp2] = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[1]]; + vNew2[iBase] = sMesh.mTexCoords[sMesh.mFaces[i].mIndices[0]]; } - sMesh->mFaces[i].mIndices[2] = iBase++; - sMesh->mFaces[i].mIndices[0] = iTemp1; - sMesh->mFaces[i].mIndices[1] = iTemp2; + sMesh.mFaces[i].mIndices[2] = iBase++; + sMesh.mFaces[i].mIndices[0] = iTemp1; + sMesh.mFaces[i].mIndices[1] = iTemp2; } - sMesh->mPositions = vNew; - sMesh->mTexCoords = vNew2; + sMesh.mPositions = vNew; + sMesh.mTexCoords = vNew2; return; } + // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat, MaterialHelper& mat) @@ -201,14 +193,14 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat, // NOTE: Pass the background image to the viewer by bypassing the // material system. This is an evil hack, never do it again! if (0 != this->mBackgroundImage.length() && this->bHasBG) - { + { aiString tex; tex.Set( this->mBackgroundImage); mat.AddProperty( &tex, AI_MATKEY_GLOBAL_BACKGROUND_IMAGE); // be sure this is only done for the first material this->mBackgroundImage = std::string(""); - } + } // At first add the base ambient color of the // scene to the material @@ -250,7 +242,7 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat, // two sided rendering? if (oldMat.mTwoSided) { - int i = 0; + int i = 1; mat.AddProperty(&i,1,AI_MATKEY_TWOSIDED); } @@ -268,8 +260,6 @@ void Dot3DSImporter::ConvertMaterial(Dot3DS::Material& oldMat, eShading = aiShadingMode_Gouraud; break; // assume cook-torrance shading for metals. - // NOTE: I assume the real shader inside 3ds max is an anisotropic - // Phong-Blinn shader, but this is a good approximation too case Dot3DS::Dot3DSFile::Phong : eShading = aiShadingMode_Phong; break; @@ -409,20 +399,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut) a = (*i).mFaceMaterials.begin(); a != (*i).mFaceMaterials.end();++a,++iNum) { - // check range - // FIX: shouldn't be necessary anymore, has been moved to ReplaceDefaultMaterial() -#if 0 - if ((*a) >= this->mScene->mMaterials.size()) - { - DefaultLogger::get()->error("3DS face material index is out of range"); - - // use the last material instead - // TODO: assign the default material index - aiSplit[this->mScene->mMaterials.size()-1].push_back(iNum); - } - else -#endif - aiSplit[*a].push_back(iNum); + aiSplit[*a].push_back(iNum); } // now generate submeshes bool bFirst = true; @@ -506,12 +483,9 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut) pcOut->mNumMeshes = (unsigned int)avOutMeshes.size(); pcOut->mMeshes = new aiMesh*[pcOut->mNumMeshes](); for (unsigned int a = 0; a < pcOut->mNumMeshes;++a) - { pcOut->mMeshes[a] = avOutMeshes[a]; - } - if (!iFaceCnt) - throw new ImportErrorException("No faces loaded. The mesh is empty"); + if (!iFaceCnt)throw new ImportErrorException("No faces loaded. The mesh is empty"); // for each material in the scene we need to setup the UV source // set for each texture @@ -519,6 +493,7 @@ void Dot3DSImporter::ConvertMeshes(aiScene* pcOut) TextureTransform::SetupMatUVSrc( pcOut->mMaterials[a], &this->mScene->mMaterials[a] ); return; } + // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pcIn) { @@ -533,11 +508,8 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* ai_assert(NULL != pcMesh); // do case independent comparisons here, just for safety - if (pcIn->mName.length() == pcMesh->mName.length() && - !ASSIMP_stricmp(pcIn->mName.c_str(),pcMesh->mName.c_str())) - { + if (!ASSIMP_stricmp(pcIn->mName,pcMesh->mName)) iArray.push_back(a); - } } if (!iArray.empty()) { @@ -568,10 +540,11 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* pvCurrent->y -= pivot.y; pvCurrent->z -= pivot.z; *pvCurrent = mTrafo * (*pvCurrent); - std::swap( pvCurrent->y, pvCurrent->z ); + //std::swap( pvCurrent->y, pvCurrent->z ); ++pvCurrent; } } +#if 0 else { while (pvCurrent != pvEnd) @@ -580,6 +553,7 @@ void Dot3DSImporter::AddNodeToGraph(aiScene* pcSOut,aiNode* pcOut,Dot3DS::Node* ++pvCurrent; } } +#endif pcOut->mMeshes[i] = iIndex; } } @@ -658,9 +632,7 @@ void Dot3DSImporter::GenerateNodeGraph(aiScene* pcOut) // if the root node is a default node setup a name for it if (pcOut->mRootNode->mName.data[0] == '$' && pcOut->mRootNode->mName.data[1] == '$') - { pcOut->mRootNode->mName.Set(""); - } } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ConvertScene(aiScene* pcOut) diff --git a/code/3DSHelper.h b/code/3DSHelper.h index b4a190fc6..353d71e6e 100644 --- a/code/3DSHelper.h +++ b/code/3DSHelper.h @@ -38,7 +38,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ -/** @file Defines the helper data structures for importing 3DS files. +/** @file Defines helper data structures for the import of 3DS files. http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ #ifndef AI_3DSFILEHELPER_H_INC @@ -55,20 +55,18 @@ http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ #include "../include/aiMaterial.h" #include "SpatialSort.h" +#include "SmoothingGroups.h" namespace Assimp { namespace Dot3DS { #include "./Compiler/pushpack1.h" -#ifdef _MSC_VER -# define sprintf sprintf_s -#endif - // --------------------------------------------------------------------------- /** Dot3DSFile class: Helper class for loading 3ds files. Defines chunks * and data structures. */ +// --------------------------------------------------------------------------- class Dot3DSFile { public: @@ -91,32 +89,27 @@ public: typedef enum { // translated to gouraud shading with wireframe active - Wire = 0, + Wire = 0x0, // if this material is set, no vertex normals will // be calculated for the model. Face normals + gouraud - Flat = 1, + Flat = 0x1, // standard gouraud shading - Gouraud = 2, + Gouraud = 0x2, // phong shading - Phong = 3, + Phong = 0x3, // cooktorrance or anistropic phong shading ... // the exact meaning is unknown, if you know it // feel free to tell me ;-) - Metal = 4, + Metal = 0x4, // required by the ASE loader - Blinn = 5 + Blinn = 0x5 } shadetype3ds; - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - // enum for all chunks in 3ds files. Unused - // ones are commented, list is not complete since - // there are many undocumented chunks - // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ enum { @@ -316,26 +309,10 @@ public: // --------------------------------------------------------------------------- /** Helper structure representing a 3ds mesh face */ -struct Face +struct Face : public FaceWithSmoothingGroup { - Face() : iSmoothGroup(0), bFlipped(false) - { - // let the rest uninitialized for performance - this->mIndices[0] = 0; - this->mIndices[1] = 0; - this->mIndices[2] = 0; - } - - - //! Indices. .3ds is using uint16. However, after - //! an unique vrtex set has been geneerated it might - //! be an index becomes > 2^16 - uint32_t mIndices[3]; - - //! specifies to which smoothing group the face belongs to - uint32_t iSmoothGroup; - - //! Specifies that the face normal must be flipped + //! Specifies that the face normal must be flipped. + //! todo: do we really need this? bool bFlipped; }; // --------------------------------------------------------------------------- @@ -442,7 +419,7 @@ struct Material }; // --------------------------------------------------------------------------- /** Helper structure to represent a 3ds file mesh */ -struct Mesh +struct Mesh : public MeshWithSmoothingGroups { //! Default constructor Mesh() @@ -457,21 +434,12 @@ struct Mesh //! Name of the mesh std::string mName; - //! Vertex positions - std::vector mPositions; - - //! Face lists - std::vector mFaces; - //! Texture coordinates std::vector mTexCoords; //! Face materials std::vector mFaceMaterials; - //! Normal vectors - std::vector mNormals; - //! Local transformation matrix aiMatrix4x4 mMat; }; diff --git a/code/3DSLoader.cpp b/code/3DSLoader.cpp index afda4e27d..36caa4550 100644 --- a/code/3DSLoader.cpp +++ b/code/3DSLoader.cpp @@ -50,23 +50,48 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" #include "../include/assimp.hpp" // boost headers #include using namespace Assimp; + -#if (!defined ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG) -# define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG \ - "WARNING: Size of chunk data plus size of " \ - "subordinate chunks is larger than the size " \ - "specified in the top-level chunk header." +// begin a chunk: parse it, validate its length, get a pointer to its end +#define ASSIMP_3DS_BEGIN_CHUNK() \ + const Dot3DSFile::Chunk* psChunk; \ + this->ReadChunk(&psChunk); \ + const unsigned char* pcCur = this->mCurrent; \ + const unsigned char* pcCurNext = pcCur + (psChunk->Size \ + - sizeof(Dot3DSFile::Chunk)); + +// process the end of a chunk and return if the end of the file is reached +#define ASSIMP_3DS_END_CHUNK() \ + this->mCurrent = pcCurNext; \ + piRemaining -= psChunk->Size; \ + if (0 >= piRemaining)return; + + +// check whether the size of all subordinate chunks of a chunks is +// not larger than the size of the chunk itself +#ifdef _DEBUG +# define ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG \ + "Size of chunk data plus size of subordinate chunks is " \ + "larger than the size specified in the top-level chunk header." + +# define ASSIMP_3DS_VALIDATE_CHUNK_SIZE() \ + if (pcCurNext < this->mCurrent) \ + { \ + DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); \ + pcCurNext = this->mCurrent; \ + } +#else +# define ASSIMP_3DS_VALIDATE_CHUNK_SIZE() #endif // ------------------------------------------------------------------------------------------------ @@ -92,7 +117,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co return false; std::string extension = pFile.substr( pos); - // not brilliant but working ;-) + // not brillant but working ;-) if( extension == ".3ds" || extension == ".3DS" || extension == ".3Ds" || extension == ".3dS") return true; @@ -103,7 +128,7 @@ bool Dot3DSImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) co // Setup configuration properties void Dot3DSImporter::SetupProperties(const Importer* pImp) { - this->configSkipPivot = pImp->GetProperty(AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT,0) ? true : false; + this->configSkipPivot = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_3DS_IGNORE_PIVOT,0) ? true : false; } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. @@ -151,11 +176,11 @@ void Dot3DSImporter::InternReadFile( i != this->mScene->mMeshes.end();++i) { // TODO: see function body - this->CheckIndices(&(*i)); - this->MakeUnique(&(*i)); + this->CheckIndices(*i); + this->MakeUnique(*i); // first generate normals for the mesh - this->GenNormals(&(*i)); + ComputeNormalsWithSmoothingsGroups(*i); } // Apply scaling and offsets to all texture coordinates @@ -179,11 +204,7 @@ void Dot3DSImporter::InternReadFile( // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ApplyMasterScale(aiScene* pScene) { - // NOTE: Some invalid files have masterscale set to 0.0 - if (0.0f == this->mMasterScale) - { - this->mMasterScale = 1.0f; - } + if (!this->mMasterScale)this->mMasterScale = 1.0f; else this->mMasterScale = 1.0f / this->mMasterScale; // construct an uniform scaling matrix and multiply with it @@ -200,9 +221,8 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut) // read chunk if (this->mCurrent >= this->mLast) - { throw new ImportErrorException("Unexpected end of file, can't read chunk header"); - } + const uintptr_t iDiff = this->mLast - this->mCurrent; if (iDiff < sizeof(Dot3DSFile::Chunk)) { @@ -211,23 +231,15 @@ void Dot3DSImporter::ReadChunk(const Dot3DSFile::Chunk** p_ppcOut) } *p_ppcOut = (const Dot3DSFile::Chunk*) this->mCurrent; if ((**p_ppcOut).Size + this->mCurrent > this->mLast) - { throw new ImportErrorException("Unexpected end of file, can't read chunk footer"); - } + this->mCurrent += sizeof(Dot3DSFile::Chunk); return; } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseMainChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - - - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); @@ -237,27 +249,14 @@ void Dot3DSImporter::ParseMainChunk(int& piRemaining) this->ParseEditorChunk(iRemaining); break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next top-level chunk - this->mCurrent = pcCurNext; - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return this->ParseMainChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseEditorChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); @@ -291,27 +290,14 @@ void Dot3DSImporter::ParseEditorChunk(int& piRemaining) } break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next top-level chunk - this->mCurrent = pcCurNext; - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return this->ParseEditorChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseObjectChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); const unsigned char* sz = this->mCurrent; unsigned int iCnt = 0; @@ -384,17 +370,8 @@ void Dot3DSImporter::ParseObjectChunk(int& piRemaining) break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next top-level chunk - this->mCurrent = pcCurNext; - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return this->ParseObjectChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ @@ -409,11 +386,7 @@ void Dot3DSImporter::SkipChunk() // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); @@ -424,28 +397,14 @@ void Dot3DSImporter::ParseChunk(int& piRemaining) this->ParseMeshChunk(iRemaining); break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next top-level chunk - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return this->ParseChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type int iRemaining = (psChunk->Size - sizeof(Dot3DSFile::Chunk)); @@ -456,18 +415,8 @@ void Dot3DSImporter::ParseKeyframeChunk(int& piRemaining) this->ParseHierarchyChunk(iRemaining); break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next top-level chunk - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return this->ParseKeyframeChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ @@ -489,14 +438,7 @@ void Dot3DSImporter::InverseNodeSearch(Dot3DS::Node* pcNode,Dot3DS::Node* pcCurr // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - - - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type const unsigned char* sz = (unsigned char*)this->mCurrent; @@ -548,6 +490,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) // pivot = origin of rotation and scaling this->mCurrentNode->vPivot = *((const aiVector3D*)this->mCurrent); + std::swap(this->mCurrentNode->vPivot.y,this->mCurrentNode->vPivot.z); this->mCurrent += sizeof(aiVector3D); break; @@ -576,8 +519,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) uint16_t sNum = *((const uint16_t*)mCurrent); this->mCurrent += sizeof(uint16_t); - aiVectorKey v; - v.mTime = (double)sNum; + aiVectorKey v;v.mTime = (double)sNum; this->mCurrent += sizeof(uint32_t); v.mValue = *((const aiVector3D*)this->mCurrent); @@ -591,7 +533,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;} } // add the new keyframe - if (v.mTime != -10e10f)this->mCurrentNode->aPositionKeys.push_back(v); + if (v.mTime != -10e10f) + this->mCurrentNode->aPositionKeys.push_back(v); } break; @@ -618,9 +561,7 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) uint16_t sNum = *((const uint16_t*)mCurrent); this->mCurrent += sizeof(uint16_t); - aiQuatKey v; - v.mTime = (double)sNum; - + aiQuatKey v;v.mTime = (double)sNum; this->mCurrent += sizeof(uint32_t); float fRadians = *((const float*)this->mCurrent); @@ -640,7 +581,8 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) if ((*i).mTime == v.mTime){v.mTime = -10e10f;break;} } // add the new keyframe - if (v.mTime != -10e10f)this->mCurrentNode->aRotationKeys.push_back(v); + if (v.mTime != -10e10f) + this->mCurrentNode->aRotationKeys.push_back(v); } break; @@ -698,31 +640,16 @@ void Dot3DSImporter::ParseHierarchyChunk(int& piRemaining) break; #endif }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next top-level chunk - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return this->ParseHierarchyChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseFaceChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - + ASSIMP_3DS_BEGIN_CHUNK(); Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back(); - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); - // get chunk type const unsigned char* sz = this->mCurrent; uint32_t iCnt = 0,iTemp; @@ -796,31 +723,16 @@ void Dot3DSImporter::ParseFaceChunk(int& piRemaining) break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next chunk on this level - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return ParseFaceChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseMeshChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - + ASSIMP_3DS_BEGIN_CHUNK(); Dot3DS::Mesh& mMesh = this->mScene->mMeshes.back(); - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); - // get chunk type const unsigned char* sz = this->mCurrent; unsigned int iCnt = 0; @@ -837,7 +749,7 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining) { mMesh.mPositions.push_back(*((aiVector3D*)this->mCurrent)); aiVector3D& v = mMesh.mPositions.back(); - //std::swap( v.y, v.z); + std::swap( v.y, v.z); //v.y *= -1.0f; this->mCurrent += sizeof(aiVector3D); } @@ -924,28 +836,14 @@ void Dot3DSImporter::ParseMeshChunk(int& piRemaining) break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next chunk on this level - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return ParseMeshChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type const unsigned char* sz = this->mCurrent; @@ -958,9 +856,8 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) case Dot3DSFile::CHUNK_MAT_MATNAME: // string in file is zero-terminated, - // this should be no problem. However, validate whether - // it overlaps the end of the chunk, if yes we should - // truncate it. + // this should be no problem. However, validate whether it overlaps + // the end of the chunk, if yes we should truncate it. while (*sz++ != '\0') { if (sz > pcCurNext-1) @@ -1017,8 +914,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) pcf = &this->mScene->mMaterials.back().mTransparency; *pcf = this->ParsePercentageChunk(); // NOTE: transparency, not opacity - if (is_qnan(*pcf)) - *pcf = 1.0f; + if (is_qnan(*pcf))*pcf = 1.0f; else *pcf = 1.0f - *pcf * (float)0xFFFF / 100.0f; break; @@ -1037,16 +933,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) case Dot3DSFile::CHUNK_MAT_SHININESS: pcf = &this->mScene->mMaterials.back().mSpecularExponent; *pcf = this->ParsePercentageChunk(); - if (is_qnan(*pcf)) - *pcf = 0.0f; + if (is_qnan(*pcf))*pcf = 0.0f; else *pcf *= (float)0xFFFF; break; case Dot3DSFile::CHUNK_MAT_SHININESS_PERCENT: pcf = &this->mScene->mMaterials.back().mShininessStrength; *pcf = this->ParsePercentageChunk(); - if (is_qnan(*pcf)) - *pcf = 0.0f; + if (is_qnan(*pcf))*pcf = 0.0f; else *pcf *= (float)0xffff / 100.0f; break; @@ -1054,8 +948,7 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) // TODO: need to multiply with emissive base color? pcf = &this->mScene->mMaterials.back().sTexEmissive.mTextureBlend; *pcf = this->ParsePercentageChunk(); - if (is_qnan(*pcf)) - *pcf = 0.0f; + if (is_qnan(*pcf))*pcf = 0.0f; else *pcf = *pcf * (float)0xFFFF / 100.0f; break; @@ -1085,31 +978,14 @@ void Dot3DSImporter::ParseMaterialChunk(int& piRemaining) this->ParseTextureChunk(iRemaining,&this->mScene->mMaterials.back().sTexEmissive); break; }; - if (pcCurNext < this->mCurrent) - { - // place an error message. If we crash the programmer - // will be able to find it - DefaultLogger::get()->warn(ASSIMP_3DS_WARN_CHUNK_OVERFLOW_MSG); - pcCurNext = this->mCurrent; - } - // Go to the starting position of the next chunk on this level - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return ParseMaterialChunk(piRemaining); } // ------------------------------------------------------------------------------------------------ void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut) { - const Dot3DSFile::Chunk* psChunk; - - this->ReadChunk(&psChunk); - - - const unsigned char* pcCur = this->mCurrent; - const unsigned char* pcCurNext = pcCur + (psChunk->Size - - sizeof(Dot3DSFile::Chunk)); + ASSIMP_3DS_BEGIN_CHUNK(); // get chunk type const unsigned char* sz = this->mCurrent; @@ -1184,11 +1060,8 @@ void Dot3DSImporter::ParseTextureChunk(int& piRemaining,Dot3DS::Texture* pcOut) break; }; - // Go to the starting position of the next chunk on this level - this->mCurrent = pcCurNext; - - piRemaining -= psChunk->Size; - if (0 >= piRemaining)return; + ASSIMP_3DS_VALIDATE_CHUNK_SIZE(); + ASSIMP_3DS_END_CHUNK(); return ParseTextureChunk(piRemaining,pcOut); } // ------------------------------------------------------------------------------------------------ @@ -1226,20 +1099,22 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut, const Dot3DSFile::Chunk* psChunk; this->ReadChunk(&psChunk); - if (NULL == psChunk) + if (!psChunk) { *p_pcOut = clrError; return; } + const unsigned int diff = psChunk->Size - sizeof(Dot3DSFile::Chunk); + const unsigned char* pcCur = this->mCurrent; - this->mCurrent += psChunk->Size - sizeof(Dot3DSFile::Chunk); + this->mCurrent += diff; bool bGamma = false; switch(psChunk->Flag) { case Dot3DSFile::CHUNK_LINRGBF: bGamma = true; case Dot3DSFile::CHUNK_RGBF: - if (sizeof(float) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk)) + if (sizeof(float) * 3 > diff) { *p_pcOut = clrError; return; @@ -1252,7 +1127,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut, case Dot3DSFile::CHUNK_LINRGBB: bGamma = true; case Dot3DSFile::CHUNK_RGBB: - if (sizeof(char) * 3 > psChunk->Size - sizeof(Dot3DSFile::Chunk)) + if (sizeof(char) * 3 > diff) { *p_pcOut = clrError; return; @@ -1265,7 +1140,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut, // percentage chunks: accepted to be compatible with various // .3ds files with very curious content case Dot3DSFile::CHUNK_PERCENTF: - if (p_bAcceptPercent && 4 <= psChunk->Size - sizeof(Dot3DSFile::Chunk)) + if (p_bAcceptPercent && 4 <= diff) { p_pcOut->r = *((float*)pcCur); p_pcOut->g = *((float*)pcCur); @@ -1275,7 +1150,7 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut, *p_pcOut = clrError; return; case Dot3DSFile::CHUNK_PERCENTW: - if (p_bAcceptPercent && 1 <= psChunk->Size - sizeof(Dot3DSFile::Chunk)) + if (p_bAcceptPercent && 1 <= diff) { p_pcOut->r = (float)pcCur[0] / 255.0f; p_pcOut->g = (float)pcCur[0] / 255.0f; @@ -1289,9 +1164,6 @@ void Dot3DSImporter::ParseColorChunk(aiColor3D* p_pcOut, // skip unknown chunks, hope this won't cause any problems. return this->ParseColorChunk(p_pcOut,p_bAcceptPercent); }; - // assume input gamma = 1.0, output gamma = 2.2 - // Not sure whether this is correct, too tired to - // think about it ;-) if (bGamma) { p_pcOut->r = powf(p_pcOut->r, 1.0f / 2.2f); diff --git a/code/3DSLoader.h b/code/3DSLoader.h index f32f9084c..446ded6af 100644 --- a/code/3DSLoader.h +++ b/code/3DSLoader.h @@ -206,15 +206,10 @@ protected: */ void ConvertScene(aiScene* pcOut); - // ------------------------------------------------------------------- - /** generate normal vectors for a given mesh - */ - void GenNormals(Dot3DS::Mesh* sMesh); - // ------------------------------------------------------------------- /** generate unique vertices for a mesh */ - void MakeUnique(Dot3DS::Mesh* sMesh); + void MakeUnique(Dot3DS::Mesh& sMesh); // ------------------------------------------------------------------- /** Add a node to the node graph @@ -235,7 +230,7 @@ protected: // ------------------------------------------------------------------- /** Clamp all indices in the file to a valid range */ - void CheckIndices(Dot3DS::Mesh* sMesh); + void CheckIndices(Dot3DS::Mesh& sMesh); protected: diff --git a/code/ASELoader.cpp b/code/ASELoader.cpp index 6dfdeb857..c608b5030 100644 --- a/code/ASELoader.cpp +++ b/code/ASELoader.cpp @@ -43,7 +43,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // internal headers #include "ASELoader.h" -#include "3DSSpatialSort.h" #include "MaterialSystem.h" #include "StringComparison.h" #include "TextureTransform.h" @@ -142,6 +141,7 @@ void ASEImporter::InternReadFile( if ((*i).bSkip)continue; 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); @@ -1135,9 +1135,7 @@ void ASEImporter::BuildMaterialIndices() } // prepare for the next step for (unsigned int hans = 0; hans < this->mParser->m_vMaterials.size();++hans) - { TextureTransform::ApplyScaleNOffset(this->mParser->m_vMaterials[hans]); - } // now we need to iterate through all meshes, // generating correct texture coordinates and material uv indices @@ -1182,75 +1180,6 @@ void ASEImporter::GenerateNormals(ASE::Mesh& mesh) } } if (mesh.mNormals.empty()) - { - // need to calculate normals ... - // TODO: Find a way to merge this with the code in 3DSGenNormals.cpp - mesh.mNormals.resize(mesh.mPositions.size(),aiVector3D()); - for( unsigned int a = 0; a < mesh.mFaces.size(); a++) - { - const ASE::Face& face = mesh.mFaces[a]; - - // assume it is a triangle - aiVector3D* pV1 = &mesh.mPositions[face.mIndices[2]]; - aiVector3D* pV2 = &mesh.mPositions[face.mIndices[1]]; - aiVector3D* pV3 = &mesh.mPositions[face.mIndices[0]]; - - aiVector3D pDelta1 = *pV2 - *pV1; - aiVector3D pDelta2 = *pV3 - *pV1; - aiVector3D vNor = pDelta1 ^ pDelta2; - - for (unsigned int i = 0; i < 3;++i) - mesh.mNormals[face.mIndices[i]] = vNor; - } - - // calculate the position bounds so we have a reliable epsilon to - // check position differences against - // @Schrompf: This is the 7th time this snippet is repeated! - aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); - for( unsigned int a = 0; a < mesh.mPositions.size(); a++) - { - minVec.x = std::min( minVec.x, mesh.mPositions[a].x); - minVec.y = std::min( minVec.y, mesh.mPositions[a].y); - minVec.z = std::min( minVec.z, mesh.mPositions[a].z); - maxVec.x = std::max( maxVec.x, mesh.mPositions[a].x); - maxVec.y = std::max( maxVec.y, mesh.mPositions[a].y); - maxVec.z = std::max( maxVec.z, mesh.mPositions[a].z); - } - const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; - - std::vector avNormals; - avNormals.resize(mesh.mNormals.size()); - - // now generate the spatial sort tree - D3DSSpatialSorter sSort; - for( std::vector::iterator - i = mesh.mFaces.begin(); - i != mesh.mFaces.end();++i){sSort.AddFace(&(*i),mesh.mPositions);} - sSort.Prepare(); - - for( std::vector::iterator - i = mesh.mFaces.begin(); - i != mesh.mFaces.end();++i) - { - std::vector poResult; - for (unsigned int c = 0; c < 3;++c) - { - sSort.FindPositions(mesh.mPositions[(*i).mIndices[c]],(*i).iSmoothGroup, - posEpsilon,poResult); - - aiVector3D vNormals; - for (std::vector::const_iterator - a = poResult.begin(); - a != poResult.end();++a) - { - vNormals += mesh.mNormals[(*a)]; - } - vNormals.Normalize(); - avNormals[(*i).mIndices[c]] = vNormals; - poResult.clear(); - } - } - mesh.mNormals = avNormals; - } + ComputeNormalsWithSmoothingsGroups(mesh); return; } diff --git a/code/ASEParser.cpp b/code/ASEParser.cpp index 6036221b1..9f8cdf131 100644 --- a/code/ASEParser.cpp +++ b/code/ASEParser.cpp @@ -64,7 +64,7 @@ using namespace Assimp::ASE; #define BLUBB(_message_) \ {this->LogError(_message_);return;} - +// ------------------------------------------------------------------------------------------------ #define AI_ASE_HANDLE_TOP_LEVEL_SECTION(iDepth) \ else if ('{' == *this->m_szFile)iDepth++; \ else if ('}' == *this->m_szFile) \ @@ -87,6 +87,7 @@ using namespace Assimp::ASE; } else bLastWasEndLine = false; \ ++this->m_szFile; +// ------------------------------------------------------------------------------------------------ #define AI_ASE_HANDLE_SECTION(iDepth, level, msg) \ else if ('{' == *this->m_szFile)iDepth++; \ else if ('}' == *this->m_szFile) \ @@ -1723,62 +1724,29 @@ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut) ai_assert(NULL != apOut); for (unsigned int i = 0; i < 3;++i) - { - // skip spaces and tabs - if(!SkipSpaces(this->m_szFile,&this->m_szFile)) - { - // LOG - this->LogWarning("Unable to parse indexable long triple: unexpected EOL [#1]"); - ++this->iLineNumber; - apOut[0] = apOut[1] = apOut[2] = 0; - return; - } - apOut[i] = strtol10(this->m_szFile,&this->m_szFile); - } + ParseLV4MeshLong(apOut[i]); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLongTriple(unsigned int* apOut, unsigned int& rIndexOut) { ai_assert(NULL != apOut); - // skip spaces and tabs - if(!SkipSpaces(this->m_szFile,&this->m_szFile)) - { - // LOG - this->LogWarning("Unable to parse indexed long triple: unexpected EOL [#4]"); - rIndexOut = 0; - apOut[0] = apOut[1] = apOut[2] = 0; - ++this->iLineNumber; - return; - } // parse the index - rIndexOut = strtol10(this->m_szFile,&this->m_szFile); + ParseLV4MeshLong(rIndexOut); // parse the three others this->ParseLV4MeshLongTriple(apOut); - return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFloatTriple(float* apOut, unsigned int& rIndexOut) { ai_assert(NULL != apOut); - // skip spaces and tabs - if(!SkipSpaces(this->m_szFile,&this->m_szFile)) - { - // LOG - this->LogWarning("Unable to parse indexed float triple: unexpected EOL [#1]"); - rIndexOut = 0; - apOut[0] = apOut[1] = apOut[2] = 0.0f; - ++this->iLineNumber; - return; - } // parse the index - rIndexOut = strtol10(this->m_szFile,&this->m_szFile); + ParseLV4MeshLong(rIndexOut); // parse the three others this->ParseLV4MeshFloatTriple(apOut); - return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFloatTriple(float* apOut) @@ -1786,20 +1754,7 @@ void Parser::ParseLV4MeshFloatTriple(float* apOut) ai_assert(NULL != apOut); for (unsigned int i = 0; i < 3;++i) - { - // skip spaces and tabs - if(!SkipSpaces(this->m_szFile,&this->m_szFile)) - { - // LOG - this->LogWarning("Unable to parse float triple: unexpected EOL [#5]"); - apOut[0] = apOut[1] = apOut[2] = 0.0f; - ++this->iLineNumber; - return; - } - // parse the float - this->m_szFile = fast_atof_move(this->m_szFile,apOut[i]); - } - return; + ParseLV4MeshFloat(apOut[i]); } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshFloat(float& fOut) @@ -1815,9 +1770,6 @@ void Parser::ParseLV4MeshFloat(float& fOut) } // parse the first float this->m_szFile = fast_atof_move(this->m_szFile,fOut); - // go to the next valid sequence - //this->SkipToNextToken(); - return; } // ------------------------------------------------------------------------------------------------ void Parser::ParseLV4MeshLong(unsigned int& iOut) @@ -1833,7 +1785,4 @@ void Parser::ParseLV4MeshLong(unsigned int& iOut) } // parse the value iOut = strtol10(this->m_szFile,&this->m_szFile); - // go to the next valid sequence - //this->SkipToNextToken(); - return; } \ No newline at end of file diff --git a/code/ASEParser.h b/code/ASEParser.h index 221223bcb..df9326adb 100644 --- a/code/ASEParser.h +++ b/code/ASEParser.h @@ -54,7 +54,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/aiAnim.h" // for some helper routines like IsSpace() -#include "PlyParser.h" +#include "ParsingUtils.h" #include "qnan.h" // ASE is quite similar to 3ds. We can reuse some structures @@ -87,7 +87,7 @@ struct Material : public Dot3DS::Material }; // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file face */ -struct Face : public Dot3DS::Face +struct Face : public FaceWithSmoothingGroup { //! Default constructor. Initializes everything with 0 Face() @@ -224,13 +224,13 @@ struct DecompTransform // --------------------------------------------------------------------------- /** Helper structure to represent an ASE file mesh */ -struct Mesh +struct Mesh : public MeshWithSmoothingGroups { //! Constructor. Creates a default name for the mesh Mesh() : bSkip(false) { static int iCnt = 0; - char szTemp[128]; + char szTemp[128]; // should be sufficiently large ::sprintf(szTemp,"UNNAMED_%i",iCnt++); mName = szTemp; @@ -249,21 +249,12 @@ struct Mesh //! "" if there is no parent ... std::string mParent; - //! vertex positions - std::vector mPositions; - - //! List of all faces loaded - std::vector mFaces; - //! List of all texture coordinate sets std::vector amTexCoords[AI_MAX_NUMBER_OF_TEXTURECOORDS]; //! List of all vertex color sets. std::vector mVertexColors; - //! List of normal vectors - std::vector mNormals; - //! List of all bone vertices std::vector mBoneVertices; diff --git a/code/Assimp.cpp b/code/Assimp.cpp index a67c4b4ad..8c9ba964d 100644 --- a/code/Assimp.cpp +++ b/code/Assimp.cpp @@ -40,14 +40,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Implementation of the Plain-C API */ -// CRT headers -#include // public ASSIMP headers #include "../include/assimp.h" +#include "../include/aiFileIO.h" #include "../include/assimp.hpp" #include "../include/DefaultLogger.h" #include "../include/aiAssert.h" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" + +#include "GenericProperty.h" // boost headers #define AI_C_THREADSAFE @@ -67,19 +70,158 @@ static ImporterMap gActiveImports; /** Error message of the last failed import process */ static std::string gLastErrorString; +/** Configuration properties */ +static Importer::IntPropertyMap gIntProperties; +static Importer::FloatPropertyMap gFloatProperties; +static Importer::StringPropertyMap gStringProperties; + #if (defined AI_C_THREADSAFE) /** Global mutex to manage the access to the importer map */ static boost::mutex gMutex; #endif +class CIOSystemWrapper; +class CIOStreamWrapper; + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +class CIOStreamWrapper : public IOStream +{ + friend class CIOSystemWrapper; +public: + + CIOStreamWrapper(aiFile* pFile) + : mFile(pFile) + {} + + // ------------------------------------------------------------------- + size_t Read(void* pvBuffer, + size_t pSize, + size_t pCount) + { + // need to typecast here as C has no void* + return mFile->ReadProc(mFile,(char*)pvBuffer,pSize,pCount); + } + + + // ------------------------------------------------------------------- + size_t Write(const void* pvBuffer, + size_t pSize, + size_t pCount) + { + // need to typecast here as C has no void* + return mFile->WriteProc(mFile,(const char*)pvBuffer,pSize,pCount); + } + + + // ------------------------------------------------------------------- + aiReturn Seek(size_t pOffset, + aiOrigin pOrigin) + { + return mFile->SeekProc(mFile,pOffset,pOrigin); + } + + + // ------------------------------------------------------------------- + size_t Tell(void) const + { + return mFile->TellProc(mFile); + } + + + // ------------------------------------------------------------------- + size_t FileSize() const + { + return mFile->FileSizeProc(mFile); + } + +private: + aiFile* mFile; +}; + + +// ------------------------------------------------------------------------------------------------ +// Custom IOStream implementation for the C-API +class CIOSystemWrapper : public IOSystem +{ +public: + + CIOSystemWrapper(aiFileIO* pFile) + : mFileSystem(pFile) + {} + + // ------------------------------------------------------------------- + bool Exists( const std::string& pFile) const + { + CIOSystemWrapper* pip = const_cast(this); + IOStream* p = pip->Open(pFile); + if (p){pip->Close(p);return true;} + return false; + } + + // ------------------------------------------------------------------- + std::string getOsSeparator() const + { + return "/"; + } + + // ------------------------------------------------------------------- + IOStream* Open(const std::string& pFile, + const std::string& pMode = std::string("rb")) + { + aiFile* p = mFileSystem->OpenProc(mFileSystem,pFile.c_str(),pMode.c_str()); + if (!p)return NULL; + return new CIOStreamWrapper(p); + } + + // ------------------------------------------------------------------- + void Close( IOStream* pFile) + { + if (!pFile)return; + mFileSystem->CloseProc(mFileSystem,((CIOStreamWrapper*) pFile)->mFile); + delete pFile; + } + +private: + + aiFileIO* mFileSystem; +}; + +// ------------------------------------------------------------------------------------------------ +void ReportSceneNotFoundError() +{ + DefaultLogger::get()->error("Unable to find the Importer instance for this scene. " + "Are you sure it has been created by aiImportFile(ex)(...)?"); +} + // ------------------------------------------------------------------------------------------------ // Reads the given file and returns its content. const aiScene* aiImportFile( const char* pFile, unsigned int pFlags) +{ + return aiImportFileEx(pFile,pFlags,NULL); +} + +// ------------------------------------------------------------------------------------------------ +const aiScene* aiImportFileEx( const char* pFile, unsigned int pFlags, + aiFileIO* pFS) { ai_assert(NULL != pFile); // create an Importer for this file Assimp::Importer* imp = new Assimp::Importer; + + // copy the global property lists to the Importer instance + // (we are a friend of Importer) + imp->mIntProperties = gIntProperties; + imp->mFloatProperties = gFloatProperties; + imp->mStringProperties = gStringProperties; + + // setup a custom IO system if necessary + if (pFS) + { + imp->SetIOHandler( new CIOSystemWrapper (pFS) ); + } + // and have it read the file const aiScene* scene = imp->ReadFile( pFile, pFlags); @@ -118,8 +260,7 @@ void aiReleaseImport( const aiScene* pScene) // it should be there... else the user is playing fools with us if( it == gActiveImports.end()) { - DefaultLogger::get()->error("Unable to find the Importer instance for this scene. " - "Are you sure it has been created by aiImportFile(ex)(...)?"); + ReportSceneNotFoundError(); return; } @@ -196,11 +337,31 @@ void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, // it should be there... else the user is playing fools with us if( it == gActiveImports.end()) { - DefaultLogger::get()->error("Unable to find the Importer instance for this scene. " - "Are you sure it has been created by aiImportFile(ex)(...)?"); + ReportSceneNotFoundError(); return; } // get memory statistics it->second->GetMemoryRequirements(*in); } +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value) +{ + SetGenericProperty(gIntProperties,szName,value,NULL); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value) +{ + SetGenericProperty(gFloatProperties,szName,value,NULL); +} + +// ------------------------------------------------------------------------------------------------ +ASSIMP_API void aiSetImportPropertyString(const char* szName, + const C_STRUCT aiString* st) +{ + if (!st)return; + + SetGenericProperty(gStringProperties,szName, + std::string( st->data ),NULL); +} diff --git a/code/CalcTangentsProcess.cpp b/code/CalcTangentsProcess.cpp index d0cfba909..6e07a65b4 100644 --- a/code/CalcTangentsProcess.cpp +++ b/code/CalcTangentsProcess.cpp @@ -86,7 +86,7 @@ bool CalcTangentsProcess::IsActive( unsigned int pFlags) const void CalcTangentsProcess::SetupProperties(const Importer* pImp) { // get the current value of the property - this->configMaxAngle = pImp->GetProperty(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45000) / 1000.0f; + this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE,45.f); this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f); this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle); } diff --git a/code/DXFLoader.cpp b/code/DXFLoader.cpp new file mode 100644 index 000000000..11efbf9e9 --- /dev/null +++ b/code/DXFLoader.cpp @@ -0,0 +1,100 @@ +/* +--------------------------------------------------------------------------- +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 DXF importer class */ +#include "DXFLoader.h" + +// public ASSIMP headers +#include "../include/aiScene.h" +#include "../include/aiAssert.h" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" + +#include "../include/DefaultLogger.h" + +// boost headers +#include + +using namespace Assimp; + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +DXFImporter::DXFImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +DXFImporter::~DXFImporter() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the class can handle the format of the given file. +bool DXFImporter::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); + + return !(extension.length() != 4 || extension[0] != '.' || + extension[1] != 'd' && extension[1] != 'D' || + extension[2] != 'x' && extension[2] != 'X' || + extension[3] != 'f' && extension[3] != 'F'); +} +// ------------------------------------------------------------------------------------------------ +// Imports the given file into the given scene structure. +void DXFImporter::InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + boost::scoped_ptr file( pIOHandler->Open( pFile)); + + // Check whether we can read from the file + if( file.get() == NULL) + throw new ImportErrorException( "Failed to open DXF file " + pFile + ""); + + throw new ImportErrorException("DXF: not yet implemented"); +} diff --git a/code/DXFLoader.h b/code/DXFLoader.h new file mode 100644 index 000000000..8690b905b --- /dev/null +++ b/code/DXFLoader.h @@ -0,0 +1,93 @@ +/* +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 .dxf importer class. */ +#ifndef AI_DXFLOADER_H_INCLUDED +#define AI_DXFLOADER_H_INCLUDED + +#include "BaseImporter.h" +#include "../include/aiTypes.h" + +namespace Assimp { + +// --------------------------------------------------------------------------- +/** DXF importer class +*/ +class DXFImporter : public BaseImporter +{ + friend class Importer; + +protected: + /** Constructor to be privately used by Importer */ + DXFImporter(); + + /** Destructor, private as well */ + ~DXFImporter(); + +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("*.dxf"); + } + + // ------------------------------------------------------------------- + /** Imports the given file into the given scene structure. + * See BaseImporter::InternReadFile() for details + */ + void InternReadFile( const std::string& pFile, aiScene* pScene, + IOSystem* pIOHandler); + +}; + +} // end of namespace Assimp + +#endif // AI_3DSIMPORTER_H_INC \ No newline at end of file diff --git a/code/GenVertexNormalsProcess.cpp b/code/GenVertexNormalsProcess.cpp index 7d89bba7e..df854b026 100644 --- a/code/GenVertexNormalsProcess.cpp +++ b/code/GenVertexNormalsProcess.cpp @@ -76,7 +76,7 @@ bool GenVertexNormalsProcess::IsActive( unsigned int pFlags) const void GenVertexNormalsProcess::SetupProperties(const Importer* pImp) { // get the current value of the property - this->configMaxAngle = pImp->GetProperty(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,180000) / 1000.0f; + this->configMaxAngle = pImp->GetPropertyFloat(AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE,180.f); this->configMaxAngle = std::max(std::min(this->configMaxAngle,180.0f),0.0f); this->configMaxAngle = AI_DEG_TO_RAD(this->configMaxAngle); } @@ -145,8 +145,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh) SpatialSort vertexFinder( pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); std::vector verticesFound; - const float fLimit = (AI_MESH_SMOOTHING_ANGLE_NOT_SET == pMesh->mMaxSmoothingAngle - ? this->configMaxAngle : pMesh->mMaxSmoothingAngle); + const float fLimit = this->configMaxAngle; aiVector3D* pcNew = new aiVector3D[pMesh->mNumVertices]; for (unsigned int i = 0; i < pMesh->mNumVertices;++i) diff --git a/code/GenericProperty.h b/code/GenericProperty.h new file mode 100644 index 000000000..b7712a21f --- /dev/null +++ b/code/GenericProperty.h @@ -0,0 +1,88 @@ +/* +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. + +---------------------------------------------------------------------- +*/ + +#ifndef AI_GENERIC_PROPERTY_H_INCLUDED +#define AI_GENERIC_PROPERTY_H_INCLUDED + +#include "./../include/assimp.hpp" +#include "Hash.h" + +// ------------------------------------------------------------------------------------------------ +template +inline void SetGenericProperty(std::map< uint32_t, T >& list, + const char* szName, const T& value, bool* bWasExisting) +{ + ai_assert(NULL != szName); + + typedef std::map< uint32_t, T > GenericPropertyMap; + typedef std::pair< uint32_t, T > GenericPair; + + uint32_t hash = SuperFastHash(szName); + + GenericPropertyMap::iterator it = list.find(hash); + if (it == list.end()) + { + if (bWasExisting)*bWasExisting = false; + list.insert(GenericPair( hash, value )); + return; + } + (*it).second = value; + if (bWasExisting)*bWasExisting = true; +} + + +// ------------------------------------------------------------------------------------------------ +template +inline T GetGenericProperty(const std::map< uint32_t, T >& list, + const char* szName, const T& errorReturn) +{ + ai_assert(NULL != szName); + + typedef std::map< uint32_t, T > GenericPropertyMap; + typedef std::pair< uint32_t, T > GenericPair; + + uint32_t hash = SuperFastHash(szName); + + GenericPropertyMap::const_iterator it = list.find(hash); + if (it == list.end())return errorReturn; + return (*it).second; +} + +#endif // !! AI_GENERIC_PROPERTY_H_INCLUDED \ No newline at end of file diff --git a/code/Hash.h b/code/Hash.h index d7ef61a78..e6f8eab1d 100644 --- a/code/Hash.h +++ b/code/Hash.h @@ -1,3 +1,42 @@ +/* +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. + +---------------------------------------------------------------------- +*/ #ifndef AI_HASH_H_INCLUDED #define AI_HASH_H_INCLUDED @@ -19,11 +58,12 @@ #endif // ------------------------------------------------------------------------------------------------ -inline uint32_t SuperFastHash (const char * data, int len, uint32_t hash = 0) { +inline uint32_t SuperFastHash (const char * data, unsigned int len = 0, uint32_t hash = 0) { uint32_t tmp; int rem; - if (len <= 0 || data == NULL) return 0; + if (!data) return 0; + if (!len)len = (unsigned int)::strlen(data); rem = len & 3; len >>= 2; diff --git a/code/Importer.cpp b/code/Importer.cpp index ab0b8a7fb..9f2b30c30 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -57,6 +57,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "BaseProcess.h" #include "DefaultIOStream.h" #include "DefaultIOSystem.h" +#include "GenericProperty.h" // Importers #if (!defined AI_BUILD_NO_X_IMPORTER) @@ -151,6 +152,9 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) # include "RemoveRedundantMaterials.h" #endif +#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS) +# include "OptimizeGraphProcess.h" +#endif using namespace Assimp; @@ -168,6 +172,9 @@ Importer::Importer() : bExtraVerbose = false; // disable extra verbose mode by default // add an instance of each worker class here + // the order doesn't really care, however file formats that are + // used more frequently than others should be at the beginning. + #if (!defined AI_BUILD_NO_X_IMPORTER) mImporter.push_back( new XFileImporter()); #endif @@ -217,13 +224,18 @@ Importer::Importer() : #endif // add an instance of each post processing step here in the order - // of sequence it is executed + // of sequence it is executed. steps that are added here are not validated - + // as RegisterPPStep() does - all dependencies must be there. + #if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS) mPostProcessingSteps.push_back( new ValidateDSProcess()); // must be first #endif #if (!defined AI_BUILD_NO_REMOVE_REDUNDANTMATERIALS_PROCESS) mPostProcessingSteps.push_back( new RemoveRedundantMatsProcess()); #endif +#if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS) + mPostProcessingSteps.push_back( new OptimizeGraphProcess()); +#endif #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS) mPostProcessingSteps.push_back( new TriangulateProcess()); #endif @@ -379,8 +391,16 @@ bool ValidateFlags(unsigned int pFlags) if (pFlags & aiProcess_GenSmoothNormals && pFlags & aiProcess_GenNormals) { - DefaultLogger::get()->error("aiProcess_GenSmoothNormals and aiProcess_GenNormals " - "may not be specified together"); + DefaultLogger::get()->error("aiProcess_GenSmoothNormals and " + "aiProcess_GenNormals may not be specified together"); + return false; + } + + if (pFlags & aiProcess_PreTransformVertices && + pFlags & aiProcess_OptimizeGraph) + { + DefaultLogger::get()->error("aiProcess_PreTransformVertives and " + "aiProcess_OptimizeGraph may not be specified together"); return false; } @@ -488,15 +508,21 @@ const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags // ------------------------------------------------------------------------------------------------ // Helper function to check whether an extension is supported by ASSIMP bool Importer::IsExtensionSupported(const std::string& szExtension) +{ + return NULL != FindLoader(szExtension); +} + +// ------------------------------------------------------------------------------------------------ +BaseImporter* Importer::FindLoader (const std::string& szExtension) { for (std::vector::const_iterator i = this->mImporter.begin(); i != this->mImporter.end();++i) { // pass the file extension to the CanRead(..,NULL)-method - if ((*i)->CanRead(szExtension,NULL))return true; + if ((*i)->CanRead(szExtension,NULL))return *i; } - return false; + return NULL; } // ------------------------------------------------------------------------------------------------ @@ -519,49 +545,48 @@ void Importer::GetExtensionList(std::string& szOut) // ------------------------------------------------------------------------------------------------ // Set a configuration property -int Importer::SetProperty(const char* szName, int iValue) +void Importer::SetPropertyInteger(const char* szName, int iValue, + bool* bWasExisting /*= NULL*/) { - ai_assert(NULL != szName); + SetGenericProperty(mIntProperties, szName,iValue,bWasExisting); +} - // search in the list ... - for (std::vector::iterator - i = this->mIntProperties.begin(); - i != this->mIntProperties.end();++i) - { - if (0 == ::strcmp( (*i).name.c_str(), szName )) - { - int iOld = (*i).value; - (*i).value = iValue; - return iOld; - } - } - // the property is not yet in the list ... - this->mIntProperties.push_back( IntPropertyInfo() ); - IntPropertyInfo& me = this->mIntProperties.back(); - me.name = std::string(szName); - me.value = iValue; - return AI_PROPERTY_WAS_NOT_EXISTING; +// ------------------------------------------------------------------------------------------------ +void Importer::SetPropertyFloat(const char* szName, float iValue, + bool* bWasExisting /*= NULL*/) +{ + SetGenericProperty(mFloatProperties, szName,iValue,bWasExisting); +} + +// ------------------------------------------------------------------------------------------------ +void Importer::SetPropertyString(const char* szName, const std::string& value, + bool* bWasExisting /*= NULL*/) +{ + SetGenericProperty(mStringProperties, szName,value,bWasExisting); } // ------------------------------------------------------------------------------------------------ // Get a configuration property -int Importer::GetProperty(const char* szName, +int Importer::GetPropertyInteger(const char* szName, int iErrorReturn /*= 0xffffffff*/) const { - ai_assert(NULL != szName); - - // search in the list ... - for (std::vector::const_iterator - i = this->mIntProperties.begin(); - i != this->mIntProperties.end();++i) - { - if (0 == ::strcmp( (*i).name.c_str(), szName )) - { - return (*i).value; - } - } - return iErrorReturn; + return GetGenericProperty(mIntProperties,szName,iErrorReturn); } + +// ------------------------------------------------------------------------------------------------ +float Importer::GetPropertyFloat(const char* szName, + float iErrorReturn /*= 10e10*/) const +{ + return GetGenericProperty(mFloatProperties,szName,iErrorReturn); +} + +// ------------------------------------------------------------------------------------------------ +std::string Importer::GetPropertyString(const char* szName, + const std::string& iErrorReturn /*= ""*/) const +{ + return GetGenericProperty(mStringProperties,szName,iErrorReturn); +} + // ------------------------------------------------------------------------------------------------ void AddNodeWeight(unsigned int& iScene,const aiNode* pcNode) { diff --git a/code/JoinVerticesProcess.h b/code/JoinVerticesProcess.h index c4e4dde7c..7cf3e76cb 100644 --- a/code/JoinVerticesProcess.h +++ b/code/JoinVerticesProcess.h @@ -47,6 +47,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../include/aiTypes.h" struct aiMesh; +class JoinVerticesTest; namespace Assimp { @@ -62,6 +63,7 @@ namespace Assimp class ASSIMP_API JoinVerticesProcess : public BaseProcess { friend class Importer; + friend class ::JoinVerticesTest; protected: /** Constructor to be privately used by Importer */ diff --git a/code/LWOLoader.cpp b/code/LWOLoader.cpp index 51c7ca459..69be2f329 100644 --- a/code/LWOLoader.cpp +++ b/code/LWOLoader.cpp @@ -95,8 +95,8 @@ bool LWOImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const // Setup configuration properties void LWOImporter::SetupProperties(const Importer* pImp) { - this->configGradientResX = pImp->GetProperty(AI_CONFIG_IMPORT_LWO_GRADIENT_RESX,512); - this->configGradientResY = pImp->GetProperty(AI_CONFIG_IMPORT_LWO_GRADIENT_RESY,512); + this->configGradientResX = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_GRADIENT_RESX,512); + this->configGradientResY = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_LWO_GRADIENT_RESY,512); } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. @@ -174,10 +174,10 @@ void LWOImporter::InternReadFile( const std::string& pFile, std::vector apcMeshes; std::vector apcNodes; apcNodes.reserve(mLayers->size()); - apcMeshes.reserve(mLayers->size()*std::min((mSurfaces->size()/2u), 1u)); + apcMeshes.reserve(mLayers->size()*std::min(((unsigned int)mSurfaces->size()/2u), 1u)); // the RemoveRedundantMaterials step will clean this up later - pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = mSurfaces->size()]; + pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials = (unsigned int)mSurfaces->size()]; for (unsigned int mat = 0; mat < pScene->mNumMaterials;++mat) { MaterialHelper* pcMat = new MaterialHelper(); @@ -192,7 +192,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, const LWO::Layer& layer = *lit; // I don't know whether there could be dummy layers, but it would be possible - const unsigned int meshStart = apcMeshes.size(); + const unsigned int meshStart = (unsigned int)apcMeshes.size(); if (!layer.mFaces.empty() && !layer.mTempPoints.empty()) { // now sort all faces by the surfaces assigned to them @@ -207,13 +207,13 @@ void LWOImporter::InternReadFile( const std::string& pFile, if (idx >= mTags->size()) { DefaultLogger::get()->warn("LWO: Invalid face surface index"); - idx = mTags->size()-1; + idx = (unsigned int)mTags->size()-1; } if(0xffffffff == (idx = _mMapping[idx])) { if (0xffffffff == iDefaultSurface) { - iDefaultSurface = mSurfaces->size(); + iDefaultSurface = (unsigned int)mSurfaces->size(); mSurfaces->push_back(LWO::Surface()); LWO::Surface& surf = mSurfaces->back(); surf.mColor.r = surf.mColor.g = surf.mColor.b = 0.6f; @@ -240,7 +240,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, // generate the mesh aiMesh* mesh = new aiMesh(); apcMeshes.push_back(mesh); - mesh->mNumFaces = sorted.size(); + mesh->mNumFaces = (unsigned int)sorted.size(); for (SortedRep::const_iterator it = sorted.begin(), end = sorted.end(); it != end;++it) @@ -281,7 +281,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, apcNodes.push_back(pcNode); pcNode->mName.Set(layer.mName); pcNode->mParent = reinterpret_cast(layer.mParent); - pcNode->mNumMeshes = apcMeshes.size() - meshStart; + pcNode->mNumMeshes = (unsigned int)apcMeshes.size() - meshStart; pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; for (unsigned int p = 0; p < pcNode->mNumMeshes;++p) pcNode->mMeshes[p] = p + meshStart; @@ -292,7 +292,7 @@ void LWOImporter::InternReadFile( const std::string& pFile, // copy the meshes to the output structure if (apcMeshes.size()) // shouldn't occur, just to be sure we don't crash { - pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = apcMeshes.size() ]; + pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = (unsigned int)apcMeshes.size() ]; ::memcpy(pScene->mMeshes,&apcMeshes[0],pScene->mNumMeshes*sizeof(void*)); } } @@ -414,7 +414,7 @@ void LWOImporter::CopyFaceIndices(FaceList::iterator& it, if (mi > mCurLayer->mTempPoints.size()) { DefaultLogger::get()->warn("LWO: face index is out of range"); - mi = mCurLayer->mTempPoints.size()-1; + mi = (unsigned int)mCurLayer->mTempPoints.size()-1; } } } diff --git a/code/LimitBoneWeightsProcess.cpp b/code/LimitBoneWeightsProcess.cpp index df4096a31..9c50451e7 100644 --- a/code/LimitBoneWeightsProcess.cpp +++ b/code/LimitBoneWeightsProcess.cpp @@ -89,7 +89,7 @@ void LimitBoneWeightsProcess::Execute( aiScene* pScene) void LimitBoneWeightsProcess::SetupProperties(const Importer* pImp) { // get the current value of the property - this->mMaxWeights = pImp->GetProperty(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); + this->mMaxWeights = pImp->GetPropertyInteger(AI_CONFIG_PP_LBW_MAX_WEIGHTS,AI_LMW_MAX_WEIGHTS); } // ------------------------------------------------------------------------------------------------ diff --git a/code/MD2Loader.cpp b/code/MD2Loader.cpp index 0e8b11b23..42bad2349 100644 --- a/code/MD2Loader.cpp +++ b/code/MD2Loader.cpp @@ -45,14 +45,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ByteSwap.h" #include "MD2NormalTable.h" // shouldn't be included by other units -#include "../include/IOStream.h" -#include "../include/IOSystem.h" -#include "../include/aiMesh.h" +// public ASSIMP headers +#include "../include/assimp.hpp" #include "../include/aiScene.h" #include "../include/aiAssert.h" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" #include "../include/DefaultLogger.h" -#include "../include/assimp.hpp" +// boost headers #include using namespace Assimp; @@ -117,10 +118,10 @@ void MD2Importer::SetupProperties(const Importer* pImp) { // The AI_CONFIG_IMPORT_MD2_KEYFRAME option overrides the // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - if(0xffffffff == (this->configFrameID = pImp->GetProperty( + if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger( AI_CONFIG_IMPORT_MD2_KEYFRAME,0xffffffff))) { - this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); } } // ------------------------------------------------------------------------------------------------ @@ -190,255 +191,247 @@ void MD2Importer::InternReadFile( const std::string& pFile, if( fileSize < sizeof(MD2::Header)) throw new ImportErrorException( "MD2 File is too small"); - try + std::vector mBuffer2(fileSize); + file->Read(&mBuffer2[0], 1, fileSize); + this->mBuffer = &mBuffer2[0]; + + + this->m_pcHeader = (const MD2::Header*)this->mBuffer; + +#ifdef AI_BUILD_BIG_ENDIAN + + ByteSwap::Swap4(&m_pcHeader->frameSize); + ByteSwap::Swap4(&m_pcHeader->magic); + ByteSwap::Swap4(&m_pcHeader->numFrames); + ByteSwap::Swap4(&m_pcHeader->numGlCommands); + ByteSwap::Swap4(&m_pcHeader->numSkins); + ByteSwap::Swap4(&m_pcHeader->numTexCoords); + ByteSwap::Swap4(&m_pcHeader->numTriangles); + ByteSwap::Swap4(&m_pcHeader->numVertices); + ByteSwap::Swap4(&m_pcHeader->offsetEnd); + ByteSwap::Swap4(&m_pcHeader->offsetFrames); + ByteSwap::Swap4(&m_pcHeader->offsetGlCommands); + ByteSwap::Swap4(&m_pcHeader->offsetSkins); + ByteSwap::Swap4(&m_pcHeader->offsetTexCoords); + ByteSwap::Swap4(&m_pcHeader->offsetTriangles); + ByteSwap::Swap4(&m_pcHeader->skinHeight); + ByteSwap::Swap4(&m_pcHeader->skinWidth); + ByteSwap::Swap4(&m_pcHeader->version); + +#endif + + this->ValidateHeader(); + + // 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*) ((uint8_t*) + this->m_pcHeader + this->m_pcHeader->offsetFrames); + pcFrame += this->configFrameID; + + // navigate to the begin of the triangle data + MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*) + this->m_pcHeader + this->m_pcHeader->offsetTriangles); + + // navigate to the begin of the tex coords data + const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*) + this->m_pcHeader + this->m_pcHeader->offsetTexCoords); + + // navigate to the begin of the vertex data + const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices); + +#ifdef AI_BUILD_BIG_ENDIAN + for (uint32_t i = 0; i< m_pcHeader->numTriangles) { - // 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; - -#ifdef AI_BUILD_BIG_ENDIAN - - ByteSwap::Swap4(&m_pcHeader->frameSize); - ByteSwap::Swap4(&m_pcHeader->magic); - ByteSwap::Swap4(&m_pcHeader->numFrames); - ByteSwap::Swap4(&m_pcHeader->numGlCommands); - ByteSwap::Swap4(&m_pcHeader->numSkins); - ByteSwap::Swap4(&m_pcHeader->numTexCoords); - ByteSwap::Swap4(&m_pcHeader->numTriangles); - ByteSwap::Swap4(&m_pcHeader->numVertices); - ByteSwap::Swap4(&m_pcHeader->offsetEnd); - ByteSwap::Swap4(&m_pcHeader->offsetFrames); - ByteSwap::Swap4(&m_pcHeader->offsetGlCommands); - ByteSwap::Swap4(&m_pcHeader->offsetSkins); - ByteSwap::Swap4(&m_pcHeader->offsetTexCoords); - ByteSwap::Swap4(&m_pcHeader->offsetTriangles); - ByteSwap::Swap4(&m_pcHeader->skinHeight); - ByteSwap::Swap4(&m_pcHeader->skinWidth); - ByteSwap::Swap4(&m_pcHeader->version); - + for (unsigned int p = 0; p < 3;++p) + { + ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]); + ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]); + } + } + for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i) + { + ByteSwap::Swap2(& pcTexCoords[i].s); + ByteSwap::Swap2(& pcTexCoords[i].t); + } + ByteSwap::Swap4( & pcFrame->scale[0] ); + ByteSwap::Swap4( & pcFrame->scale[1] ); + ByteSwap::Swap4( & pcFrame->scale[2] ); + ByteSwap::Swap4( & pcFrame->translate[0] ); + ByteSwap::Swap4( & pcFrame->translate[1] ); + ByteSwap::Swap4( & pcFrame->translate[2] ); #endif - this->ValidateHeader(); + pcMesh->mNumFaces = this->m_pcHeader->numTriangles; + pcMesh->mFaces = new aiFace[this->m_pcHeader->numTriangles]; - // 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(); + // allocate output storage + pcMesh->mNumVertices = (unsigned int)pcMesh->mNumFaces*3; + pcMesh->mVertices = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mNormals = new aiVector3D[pcMesh->mNumVertices]; - // navigate to the begin of the frame data - const MD2::Frame* pcFrame = (const MD2::Frame*) ((uint8_t*) - this->m_pcHeader + this->m_pcHeader->offsetFrames); - pcFrame += this->configFrameID; + // 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); - // navigate to the begin of the triangle data - MD2::Triangle* pcTriangles = (MD2::Triangle*) ((uint8_t*) - this->m_pcHeader + this->m_pcHeader->offsetTriangles); + const int iMode = (int)aiShadingMode_Gouraud; + MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0]; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); - // navigate to the begin of the tex coords data - const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*) - this->m_pcHeader + this->m_pcHeader->offsetTexCoords); + aiColor3D clr; + clr.b = clr.g = clr.r = 1.0f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); - // navigate to the begin of the vertex data - const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices); + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_AMBIENT); -#ifdef AI_BUILD_BIG_ENDIAN - for (uint32_t i = 0; i< m_pcHeader->numTriangles) + if (pcSkins->name[0]) { - for (unsigned int p = 0; p < 3;++p) - { - ByteSwap::Swap2(& pcTriangles[i].textureIndices[p]); - ByteSwap::Swap2(& pcTriangles[i].vertexIndices[p]); - } - } - for (uint32_t i = 0; i < m_pcHeader->offsetTexCoords;++i) - { - ByteSwap::Swap2(& pcTexCoords[i].s); - ByteSwap::Swap2(& pcTexCoords[i].t); - } - ByteSwap::Swap4( & pcFrame->scale[0] ); - ByteSwap::Swap4( & pcFrame->scale[1] ); - ByteSwap::Swap4( & pcFrame->scale[2] ); - ByteSwap::Swap4( & pcFrame->translate[0] ); - ByteSwap::Swap4( & pcFrame->translate[1] ); - ByteSwap::Swap4( & pcFrame->translate[2] ); -#endif + aiString szString; + const size_t iLen = ::strlen(pcSkins->name); + ::memcpy(szString.data,pcSkins->name,iLen); + szString.data[iLen] = '\0'; + szString.length = iLen; - 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(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - aiColor3D clr; - clr.b = clr.g = clr.r = 1.0f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); - - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty(&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."); - } + pcHelper->AddProperty(&szString,AI_MATKEY_TEXTURE_DIFFUSE(0)); } else { - // apply a default material - const int iMode = (int)aiShadingMode_Gouraud; - MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0]; - pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); - - aiColor3D clr; - clr.b = clr.g = clr.r = 0.6f; - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); - pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); - - clr.b = clr.g = clr.r = 0.05f; - pcHelper->AddProperty(&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; - - float fDivisorU,fDivisorV; - if (this->m_pcHeader->numTexCoords) - { - // allocate storage for texture coordinates, too - pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; - pcMesh->mNumUVComponents[0] = 2; - - // 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; - } - - 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]; - - // (flip z and y component) - // FIX: no .... invert y instead - vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1]; - vec.y += pcFrame->translate[1]; - vec.y *= -1.0f; - - vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2]; - vec.z += pcFrame->translate[2]; - - // read the normal vector from the precalculated normal table - aiVector3D& vNormal = pcMesh->mNormals[iCurrent]; - LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal); - vNormal.y *= -1.0f; - - if (this->m_pcHeader->numTexCoords) - { - // 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? - } - } - // 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; + DefaultLogger::get()->warn("Texture file name has zero length. It will be skipped."); } } - catch (ImportErrorException* ex) + else { - delete[] this->mBuffer; AI_DEBUG_INVALIDATE_PTR(this->mBuffer); - throw ex; + // apply a default material + const int iMode = (int)aiShadingMode_Gouraud; + MaterialHelper* pcHelper = (MaterialHelper*)pScene->mMaterials[0]; + pcHelper->AddProperty(&iMode, 1, AI_MATKEY_SHADING_MODEL); + + aiColor3D clr; + clr.b = clr.g = clr.r = 0.6f; + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_DIFFUSE); + pcHelper->AddProperty(&clr, 1,AI_MATKEY_COLOR_SPECULAR); + + clr.b = clr.g = clr.r = 0.05f; + pcHelper->AddProperty(&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; + + float fDivisorU,fDivisorV; + if (this->m_pcHeader->numTexCoords) + { + // allocate storage for texture coordinates, too + pcMesh->mTextureCoords[0] = new aiVector3D[pcMesh->mNumVertices]; + pcMesh->mNumUVComponents[0] = 2; + + // 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; + } + + 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]; + + // (flip z and y component) + // FIX: no .... invert y instead + vec.y = (float)pcVerts[iIndex].vertex[1] * pcFrame->scale[1]; + vec.y += pcFrame->translate[1]; + vec.y *= -1.0f; + + vec.z = (float)pcVerts[iIndex].vertex[2] * pcFrame->scale[2]; + vec.z += pcFrame->translate[2]; + + // read the normal vector from the precalculated normal table + aiVector3D& vNormal = pcMesh->mNormals[iCurrent]; + LookupNormalIndex(pcVerts[iIndex].lightNormalIndex,vNormal); + vNormal.y *= -1.0f; + + if (this->m_pcHeader->numTexCoords) + { + // 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? + } + } + // 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); } diff --git a/code/MD3Loader.cpp b/code/MD3Loader.cpp index fc74f4c43..3cdb4625e 100644 --- a/code/MD3Loader.cpp +++ b/code/MD3Loader.cpp @@ -144,10 +144,10 @@ void MD3Importer::SetupProperties(const Importer* pImp) { // The AI_CONFIG_IMPORT_MD3_KEYFRAME option overrides the // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - if(0xffffffff == (this->configFrameID = pImp->GetProperty( + if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger( AI_CONFIG_IMPORT_MD3_KEYFRAME,0xffffffff))) { - this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); } } // ------------------------------------------------------------------------------------------------ diff --git a/code/MD5Loader.cpp b/code/MD5Loader.cpp index b7edd6848..53755d972 100644 --- a/code/MD5Loader.cpp +++ b/code/MD5Loader.cpp @@ -61,14 +61,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 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(...) - -#if _MSC_VER >= 1400 -# define sprintf sprintf_s -#endif - // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer MD5Importer::MD5Importer() @@ -122,9 +114,8 @@ void MD5Importer::InternReadFile( // 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; } // ------------------------------------------------------------------------------------------------ @@ -285,30 +276,48 @@ void MD5Importer::LoadMD5MeshFile () 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(); + aiNode* pcNode = pScene->mRootNode->mChildren[1] = new aiNode(); pcNode->mName.Set("MD5Anim"); pcNode->mParent = pScene->mRootNode; AttachChilds(-1,pcNode,meshParser.mJoints); + pcNode = pScene->mRootNode->mChildren[0] = new aiNode(); + pcNode->mName.Set("MD5Mesh"); + pcNode->mParent = pScene->mRootNode; + + std::vector::const_iterator end = meshParser.mMeshes.end(); + + // FIX: MD5 files exported from Blender can have empty meshes + for (std::vector::const_iterator + it = meshParser.mMeshes.begin(), + end = meshParser.mMeshes.end(); it != end;++it) + { + if (!(*it).mFaces.empty() && !(*it).mVertices.empty()) + ++pScene->mNumMaterials; + } + // generate all meshes - pScene->mNumMeshes = pScene->mNumMaterials = (unsigned int)meshParser.mMeshes.size(); + pScene->mNumMeshes = pScene->mNumMaterials; pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; pScene->mMaterials = new aiMaterial*[pScene->mNumMeshes]; - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + + // storage for node mesh indices + pcNode->mNumMeshes = pScene->mNumMeshes; + pcNode->mMeshes = new unsigned int[pcNode->mNumMeshes]; + for (unsigned int m = 0; m < pcNode->mNumMeshes;++m) + pcNode->mMeshes[m] = m; + + unsigned int n = 0; + for (std::vector::iterator + it = meshParser.mMeshes.begin(), + end = meshParser.mMeshes.end(); it != end;++it) { - aiMesh* mesh = pScene->mMeshes[i] = new aiMesh(); - MD5::MeshDesc& meshSrc = meshParser.mMeshes[i]; + MD5::MeshDesc& meshSrc = *it; + if (meshSrc.mFaces.empty() || meshSrc.mVertices.empty()) + continue; + + aiMesh* mesh = pScene->mMeshes[n] = new aiMesh(); // generate unique vertices in our internal verbose format MakeDataUnique(meshSrc); @@ -424,9 +433,9 @@ void MD5Importer::LoadMD5MeshFile () // generate a material for the mesh MaterialHelper* mat = new MaterialHelper(); - pScene->mMaterials[i] = mat; + pScene->mMaterials[n] = mat; mat->AddProperty(&meshSrc.mShader,AI_MATKEY_TEXTURE_DIFFUSE(0)); - mesh->mMaterialIndex = i; + mesh->mMaterialIndex = n++; } // delete the file again diff --git a/code/MDCLoader.cpp b/code/MDCLoader.cpp index 65c7f464d..9127bb6b4 100644 --- a/code/MDCLoader.cpp +++ b/code/MDCLoader.cpp @@ -194,10 +194,10 @@ void MDCImporter::SetupProperties(const Importer* pImp) { // The AI_CONFIG_IMPORT_MDC_KEYFRAME option overrides the // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - if(0xffffffff == (this->configFrameID = pImp->GetProperty( + if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger( AI_CONFIG_IMPORT_MDC_KEYFRAME,0xffffffff))) { - this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); } } // ------------------------------------------------------------------------------------------------ @@ -443,7 +443,7 @@ void MDCImporter::InternReadFile( pScene->mMeshes[i]->mTextureCoords[3] = NULL; // create materials - pScene->mNumMaterials = aszShaders.size(); + pScene->mNumMaterials = (unsigned int)aszShaders.size(); pScene->mMaterials = new aiMaterial*[pScene->mNumMaterials]; for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { diff --git a/code/MDLLoader.cpp b/code/MDLLoader.cpp index d68136147..b7da74f9c 100644 --- a/code/MDLLoader.cpp +++ b/code/MDLLoader.cpp @@ -51,11 +51,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // 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" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" #include "../include/assimp.hpp" // boost headers @@ -119,11 +118,12 @@ void MDLImporter::SetupProperties(const Importer* pImp) { // The AI_CONFIG_IMPORT_MDL_KEYFRAME option overrides the // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - if(0xffffffff == (this->configFrameID = pImp->GetProperty( + if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger( AI_CONFIG_IMPORT_MDL_KEYFRAME,0xffffffff))) { - this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); } + this->configPalette = pImp->GetPropertyString(AI_CONFIG_IMPORT_MDL_COLORMAP,"colormap.lmp"); } // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. diff --git a/code/MDLLoader.h b/code/MDLLoader.h index 62e3bf0b2..fa98126b5 100644 --- a/code/MDLLoader.h +++ b/code/MDLLoader.h @@ -55,8 +55,7 @@ struct aiNode; #include "MDLFileData.h" #include "HalfLifeFileData.h" -namespace Assimp -{ +namespace Assimp { class MaterialHelper; using namespace MDL; @@ -442,6 +441,9 @@ protected: /** Configuration option: frame to be loaded */ unsigned int configFrameID; + /** Configuration option: palette to be used to decode palletized images*/ + std::string configPalette; + /** Buffer to hold the loaded file */ unsigned char* mBuffer; @@ -449,16 +451,13 @@ protected: * (MDL7 doesn't need this, the format has a separate loader) */ unsigned int iGSFileVersion; - /** Output I/O handler. used to load external lmp files - */ + /** Output I/O handler. used to load external lmp files */ IOSystem* pIOHandler; - /** Output scene to be filled - */ + /** Output scene to be filled */ aiScene* pScene; - /** Size of the input file in bytes - */ + /** Size of the input file in bytes */ unsigned int iFileSize; }; diff --git a/code/MDLMaterialLoader.cpp b/code/MDLMaterialLoader.cpp index 912972ffb..7fa0a2ca6 100644 --- a/code/MDLMaterialLoader.cpp +++ b/code/MDLMaterialLoader.cpp @@ -61,7 +61,7 @@ using namespace Assimp; void MDLImporter::SearchPalette(const unsigned char** pszColorMap) { // now try to find the color map in the current directory - IOStream* pcStream = this->pIOHandler->Open("colormap.lmp","rb"); + IOStream* pcStream = this->pIOHandler->Open(configPalette,"rb"); const unsigned char* szColorMap = (const unsigned char*)::g_aclrDefaultColorMap; if(pcStream) diff --git a/code/MaterialSystem.cpp b/code/MaterialSystem.cpp index 9ddd4e880..ca04fc5ef 100644 --- a/code/MaterialSystem.cpp +++ b/code/MaterialSystem.cpp @@ -268,7 +268,7 @@ uint32_t MaterialHelper::ComputeHash() // NOTE: We need to exclude the material name from the hash 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,(unsigned int)prop->mKey.length,hash); hash = SuperFastHash(prop->mData,prop->mDataLength,hash); } } @@ -371,8 +371,8 @@ aiReturn MaterialHelper::AddProperty (const aiString* pInput, const char* pKey) { // fix ... don't keep the whole string buffer - return this->AddBinaryProperty(pInput, - pInput->length+1+ (size_t)((uint8_t*)&pInput->data - (uint8_t*)&pInput->length), + return this->AddBinaryProperty(pInput,(unsigned int)pInput->length+1+ + (unsigned int)(((uint8_t*)&pInput->data - (uint8_t*)&pInput->length)), pKey,aiPTI_String); } // ------------------------------------------------------------------------------------------------ diff --git a/code/ObjFileImporter.cpp b/code/ObjFileImporter.cpp index 4d8ade75f..fb798a144 100644 --- a/code/ObjFileImporter.cpp +++ b/code/ObjFileImporter.cpp @@ -41,13 +41,13 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ObjFileImporter.h" #include "ObjFileParser.h" #include "ObjFileData.h" -#include "../include/IOStream.h" -#include "../include/IOSystem.h" -#include "../include/aiMesh.h" +#include "MaterialSystem.h" + #include "../include/aiScene.h" #include "../include/aiAssert.h" #include "../include/DefaultLogger.h" -#include "MaterialSystem.h" +#include "../include/IOStream.h" +#include "../include/IOSystem.h" #include #include @@ -237,7 +237,7 @@ aiNode *ObjFileImporter::createNodes(const ObjFile::Model* pModel, const ObjFile if ( meshSizeDiff > 0 ) { pNode->mMeshes = new unsigned int[ meshSizeDiff ]; - pNode->mNumMeshes = meshSizeDiff; + pNode->mNumMeshes = (unsigned int)meshSizeDiff; size_t index = 0; for (size_t i = oldMeshSize; i < MeshArray.size(); i++) { diff --git a/code/ObjFileParser.cpp b/code/ObjFileParser.cpp index eca301e56..ef6e5bb11 100644 --- a/code/ObjFileParser.cpp +++ b/code/ObjFileParser.cpp @@ -306,8 +306,8 @@ void ObjFileParser::getFace() // Store the face m_pModel->m_pCurrentMesh->m_Faces.push_back( face ); - m_pModel->m_pCurrentMesh->m_uiNumIndices += face->m_pVertices->size(); - m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += face->m_pTexturCoords[0].size(); + m_pModel->m_pCurrentMesh->m_uiNumIndices += (unsigned int)face->m_pVertices->size(); + m_pModel->m_pCurrentMesh->m_uiUVCoordinates[ 0 ] += (unsigned int)face->m_pTexturCoords[0].size(); // Skip the rest of the line m_DataIt = skipLine( m_DataIt, m_DataItEnd, m_uiLine ); @@ -462,7 +462,7 @@ int ObjFileParser::getMaterialIndex( const std::string &strMaterialName ) { if ( strMaterialName == m_pModel->m_MaterialLib[ index ]) { - mat_index = index; + mat_index = (int)index; break; } } diff --git a/code/OptimizeGraphProcess.cpp b/code/OptimizeGraphProcess.cpp new file mode 100644 index 000000000..b444d7a2c --- /dev/null +++ b/code/OptimizeGraphProcess.cpp @@ -0,0 +1,909 @@ +/* +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. + +---------------------------------------------------------------------- +*/ + +/** Implementation of the OptimizeGraphProcess post-processing step*/ + +#include +#include + +#include "OptimizeGraphProcess.h" +#include "Hash.h" + +#include "../include/aiPostProcess.h" +#include "../include/aiScene.h" +#include "../include/aiAssert.h" +#include "../include/assimp.hpp" +#include "../include/DefaultLogger.h" + +using namespace Assimp; + +// MSB for type unsigned int +#define AI_OG_UINT_MSB (1u<<((sizeof(unsigned int)*8u)-1u)) +#define AI_OG_UINT_MSB_2 (AI_OG_UINT_MSB>>1) + +// check whether a node/a mesh is locked +#define AI_OG_IS_NODE_LOCKED(nd) (nd->mNumChildren & AI_OG_UINT_MSB) +#define AI_OG_IS_MESH_LOCKED(ms) (ms->mNumBones & AI_OG_UINT_MSB) + +// check whether a node has locked meshes in its list +#define AI_OG_HAS_NODE_LOCKED_MESHES(nd) (nd->mNumChildren & AI_OG_UINT_MSB_2) + +// unmask the two upper bits of an unsigned int +#define AI_OG_UNMASK(p) (p & (~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2))) + +// ------------------------------------------------------------------------------------------------ +// Constructor to be privately used by Importer +OptimizeGraphProcess::OptimizeGraphProcess() +{ + configRemoveAnimations = AI_OG_REMOVE_ANIMATIONS; + configMinNumFaces = AI_OG_MIN_NUM_FACES; + configJoinInequalTransforms = AI_OG_JOIN_INEQUAL_TRANSFORMS; +} + +// ------------------------------------------------------------------------------------------------ +// Destructor, private as well +OptimizeGraphProcess::~OptimizeGraphProcess() +{ + // nothing to do here +} + +// ------------------------------------------------------------------------------------------------ +// Returns whether the processing step is present in the given flag field. +bool OptimizeGraphProcess::IsActive( unsigned int pFlags) const +{ + return (pFlags & aiProcess_OptimizeGraph) != 0; +} + +// ------------------------------------------------------------------------------------------------ +// Setup properties of the step +void OptimizeGraphProcess::SetupProperties(const Importer* pImp) +{ + // remove animation nodes? + configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_REMOVE_ANIMATIONS, + AI_OG_REMOVE_ANIMATIONS) != 0 ? true : false; + + // join nods with inequal transformations? + configRemoveAnimations = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_JOIN_INEQUAL_TRANSFORMS, + AI_OG_JOIN_INEQUAL_TRANSFORMS) != 0 ? true : false; + + // minimum face number per node + configMinNumFaces = pImp->GetPropertyInteger(AI_CONFIG_PP_OG_MIN_NUM_FACES, + AI_OG_MIN_NUM_FACES); +} + +// ------------------------------------------------------------------------------------------------ +aiNode* OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node) +{ + ai_assert(NULL != node); + + std::vector out; + RemoveAnimationNodes(node,out); + if (out.empty()) + throw new ImportErrorException("OptimizeGraphProcess: no nodes are remaining."); + if (1 == out.size()) + return out[0]; + aiNode* p = new aiNode(); + p->mName.Set(""); + p->mNumChildren = (unsigned int)out.size(); + p->mChildren = new aiNode*[p->mNumChildren]; + ::memcpy(p->mChildren,&out[0],p->mNumChildren*sizeof(void*)); + return p; +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::RemoveAnimationNodes (aiNode* node,std::vector& out) +{ + ai_assert(NULL != node); + + // if this is an animation node: shift all children on this layer + if (!node->mNumMeshes) + { + unsigned int old = (unsigned int)out.size(); + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + RemoveAnimationNodes(node->mChildren[i],out); + } + // update the transformations of all shifted childs + std::vector::iterator it2 = out.end(),it = out.begin()+old; + for (; it != it2; ++it) + (*it)->mTransformation = node->mTransformation * (*it)->mTransformation; + + delete[] node->mChildren;node->mChildren = NULL; + delete node; + } + else + { + // *this* node remains on this layer, and the children, too + out.push_back(node); + + std::vector outNew; + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + RemoveAnimationNodes(node->mChildren[i],outNew); + } + if (outNew.size() > node->mNumChildren) + { + delete[] node->mChildren; + node->mChildren = new aiNode*[outNew.size()]; + } + node->mNumChildren = (unsigned int)outNew.size(); + ::memcpy(node->mChildren,&outNew[0],node->mNumChildren*sizeof(void*)); + } +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::FindLockedNodes(aiNode* node) +{ + ai_assert(NULL != node); + + for (unsigned int i = 0; i < pScene->mNumAnimations;++i) + { + aiAnimation* pani = pScene->mAnimations[i]; + for (unsigned int a = 0; a < pani->mNumBones;++a) + { + aiBoneAnim* pba = pani->mBones[a]; + if (pba->mBoneName == node->mName) + { + // this node is locked + node->mNumChildren |= AI_OG_UINT_MSB; + } + } + } + // call all children + for (unsigned int i = 0; i < node->mNumChildren;++i) + FindLockedNodes(node->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount) +{ + ai_assert(NULL != node && NULL != pRefCount); + for (unsigned int i = 0;i < node->mNumMeshes;++i) + { + unsigned int m = node->mMeshes[i]; + if (pRefCount[m].first) + { + // we have already one reference - lock the first node + // that had a referenced to this mesh too if it has only + // one mesh assigned. If there are multiple meshes, + // the others could still be used for optimizations. + if (pRefCount[m].second) + { + pRefCount[m].second->mNumChildren |= (pRefCount[m].second->mNumMeshes <= 1 + ? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2); + + pRefCount[m].second = NULL; + } + pScene->mMeshes[m]->mNumBones |= AI_OG_UINT_MSB; + + // lock this node + node->mNumChildren |= (node->mNumMeshes <= 1 + ? AI_OG_UINT_MSB : AI_OG_UINT_MSB_2); + } + else pRefCount[m].second = node; + ++pRefCount[m].first; + } + // call all children + for (unsigned int i = 0; i < node->mNumChildren;++i) + FindLockedMeshes(node->mChildren[i],pRefCount); +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::FindLockedMeshes(aiNode* node) +{ + ai_assert(NULL != node); + MeshRefCount* pRefCount = new MeshRefCount[pScene->mNumMeshes]; + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + pRefCount[i] = MeshRefCount(); + + // execute the algorithm + FindLockedMeshes(node,pRefCount); + + delete[] pRefCount; +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::UnlockNodes(aiNode* node) +{ + ai_assert(NULL != node); + node->mNumChildren &= ~(AI_OG_UINT_MSB|AI_OG_UINT_MSB_2); + + // call all children + for (unsigned int i = 0; i < node->mNumChildren;++i) + UnlockNodes(node->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::UnlockMeshes() +{ + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + pScene->mMeshes[i]->mNumBones &= ~AI_OG_UINT_MSB; +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::ComputeMeshHashes() +{ + mMeshHashes.resize(pScene->mNumMeshes); + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + unsigned int iRet = 0; + aiMesh* pcMesh = pScene->mMeshes[i]; + + // normals + if (pcMesh->HasNormals())iRet |= 0x1; + // tangents and bitangents + if (pcMesh->HasTangentsAndBitangents())iRet |= 0x2; + + // texture coordinates + unsigned int p = 0; + ai_assert(4 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); + while (pcMesh->HasTextureCoords(p)) + { + iRet |= (0x100 << p++); + + // NOTE: meshes with numUVComp != 3 && != 2 aren't handled correctly here + ai_assert(pcMesh->mNumUVComponents[p] == 3 || pcMesh->mNumUVComponents[p] == 2); + if (3 == pcMesh->mNumUVComponents[p]) + iRet |= (0x1000 << p++); + } + // vertex colors + p = 0; + ai_assert(4 >= AI_MAX_NUMBER_OF_COLOR_SETS); + while (pcMesh->HasVertexColors(p))iRet |= (0x10000 << p++); + mMeshHashes[i] = iRet; + + // material index -store it in the upper 1 1/2 bytes, so + // are able to encode 2^12 material indices. + + iRet |= (pcMesh->mMaterialIndex << 20u); + } +} + +// ------------------------------------------------------------------------------------------------ +inline unsigned int OptimizeGraphProcess::BinarySearch(NodeIndexList& sortedArray, + unsigned int min, unsigned int& index, unsigned int iStart) +{ + unsigned int first = iStart,last = (unsigned int)sortedArray.size()-1; + while (first <= last) + { + unsigned int mid = (first + last) / 2; + unsigned int id = sortedArray[mid].second; + + if (min > id) + first = mid + 1; + else if (min <= id) + { + last = mid - 1; + if (!mid || min > sortedArray[last].second) + { + index = sortedArray[last].first; + return mid; + } + } + } + return (unsigned int)sortedArray.size(); +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::BuildUniqueBoneList( + std::vector::const_iterator it, + std::vector::const_iterator end, + std::list& asBones) +{ + + unsigned int iOffset = 0; + for (; it != end;++it) + { + for (unsigned int l = 0; l < (*it)->mNumBones;++l) + { + aiBone* p = (*it)->mBones[l]; + uint32_t itml = SuperFastHash(p->mName.data,(unsigned int)p->mName.length); + + std::list::iterator it2 = asBones.begin(); + std::list::iterator end2 = asBones.end(); + + for (;it2 != end2;++it2) + { + if ((*it2).first == itml) + { + (*it2).pSrcBones.push_back(BoneSrcIndex(p,iOffset)); + break; + } + } + if (end2 == it2) + { + // need to begin a new bone entry + asBones.push_back(BoneWithHash()); + BoneWithHash& btz = asBones.back(); + + // setup members + btz.first = itml; + btz.second = &p->mName; + btz.pSrcBones.push_back(BoneSrcIndex(p,iOffset)); + } + } + iOffset += (*it)->mNumVertices; + } +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::JoinBones( + std::vector::const_iterator it, + std::vector::const_iterator end, + aiMesh* out) +{ + ai_assert(NULL != out); + + // find we need to build an unique list of all bones. + // we work with hashes to make the comparisons MUCH faster, + // at least if we have many bones. + std::list asBones; + BuildUniqueBoneList(it,end,asBones); + + // now create the output bones + out->mBones = new aiBone*[asBones.size()]; + + for (std::list::const_iterator it = asBones.begin(), + end = asBones.end(); it != end;++it) + { + aiBone* pc = out->mBones[out->mNumBones++] = new aiBone(); + pc->mName = aiString( *((*it).second )); + + // get an itrator to the end of the list + std::vector< BoneSrcIndex >::const_iterator wend = (*it).pSrcBones.end(); + + // loop through all bones to be joined for this bone + for (std::vector< BoneSrcIndex >::const_iterator + wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) + { + pc->mNumWeights += (*wmit).first->mNumWeights; + + // NOTE: different offset matrices for bones with equal names + // are - at the moment - not handled correctly. + if (wmit != (*it).pSrcBones.begin() && + pc->mOffsetMatrix != (*wmit).first->mOffsetMatrix) + { + DefaultLogger::get()->warn("Bones with equal names but different " + "offset matrices can't be joined at the moment. If this causes " + "problems, deactivate the OptimizeGraph-Step"); + + continue; + } + pc->mOffsetMatrix = (*wmit).first->mOffsetMatrix; + } + // allocate the vertex weight array + aiVertexWeight* avw = pc->mWeights = new aiVertexWeight[pc->mNumWeights]; + + // and copy the final weights - adjust the vertex IDs by the + // face index offset of the coresponding mesh. + for (std::vector< BoneSrcIndex >::const_iterator + wmit = (*it).pSrcBones.begin(); wmit != wend; ++wmit) + { + aiBone* pip = (*wmit).first; + for (unsigned int mp = 0; mp < pip->mNumWeights;++mp) + { + aiVertexWeight& vf = aiVertexWeight(pip->mWeights[mp]); + vf.mVertexId += (*wmit).second; + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::JoinMeshes(std::vector& meshList, + aiMesh*& out, unsigned int max) +{ + ai_assert(NULL != out && 0 != max); + + out->mMaterialIndex = meshList[0]->mMaterialIndex; + + // allocate the output mesh + out = new aiMesh(); + std::vector::const_iterator end = meshList.begin()+max; + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + out->mNumVertices += (*it)->mNumVertices; + out->mNumFaces += (*it)->mNumFaces; + out->mNumBones += AI_OG_UNMASK((*it)->mNumBones); + } + + if (out->mNumVertices) // just for safety + { + aiVector3D* pv2; + + // copy vertex positions + if (meshList[0]->HasPositions()) + { + pv2 = out->mVertices = new aiVector3D[out->mNumVertices]; + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + ::memcpy(pv2,(*it)->mVertices,(*it)->mNumVertices*sizeof(aiVector3D)); + pv2 += (*it)->mNumVertices; + } + } + // copy normals + if (meshList[0]->HasNormals()) + { + pv2 = out->mNormals = new aiVector3D[out->mNumVertices]; + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + ::memcpy(pv2,(*it)->mNormals,(*it)->mNumVertices*sizeof(aiVector3D)); + pv2 += (*it)->mNumVertices; + } + } + // copy tangents and bitangents + if (meshList[0]->HasTangentsAndBitangents()) + { + pv2 = out->mTangents = new aiVector3D[out->mNumVertices]; + aiVector3D* pv2b = out->mBitangents = new aiVector3D[out->mNumVertices]; + + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + ::memcpy(pv2, (*it)->mTangents, (*it)->mNumVertices*sizeof(aiVector3D)); + ::memcpy(pv2b,(*it)->mBitangents,(*it)->mNumVertices*sizeof(aiVector3D)); + pv2 += (*it)->mNumVertices; + pv2b += (*it)->mNumVertices; + } + } + // copy texture coordinates + unsigned int n = 0; + while (meshList[0]->HasTextureCoords(n)) + { + out->mNumUVComponents[n] = meshList[0]->mNumUVComponents[n]; + + pv2 = out->mTextureCoords[n] = new aiVector3D[out->mNumVertices]; + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + ::memcpy(pv2,(*it)->mTextureCoords[n],(*it)->mNumVertices*sizeof(aiVector3D)); + pv2 += (*it)->mNumVertices; + } + ++n; + } + // copy vertex colors + n = 0; + while (meshList[0]->HasVertexColors(n)) + { + aiColor4D* pv2 = out->mColors[n] = new aiColor4D[out->mNumVertices]; + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + ::memcpy(pv2,(*it)->mColors[n],(*it)->mNumVertices*sizeof(aiColor4D)); + pv2 += (*it)->mNumVertices; + } + ++n; + } + } + + if (out->mNumFaces) // just for safety + { + // copy faces + out->mFaces = new aiFace[out->mNumFaces]; + aiFace* pf2 = out->mFaces; + + unsigned int ofs = 0; + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + { + for (unsigned int m = 0; m < (*it)->mNumFaces;++m,++pf2) + { + aiFace& face = (*it)->mFaces[m]; + pf2->mNumIndices = face.mNumIndices; + pf2->mIndices = face.mIndices; + + if (ofs) + { + // add the offset to the vertex + for (unsigned int q = 0; q < face.mNumIndices; ++q) + face.mIndices[q] += ofs; + } + ofs += (*it)->mNumVertices; + face.mIndices = NULL; + } + } + } + + // bones - as this is quite lengthy, I moved the code to a separate function + if (out->mNumBones)JoinBones(meshList.begin(),end,out); + + // delete all source meshes + for (std::vector::const_iterator it = meshList.begin(); it != end;++it) + delete *it; +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::ApplyNodeMeshesOptimization(aiNode* pNode) +{ + ai_assert(NULL != pNode); + + // find all meshes which are compatible and could therefore be joined. + // we can't join meshes that are locked + std::vector apcMeshes(pNode->mNumMeshes); + unsigned int iNumMeshes; + + for (unsigned int m = 0, ttt = 0; m < pNode->mNumMeshes;++m) + { + iNumMeshes = 0; + + unsigned int nm = pNode->mMeshes[m]; + if (0xffffffff == nm || AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nm]))continue; + + for (unsigned int q = m+1; q < pNode->mNumMeshes;++q) + { + register unsigned int nq = pNode->mMeshes[q]; + + // skip locked meshes + if (AI_OG_IS_MESH_LOCKED(pScene->mMeshes[nq]))continue; + + // compare the mesh hashes + if (mMeshHashes[nm] == mMeshHashes[nq]) + { + apcMeshes[iNumMeshes++] = pScene->mMeshes[nq]; + pNode->mMeshes[q] = 0xffffffff; + } + } + aiMesh* out; + if (iNumMeshes > 0) + { + apcMeshes[iNumMeshes++] = pScene->mMeshes[nm]; + JoinMeshes(apcMeshes,out,iNumMeshes); + } + else out = pScene->mMeshes[nm]; + + pNode->mMeshes[ttt++] = (unsigned int)mOutputMeshes.size(); + mOutputMeshes.push_back(out); + } +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::TransformMeshes(aiNode* quak,aiNode* pNode) +{ + for (unsigned int ä = 0; ä < quak->mNumMeshes;++ä) + { + aiMesh* mariusIsHot = pScene->mMeshes[quak->mMeshes[ä]]; + aiMatrix4x4 mMatTransform = pNode->mTransformation; + + // transformation: first back to the parent's local space, + // later into the local space of the destination child node + mMatTransform.Inverse(); + mMatTransform = quak->mTransformation * mMatTransform; + + // transform all vertices + for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo) + mariusIsHot->mVertices[oo] = mMatTransform * mariusIsHot->mVertices[oo]; + + // transform all normal vectors + if (mariusIsHot->HasNormals()) + { + mMatTransform.Inverse().Transpose(); + for (unsigned int oo =0; oo < mariusIsHot->mNumVertices;++oo) + mariusIsHot->mNormals[oo] = mMatTransform * mariusIsHot->mNormals[oo]; + } + } +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::ApplyOptimizations(aiNode* node) +{ + ai_assert(NULL != node); + + unsigned int iJoinedIndex = 0; + + // first: node index; second: number of faces in node + NodeIndexList aiBelowTreshold; + aiBelowTreshold.reserve(node->mNumChildren); + + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + aiNode* pChild = node->mChildren[i]; + if (AI_OG_IS_NODE_LOCKED(pChild) || !pChild->mNumMeshes)continue; + + // find out how many faces this node is referencing + unsigned int iFaceCnt = 0; + for (unsigned int a = 0; a < pChild->mNumMeshes;++a) + iFaceCnt += pScene->mMeshes[pChild->mMeshes[a]]->mNumFaces; + + // are we below the treshold? + if (iFaceCnt < configMinNumFaces) + { + aiBelowTreshold.push_back(NodeIndexEntry()); + NodeIndexEntry& p = aiBelowTreshold.back(); + p.first = i; + p.second = iFaceCnt; + p.pNode = pChild; + } + } + + if (!aiBelowTreshold.empty()) + { + // some typedefs for the data structures we'll need + typedef std::pair JoinListEntry; + std::vector aiJoinList(aiBelowTreshold.size()); + std::vector aiTempList(aiBelowTreshold.size()); + + unsigned int iNumJoins, iNumTemp; + + // sort the list by size + std::sort(aiBelowTreshold.begin(),aiBelowTreshold.end()); + + unsigned int iStart = 0; + for (NodeIndexList::const_iterator it = aiBelowTreshold.begin(),end = aiBelowTreshold.end(); + it != end; /*++it */++iStart) + { + aiNode* pNode = node->mChildren[(*it).first]; + + // get the hash of the mesh + const unsigned int iMeshVFormat = mMeshHashes[pNode->mMeshes[0]]; + + // we search for a node with more faces than this ... find + // the one that fits best and continue until we've reached + // treshold size. + int iDiff = configMinNumFaces-(*it).second; + for (;;) + { + // do a binary search and start the iteration there + unsigned int index; + unsigned int start = BinarySearch(aiBelowTreshold,iDiff,index,iStart); + + if (index == (*it).first)start++; + + if (start >= aiBelowTreshold.size()) + { + // there is no node with enough faces. take the first + start = 0; + } + + // todo: implement algorithm to find the best possible combination ... + iNumTemp = 0; + + while( start < aiBelowTreshold.size()) + { + // check whether the node has akready been processed before + const NodeIndexEntry& entry = aiBelowTreshold[start]; + if (!entry.pNode)continue; + + const aiNode* pip = node->mChildren[entry.first]; + if (configJoinInequalTransforms ) + { + // we need to check whether this node has locked meshes + // in this case we can't add it here - the meshes will + // be transformed from one to another coordinate space + + if (!AI_OG_HAS_NODE_LOCKED_MESHES(pip) || pip->mTransformation == pNode->mTransformation) + aiTempList[iNumTemp++] = start; + } + else if (node->mChildren[entry.first]->mTransformation == pNode->mTransformation) + { + aiTempList[iNumTemp++] = start; + break; + } + ++start; + } + + if (iNumTemp) + { + // search for a node which has a mesh with + // - the same material index + // - the same vertex layout + unsigned int d = iNumJoins = 0; + for (unsigned int m = 0; m < iNumTemp;++m) + { + register unsigned int mn = aiTempList[m]; + aiNode* pip = aiBelowTreshold[mn].pNode; + + for (unsigned int tt = 0; tt < pip->mNumMeshes;++tt) + { + register unsigned int mm = pip->mMeshes[tt]; + + if (mMeshHashes [ mm ] == iMeshVFormat) + { + d = mn; + goto break_out; + } + } + } +break_out: + aiJoinList[iNumJoins++] = JoinListEntry( aiBelowTreshold[d].first, d ); + iDiff -= aiBelowTreshold[d].second; + } + // did we reach the target treshold? + if (iDiff <= 0)break; + } + + // did we found any nodes to be joined with *this* one? + if (iNumJoins) + { + unsigned int iNumTotalChilds = pNode->mNumChildren; + unsigned int iNumTotalMeshes = pNode->mNumMeshes; + std::vector::const_iterator wend = aiJoinList.begin()+iNumJoins; + + // get output array bounds + for (std::vector::const_iterator wit = aiJoinList.begin(); + wit != wend;++wit ) + { + aiNode*& quak = node->mChildren[(*wit).first]; + iNumTotalChilds += AI_OG_UNMASK( quak->mNumChildren ); + iNumTotalMeshes += quak->mNumMeshes; + } + + // build the output child list + if (iNumTotalChilds != pNode->mNumChildren) + { + aiNode** ppc = pNode->mChildren; + delete[] pNode->mChildren; + pNode->mChildren = new aiNode*[iNumTotalChilds]; + ::memcpy(pNode->mChildren,ppc, sizeof(void*)* AI_OG_UNMASK( pNode->mNumChildren )); + + for (std::vector::const_iterator wit = aiJoinList.begin(); + wit != wend;++wit ) + { + aiNode*& quak = node->mChildren[(*wit).first]; + ::memcpy(pNode->mChildren+pNode->mNumChildren, + quak->mChildren, sizeof(void*)*quak->mNumChildren); + + pNode->mNumChildren += AI_OG_UNMASK( quak->mNumChildren ); + } + } + + // build the output mesh list + unsigned int* ppc = pNode->mMeshes; + delete[] pNode->mMeshes; + pNode->mMeshes = new unsigned int[iNumTotalMeshes]; + ::memcpy(pNode->mMeshes,ppc, sizeof(void*)*pNode->mNumMeshes); + + for (std::vector::const_iterator wit = aiJoinList.begin(); + wit != wend;++wit ) + { + aiNode*& quak = node->mChildren[(*wit).first]; + ::memcpy(pNode->mMeshes+pNode->mNumMeshes, + quak->mMeshes, sizeof(unsigned int)*quak->mNumMeshes); + + // if the node has a transformation matrix that is not equal to ours, + // we'll need to transform all vertices of the mesh into our + // local coordinate space. + if (configJoinInequalTransforms && quak->mTransformation != pNode->mTransformation) + TransformMeshes(quak,pNode); + + pNode->mNumMeshes += quak->mNumMeshes; + + // remove the joined nodes from all lists. + aiBelowTreshold[(*wit).second].pNode = NULL; + if ((*wit).second == iStart+1)++iStart; + } + + // now generate an output name for the joined nodes + if (1 == iNumTotalChilds) + { + pNode->mName.length = ::sprintf( pNode->mName.data, "", + iJoinedIndex++,iNumJoins+1); + } + } + + // now optimize the meshes in this node + ApplyNodeMeshesOptimization(pNode); + + // note - this has been optimized away. The search in the binary + // list starts with iStart, which is incremented each iteration + ++it; // = aiBelowTreshold.erase(it); + } + } + + // call all children recursively + for (unsigned int i = 0; i < node->mNumChildren;++i) + ApplyOptimizations(node->mChildren[i]); +} + +// ------------------------------------------------------------------------------------------------ +void OptimizeGraphProcess::BuildOutputMeshList() +{ + // all meshes should have been deleted before if they are + // not contained in the new mesh list + + if (pScene->mNumMeshes < mOutputMeshes.size()) + { + delete[] pScene->mMeshes; + pScene->mMeshes = new aiMesh*[mOutputMeshes.size()]; + } + pScene->mNumMeshes = (unsigned int)mOutputMeshes.size(); + ::memcpy(pScene->mMeshes,&mOutputMeshes[0],pScene->mNumMeshes*sizeof(void*)); +} + +// ------------------------------------------------------------------------------------------------ +// Executes the post processing step on the given imported data. +void OptimizeGraphProcess::Execute( aiScene* pScene) +{ + this->pScene = pScene; + /* + + a) the term "mesh node" stands for a node with numMeshes > 0 + b) the term "animation node" stands for a node with numMeshes == 0, + regardless whether the node is referenced by animation channels. + + Algorithm: + + 1. Compute hashes for all meshes that we're able to check whether + two meshes are compatible. + 2. Remove animation nodes if we have been configured to do so + 3. Find out which nodes may not be moved, so to speak are "locked" - a + locked node will never be joined with neighbors. + - A node lock is indicated by a set MSB in the aiNode::mNumChildren member + 4. Find out which meshes are locked - they are referenced by + more than one node. They will never be joined. Mark all + nodes referencing such a mesh as "locked", too. + - A mesh lock is indicated by a set MSB in the aiMesh::mNumBones member + 5. For each unlocked node count the face numbers of all assigned meshes + - if it is below the pre-defined treshold add the node to a list. + For each node in the list - try to find enough joinable nodes to + have enough faces all together. + Two nodes are joined if: + - none of them is locked + - (optional) their world matrices are identical + - nodes whose meshes share the same material indices are prefered + Two meshes in one node are joined if: + - their material indices are identical + - none of them is locked + - they share the same vertex format + 6. Build the final mesh list + 7. For all meshes and all nodes - remove locks. + + */ + + throw new ImportErrorException("OG step is still undeer development and not yet finished"); + + // STEP 1 + ComputeMeshHashes(); + + // STEP 2 + if (configRemoveAnimations) + pScene->mRootNode = RemoveAnimationNodes(pScene->mRootNode); + + // STEP 3 + else FindLockedNodes(pScene->mRootNode); + + // STEP 4 + FindLockedMeshes(pScene->mRootNode); + + // STEP 5 + ApplyOptimizations(pScene->mRootNode); + + // STEP 6 + BuildOutputMeshList(); + + // STEP 7 + UnlockNodes(pScene->mRootNode); + UnlockMeshes(); +} diff --git a/code/OptimizeGraphProcess.h b/code/OptimizeGraphProcess.h new file mode 100644 index 000000000..0bc15ff74 --- /dev/null +++ b/code/OptimizeGraphProcess.h @@ -0,0 +1,340 @@ +/* +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. + +---------------------------------------------------------------------- +*/ + +/** Defines a post processing step to refactor the output node graph to + be more compact */ +#ifndef AI_OPTIMIZEGRAPHPROCESS_H_INC +#define AI_OPTIMIZEGRAPHPROCESS_H_INC + +#include "BaseProcess.h" + +struct aiMesh; +struct aiNode; +struct aiBone; +class OptimizeGraphProcessTest; + +namespace Assimp { + +// NOTE: If you change these limits, don't forget to change the +// corresponding values in all Assimp ports + +// ********************************************************** +// Java: ConfigProperty.java, +// ConfigProperty.OG_MAX_HIERARCHY_DEPTH +// ConfigProperty.OG_MIN_NUM_FACES +// ConfigProperty.JOIN_INEQUAL_TRANSFORMS +// ********************************************************** + +#if (!defined AI_OG_MAX_DEPTH) +# define AI_OG_MAX_DEPTH 0x4 +#endif // !! AI_LMW_MAX_WEIGHTS + +#if (!defined AI_OG_MIN_NUM_FACES) +# define AI_OG_MIN_NUM_FACES 0xffffffff +#endif // !! AI_LMW_MAX_WEIGHTS + +#if (!defined AI_OG_REMOVE_ANIMATIONS) +# define AI_OG_REMOVE_ANIMATIONS false +#endif // !! AI_LMW_MAX_WEIGHTS + +#if (!defined AI_OG_JOIN_INEQUAL_TRANSFORMS) +# define AI_OG_JOIN_INEQUAL_TRANSFORMS false +#endif // !! AI_LMW_MAX_WEIGHTS + + +// --------------------------------------------------------------------------- +struct NodeIndexEntry : public std::pair +{ + // binary operator < for use with std::sort + bool operator< (const NodeIndexEntry& other) + { + return second < other.second; + } + + // pointer to the original node + aiNode* pNode; +}; +typedef std::vector NodeIndexList; +typedef std::pair BoneSrcIndex; + +// --------------------------------------------------------------------------- +struct BoneWithHash : public std::pair +{ + std::vector pSrcBones; +}; + + +// --------------------------------------------------------------------------- +/** This post processing step reformats the output node graph to be more + * compact. There are several options, e.g. maximum hierachy depth or + * minimum mesh size. Some files store every face as a new mesh, so + * some Assimp steps (such as FixInfacingNormals or SmoothNormals) don't + * work properly on these meshes. This step joins such small meshes and + * nodes. Animations are kept during the step. + * @note Use the PretransformVertices step to remove the node graph + * completely (and all animations, too). +*/ +class ASSIMP_API OptimizeGraphProcess : public BaseProcess +{ + friend class Importer; + friend class ::OptimizeGraphProcessTest; + +protected: + /** Constructor to be privately used by Importer */ + OptimizeGraphProcess(); + + /** Destructor, private as well */ + ~OptimizeGraphProcess(); + + + typedef std::pair< unsigned int, aiNode* > MeshRefCount; + typedef unsigned int MeshHash; + +public: + + // ------------------------------------------------------------------- + /** Returns whether the processing step is present in the given flag. + * @param pFlags The processing flags the importer was called with. + * A bitwise combination of #aiPostProcessSteps. + * @return true if the process is present in this flag fields, + * false if not. + */ + bool IsActive( unsigned int pFlags) const; + + // ------------------------------------------------------------------- + /** Called prior to ExecuteOnScene(). + * The function is a request to the process to update its configuration + * basing on the Importer's configuration property list. + */ + void SetupProperties(const Importer* pImp); + + + // set the configMinNumfaces property + inline void SetMinNumFaces(unsigned int n) + { + configMinNumFaces = n; + } + + // set the configRemoveAnimations property + inline void SetRemoveAnimations(bool b) + { + configRemoveAnimations = b; + } + + +protected: + + // ------------------------------------------------------------------- + /** Executes the post processing step on the given imported data. + * At the moment a process is not supposed to fail. + * @param pScene The imported data to work at. + */ + void Execute( aiScene* pScene); + + + // ------------------------------------------------------------------- + /** Removes animation nodes from the tree. + * @param node Current node + * @param out Receives a list of replacement nodes for *this* node - + * if *this* node should be kept, it must be added to the list. + */ + void RemoveAnimationNodes (aiNode* node,std::vector& out); + + + // ------------------------------------------------------------------- + /** Entry point to the RemoveAnimationNodes algorithm. + * @param node Root node to start with + * @return New root node + */ + aiNode* RemoveAnimationNodes (aiNode* node); + + + // ------------------------------------------------------------------- + /** Finds and marks all locked nodes in the tree. + * A node is locked if it is referenced by animations. + * @param node ROot node to start with + * @note A locked node has the MSB set in its mNumChildren member + */ + void FindLockedNodes(aiNode* node); + + + // ------------------------------------------------------------------- + /** Searches for locked meshes. A mesh is locked if it is referenced + * by more than one node in the hierarchy. + * @param node Root node to start with + */ + void FindLockedMeshes(aiNode* node); + void FindLockedMeshes(aiNode* node, MeshRefCount* pRefCount); + + + // ------------------------------------------------------------------- + /** Unlocks all nodes in the output tree. + * @param node Root node to start with + */ + void UnlockNodes(aiNode* node); + + + // ------------------------------------------------------------------- + /** Unlocks all meshes in the output tree. + */ + void UnlockMeshes(); + + + // ------------------------------------------------------------------- + /** Apply the final optimization algorithm to the tree. + * See the Execute-method for a detailled description of the algorithm. + * @param node Root node to start with + */ + void ApplyOptimizations(aiNode* node); + + + // ------------------------------------------------------------------- + /** Binary search for the first element that is >= min. + * @param sortedArray Input array + * @param min Treshold + */ + unsigned int BinarySearch(NodeIndexList& sortedArray, + unsigned int min, unsigned int& index, unsigned int iStart); + + + // ------------------------------------------------------------------- + /** Compute stable hashes for all meshes and fill mMeshHashes + * with the results. + */ + void ComputeMeshHashes(); + + + // ------------------------------------------------------------------- + /** Optimizes the meshes in a single node aftr the joining process. + * @param pNode Node to be optimized. Childs noded won't be processed + * automatically. + */ + void ApplyNodeMeshesOptimization(aiNode* pNode); + + + // ------------------------------------------------------------------- + /** Join meshes. + * The output meshes are deleted afterwards. + * @param meshList List of meshes to be joined + * @param out Receives a pointer to the output mesh. + */ + void JoinMeshes(std::vector& meshList,aiMesh*& out, + unsigned int max); + + + // ------------------------------------------------------------------- + /** Join bones from a collection of meshes. + * + * @param it First mesh to be processed + * @param end Last mesh to be processed + * @param out Valid output mesh to receive the output bone list. + */ + void JoinBones(std::vector::const_iterator it, + std::vector::const_iterator end, + aiMesh* out); + + + // ------------------------------------------------------------------- + /** Build a list of unique bones from a collection of meshes. + * + * @param it First mesh to be processed + * @param end Last mesh to be processed + * @param asBones Receives a list of unique bones + */ + void BuildUniqueBoneList(std::vector::const_iterator it, + std::vector::const_iterator end, + std::list& asBones); + + // ------------------------------------------------------------------- + /** Build the output mesh list. + */ + void BuildOutputMeshList(); + + + // ------------------------------------------------------------------- + /** Transform meshes from one coordinate space into another. + * @param quak Input space. All meshes referenced by this node - + * assuming none of them is locked - are transformed in the + * local coordinate space of pNode + * @param pNode Destination coordinate space + */ + void TransformMeshes(aiNode* quak,aiNode* pNode); + +private: + + /** Configuration option: specifies the minimum number of faces + a node should have. The steps tries to join meshes with less + vertices that are on the same hierarchy level. + If this value is set to a very large value (e.g. 0xffffffff) + all meshes on the same hierarchy level are joined - if they + aren't animation nodes and if they have the same world matrices + */ + unsigned int configMinNumFaces; + + + /** Configuration option: specifies whether animations are removed + from the node graph. If animations aren't needed by the caller, + this allows for further optimization. + */ + bool configRemoveAnimations; + + /** Configuration option: specifies whether nodes with inequal + world matrices are joined if they are on the same hierarchy + level and if it seems to make sense. + */ + bool configJoinInequalTransforms; + + /** Working data */ + aiScene* pScene; + + /** List of hash identifiers for all meshes. + The hashes are build from both the meshes vertex format + and the material indices. Bones are not taken into account. + */ + std::vector mMeshHashes; + + /** List of output meshes. + */ + std::vector mOutputMeshes; +}; + +} // end of namespace Assimp + +#endif // AI_LIMITBONEWEIGHTSPROCESS_H_INC diff --git a/code/PlyLoader.cpp b/code/PlyLoader.cpp index 8d333c07e..c7f2d8556 100644 --- a/code/PlyLoader.cpp +++ b/code/PlyLoader.cpp @@ -40,16 +40,21 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Implementation of the PLY importer class */ + +// internal headers #include "PlyLoader.h" #include "MaterialSystem.h" #include "StringComparison.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 headeers #include using namespace Assimp; @@ -95,24 +100,19 @@ void PLYImporter::InternReadFile( // Check whether we can read from the file if( file.get() == NULL) - { throw new ImportErrorException( "Failed to open PLY file " + pFile + "."); - } // check whether the ply file is large enough to contain // at least the file header size_t fileSize = file->FileSize(); if( fileSize < 10) - { throw new ImportErrorException( "PLY File is too small."); - } // allocate storage and copy the contents of the file to a memory buffer // (terminate it with zero) - // FIX: Allocate an extra buffer of 12.5% to be sure we won't crash - // if an overrun occurs. - this->mBuffer = new unsigned char[fileSize+1 + (fileSize>>3)]; - file->Read( (void*)mBuffer, 1, fileSize); + std::vector mBuffer2(fileSize+1); + file->Read( &mBuffer2[0], 1, fileSize); + this->mBuffer = &mBuffer2[0]; this->mBuffer[fileSize] = '\0'; // the beginning of the file must be PLY @@ -120,8 +120,6 @@ void PLYImporter::InternReadFile( this->mBuffer[1] != 'L' && this->mBuffer[1] != 'l' || this->mBuffer[2] != 'Y' && this->mBuffer[2] != 'y') { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); throw new ImportErrorException( "Invalid .ply file: Magic number \'ply\' is no there"); } char* szMe = (char*)&this->mBuffer[3]; @@ -137,11 +135,7 @@ void PLYImporter::InternReadFile( szMe += 6; SkipLine(szMe,(const char**)&szMe); if(!PLY::DOM::ParseInstance(szMe,&sPlyDom)) - { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#1)"); - } } else if (0 == ASSIMP_strincmp(szMe,"binary_",7)) { @@ -159,18 +153,9 @@ void PLYImporter::InternReadFile( // skip the line, parse the rest of the header and build the DOM SkipLine(szMe,(const char**)&szMe); if(!PLY::DOM::ParseInstanceBinary(szMe,&sPlyDom,bIsBE)) - { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); throw new ImportErrorException( "Invalid .ply file: Unable to build DOM (#2)"); - } - } - else - { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); - throw new ImportErrorException( "Invalid .ply file: Unknown file format"); } + else throw new ImportErrorException( "Invalid .ply file: Unknown file format"); } else { @@ -185,12 +170,8 @@ void PLYImporter::InternReadFile( this->LoadVertices(&avPositions,false); if (avPositions.empty()) - { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); throw new ImportErrorException( "Invalid .ply file: No vertices found. " - "Unable to interpret the data format of the PLY file"); - } + "Unable to parse the data format of the PLY file."); // now load a list of normals. std::vector avNormals; @@ -206,10 +187,8 @@ void PLYImporter::InternReadFile( { if (avPositions.size() < 3) { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); - throw new ImportErrorException( "Invalid .ply file: Not enough vertices to build " - "a face list. "); + throw new ImportErrorException( "Invalid .ply file: Not enough " + "vertices to build a face list. "); } unsigned int iNum = (unsigned int)avPositions.size() / 3; @@ -244,11 +223,7 @@ void PLYImporter::InternReadFile( &avColors,&avTexCoords,&avMaterials,&avMeshes); if (avMeshes.empty()) - { - delete[] this->mBuffer; - AI_DEBUG_INVALIDATE_PTR(this->mBuffer); throw new ImportErrorException( "Invalid .ply file: Unable to extract mesh data "); - } // now generate the output scene object. Fill the material list pScene->mNumMaterials = (unsigned int)avMaterials.size(); @@ -269,12 +244,6 @@ void PLYImporter::InternReadFile( for (unsigned int i = 0; i < pScene->mRootNode->mNumMeshes;++i) pScene->mRootNode->mMeshes[i] = i; - - // delete the file buffer - delete[] this->mBuffer;AI_DEBUG_INVALIDATE_PTR(this->mBuffer); - - // DOM is lying on the stack, will be deconstructed automatically - return; } // ------------------------------------------------------------------------------------------------ void PLYImporter::ConvertMeshes(std::vector* avFaces, @@ -819,9 +788,8 @@ void PLYImporter::LoadFaces(std::vector* pvOut) if (3 > iNum) { - // We must filter out all degenerates. Leave a message - // in the log ... - // LOG + // We must filter out all degenerates. + DefaultLogger::get()->warn("PLY: Found degenerated triangle"); continue; } diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 48a77ca23..e0cfdb9e9 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -52,17 +52,17 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; +// ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer PretransformVertices::PretransformVertices() - { - } - +{ +} +// ------------------------------------------------------------------------------------------------ // Destructor, private as well PretransformVertices::~PretransformVertices() - { +{ // nothing to do here - } - +} // ------------------------------------------------------------------------------------------------ // Returns whether the processing step is present in the given flag field. bool PretransformVertices::IsActive( unsigned int pFlags) const diff --git a/code/3DSSpatialSort.cpp b/code/SGSpatialSort.cpp similarity index 64% rename from code/3DSSpatialSort.cpp rename to code/SGSpatialSort.cpp index e80702e9b..83135df2e 100644 --- a/code/3DSSpatialSort.cpp +++ b/code/SGSpatialSort.cpp @@ -12,18 +12,18 @@ 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. +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. +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. +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 @@ -40,66 +40,53 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Implementation of the helper class to quickly find - vertices close to a given position. Special implementation for - the 3ds loader handling smooth groups correctly */ +vertices close to a given position. Special implementation for +the 3ds loader handling smooth groups correctly */ #include -#include "3DSSpatialSort.h" +#include "SGSpatialSort.h" #include "../include/aiAssert.h" using namespace Assimp; -using namespace Assimp::Dot3DS; // ------------------------------------------------------------------------------------------------ -D3DSSpatialSorter::D3DSSpatialSorter() - { +SGSpatialSort::SGSpatialSort() +{ // define the reference plane. We choose some arbitrary vector away from all basic axises // in the hope that no model spreads all its vertices along this plane. mPlaneNormal.Set( 0.8523f, 0.34321f, 0.5736f); mPlaneNormal.Normalize(); - } -// ------------------------------------------------------------------------------------------------ -// Destructor -D3DSSpatialSorter::~D3DSSpatialSorter() - { - // nothing to do here, everything destructs automatically - } -// ------------------------------------------------------------------------------------------------ -void D3DSSpatialSorter::AddFace(const Dot3DS::Face* pcFace, - const std::vector& vPositions) -{ - ai_assert(NULL != pcFace); - - // store position by index and distance - float distance = vPositions[pcFace->mIndices[0]] * mPlaneNormal; - mPositions.push_back( Entry( pcFace->mIndices[0], vPositions[pcFace->mIndices[0]], - distance, pcFace->iSmoothGroup)); - - // triangle vertex 2 - distance = vPositions[pcFace->mIndices[1]] * mPlaneNormal; - mPositions.push_back( Entry( pcFace->mIndices[1], vPositions[pcFace->mIndices[1]], - distance, pcFace->iSmoothGroup)); - - // triangle vertex 3 - distance = vPositions[pcFace->mIndices[2]] * mPlaneNormal; - mPositions.push_back( Entry( pcFace->mIndices[2], vPositions[pcFace->mIndices[2]], - distance, pcFace->iSmoothGroup)); } // ------------------------------------------------------------------------------------------------ -void D3DSSpatialSorter::Prepare() +// Destructor +SGSpatialSort::~SGSpatialSort() +{ + // nothing to do here, everything destructs automatically +} +// ------------------------------------------------------------------------------------------------ +void SGSpatialSort::Add(const aiVector3D& vPosition, unsigned int index, + unsigned int smoothingGroup) +{ + // store position by index and distance + float distance = vPosition * mPlaneNormal; + mPositions.push_back( Entry( index, vPosition, + distance, smoothingGroup)); +} +// ------------------------------------------------------------------------------------------------ +void SGSpatialSort::Prepare() { // now sort the array ascending by distance. std::sort( this->mPositions.begin(), this->mPositions.end()); } // ------------------------------------------------------------------------------------------------ // Returns an iterator for all positions close to the given position. -void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, - uint32_t pSG, - float pRadius, - std::vector& poResults) const - { +void SGSpatialSort::FindPositions( const aiVector3D& pPosition, + uint32_t pSG, + float pRadius, + std::vector& poResults) const +{ float dist = pPosition * mPlaneNormal; float minDist = dist - pRadius, maxDist = dist + pRadius; @@ -119,14 +106,14 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, unsigned int index = (unsigned int)mPositions.size() / 2; unsigned int binaryStepSize = (unsigned int)mPositions.size() / 4; while( binaryStepSize > 1) - { + { if( mPositions[index].mDistance < minDist) index += binaryStepSize; else index -= binaryStepSize; binaryStepSize /= 2; - } + } // depending on the direction of the last step we need to single step a bit back or forth // to find the actual beginning element of the range @@ -140,33 +127,16 @@ void D3DSSpatialSorter::FindPositions( const aiVector3D& pPosition, float squareEpsilon = pRadius * pRadius; std::vector::const_iterator it = mPositions.begin() + index; - if (0 == pSG) + while( it->mDistance < maxDist) + { + if((it->mPosition - pPosition).SquareLength() < squareEpsilon && + (it->mSmoothGroups & pSG || 0 == it->mSmoothGroups || 0 == pSG)) { - while( it->mDistance < maxDist) - { - if((it->mPosition - pPosition).SquareLength() < squareEpsilon) - { - poResults.push_back( it->mIndex); - } - ++it; - if( it == mPositions.end()) - break; - } + poResults.push_back( it->mIndex); } - else - { - while( it->mDistance < maxDist) - { - if((it->mPosition - pPosition).SquareLength() < squareEpsilon && - (it->mSmoothGroups & pSG || 0 == it->mSmoothGroups)) - { - poResults.push_back( it->mIndex); - } - ++it; - if( it == mPositions.end()) - break; - } - } - return; + ++it; + if( it == mPositions.end()) + break; } +} diff --git a/code/3DSSpatialSort.h b/code/SGSpatialSort.h similarity index 87% rename from code/3DSSpatialSort.h rename to code/SGSpatialSort.h index a8e9a44d1..be05abc96 100644 --- a/code/3DSSpatialSort.h +++ b/code/SGSpatialSort.h @@ -44,40 +44,34 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #define AI_D3DSSPATIALSORT_H_INC #include -#include "../include/aiVector3D.h" - - -#if (!defined AI_BUILD_NO_ASE_IMPORTER) -# include "3DSHelper.h" -#endif +#include "../include/aiTypes.h" namespace Assimp { -using namespace Dot3DS; - // ------------------------------------------------------------------------------------------------ /** Specialized version of SpatialSort to support smoothing groups * This is used in the .3ds loader */ -class D3DSSpatialSorter +class SGSpatialSort { public: - D3DSSpatialSorter(); + SGSpatialSort(); // ------------------------------------------------------------------- /** Construction from a given face array, handling smoothing groups properly */ - D3DSSpatialSorter(const std::vector& vPositions); + SGSpatialSort(const std::vector& vPositions); // ------------------------------------------------------------------- - /** Add a face to the spatial sorter - * @param pcFace Face to be added - * @param vPositions Input position list + /** Add a vertex to the spatial sort + * @param vPosition Vertex position to be added + * @param index Index of the vrtex + * @param smoothingGroup SmoothingGroup for this vertex */ - void AddFace(const Dot3DS::Face* pcFace, - const std::vector& vPositions); + void Add(const aiVector3D& vPosition, unsigned int index, + unsigned int smoothingGroup); // ------------------------------------------------------------------- /** Prepare the spatial sorter for use @@ -85,7 +79,7 @@ public: void Prepare(); /** Destructor */ - ~D3DSSpatialSorter(); + ~SGSpatialSort(); // ------------------------------------------------------------------- /** Returns an iterator for all positions close to the given position. diff --git a/code/SMDLoader.cpp b/code/SMDLoader.cpp index 21b004477..5571230c3 100644 --- a/code/SMDLoader.cpp +++ b/code/SMDLoader.cpp @@ -110,10 +110,10 @@ void SMDImporter::SetupProperties(const Importer* pImp) { // The AI_CONFIG_IMPORT_SMD_KEYFRAME option overrides the // AI_CONFIG_IMPORT_GLOBAL_KEYFRAME option. - if(0xffffffff == (this->configFrameID = pImp->GetProperty( + if(0xffffffff == (this->configFrameID = pImp->GetPropertyInteger( AI_CONFIG_IMPORT_SMD_KEYFRAME,0xffffffff))) { - this->configFrameID = pImp->GetProperty(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); + this->configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); } } // ------------------------------------------------------------------------------------------------ diff --git a/code/SmoothingGroups.h b/code/SmoothingGroups.h new file mode 100644 index 000000000..ea8c92890 --- /dev/null +++ b/code/SmoothingGroups.h @@ -0,0 +1,103 @@ +/* +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 Defines the helper data structures for importing 3DS files. +http://www.jalix.org/ressources/graphics/3DS/_unofficials/3ds-unofficial.txt */ + +#ifndef AI_SMOOTHINGGROUPS_H_INC +#define AI_SMOOTHINGGROUPS_H_INC + +// --------------------------------------------------------------------------- +/** Helper structure representing a face with smoothing groups assigned */ +struct FaceWithSmoothingGroup +{ + FaceWithSmoothingGroup() : iSmoothGroup(0) + { + // let the rest uninitialized for performance - in release builds. + // in debug builds set all indices to a common magic value +#ifdef _DEBUG + this->mIndices[0] = 0xffffffff; + this->mIndices[1] = 0xffffffff; + this->mIndices[2] = 0xffffffff; +#endif + } + + + //! Indices. .3ds is using uint16. However, after + //! an unique vrtex set has been geneerated it might + //! be an index becomes > 2^16 + uint32_t mIndices[3]; + + //! specifies to which smoothing group the face belongs to + uint32_t iSmoothGroup; +}; + +// --------------------------------------------------------------------------- +/** Helper structure representing a mesh whose faces have smoothing + groups assigned. This allows us to reuse the code for normal computations + from smoothings groups for several loaders (3DS, ASE). All of them + use face structures which inherit from #FaceWithSmoothingGroup, + but as they add extra members and need to be copied by value we + need to use a template here. + */ +template +struct MeshWithSmoothingGroups +{ + //! Vertex positions + std::vector mPositions; + + //! Face lists + std::vector mFaces; + + //! List of normal vectors + std::vector mNormals; +}; + +// --------------------------------------------------------------------------- +/** Computes normal vectors for the mesh + */ +template +void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh); + + +// include implementations +#include "SmoothingGroups.inl" + +#endif // !! AI_SMOOTHINGGROUPS_H_INC \ No newline at end of file diff --git a/code/3DSGenNormals.cpp b/code/SmoothingGroups.inl similarity index 56% rename from code/3DSGenNormals.cpp rename to code/SmoothingGroups.inl index 897f1d1b3..42468afac 100644 --- a/code/3DSGenNormals.cpp +++ b/code/SmoothingGroups.inl @@ -39,80 +39,81 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. --------------------------------------------------------------------------- */ -/** @file Implementation of the 3ds importer class */ -#include "3DSLoader.h" -#include "MaterialSystem.h" +/** @file Generation of normal vectors basing on smoothing groups */ + +#ifndef AI_SMOOTHINGGROUPS_INL_INCLUDED +#define AI_SMOOTHINGGROUPS_INL_INCLUDED + +// internal headers +#include "SGSpatialSort.h" + +// CRT header #include -#include "../include/IOStream.h" -#include "../include/IOSystem.h" -#include "../include/aiMesh.h" -#include "../include/aiScene.h" -#include "../include/aiAssert.h" -#include "3DSSpatialSort.h" using namespace Assimp; // ------------------------------------------------------------------------------------------------ -void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh) +template +void ComputeNormalsWithSmoothingsGroups(MeshWithSmoothingGroups& sMesh) { // First generate face normals - sMesh->mNormals.resize(sMesh->mPositions.size(),aiVector3D()); - for( unsigned int a = 0; a < sMesh->mFaces.size(); a++) + sMesh.mNormals.resize(sMesh.mPositions.size(),aiVector3D()); + for( unsigned int a = 0; a < sMesh.mFaces.size(); a++) { - Dot3DS::Face& face = sMesh->mFaces[a]; + T& face = sMesh.mFaces[a]; // assume it is a triangle - aiVector3D* pV1 = &sMesh->mPositions[face.mIndices[0]]; - aiVector3D* pV2 = &sMesh->mPositions[face.mIndices[1]]; - aiVector3D* pV3 = &sMesh->mPositions[face.mIndices[2]]; + aiVector3D* pV1 = &sMesh.mPositions[face.mIndices[0]]; + aiVector3D* pV2 = &sMesh.mPositions[face.mIndices[1]]; + aiVector3D* pV3 = &sMesh.mPositions[face.mIndices[2]]; - // FIX invert all vertex normals - aiVector3D pDelta1 = *pV3 - *pV1; - aiVector3D pDelta2 = *pV2 - *pV1; + aiVector3D pDelta1 = *pV2 - *pV1; + aiVector3D pDelta2 = *pV3 - *pV1; aiVector3D vNor = pDelta1 ^ pDelta2; - sMesh->mNormals[face.mIndices[0]] = vNor; - sMesh->mNormals[face.mIndices[1]] = vNor; - sMesh->mNormals[face.mIndices[2]] = vNor; + sMesh.mNormals[face.mIndices[0]] = vNor; + sMesh.mNormals[face.mIndices[1]] = vNor; + sMesh.mNormals[face.mIndices[2]] = vNor; } // calculate the position bounds so we have a reliable epsilon to // check position differences against // @Schrompf: This is the 6th time this snippet is repeated! aiVector3D minVec( 1e10f, 1e10f, 1e10f), maxVec( -1e10f, -1e10f, -1e10f); - for( unsigned int a = 0; a < sMesh->mPositions.size(); a++) + for( unsigned int a = 0; a < sMesh.mPositions.size(); a++) { - minVec.x = std::min( minVec.x, sMesh->mPositions[a].x); - minVec.y = std::min( minVec.y, sMesh->mPositions[a].y); - minVec.z = std::min( minVec.z, sMesh->mPositions[a].z); - maxVec.x = std::max( maxVec.x, sMesh->mPositions[a].x); - maxVec.y = std::max( maxVec.y, sMesh->mPositions[a].y); - maxVec.z = std::max( maxVec.z, sMesh->mPositions[a].z); + minVec.x = std::min( minVec.x, sMesh.mPositions[a].x); + minVec.y = std::min( minVec.y, sMesh.mPositions[a].y); + minVec.z = std::min( minVec.z, sMesh.mPositions[a].z); + maxVec.x = std::max( maxVec.x, sMesh.mPositions[a].x); + maxVec.y = std::max( maxVec.y, sMesh.mPositions[a].y); + maxVec.z = std::max( maxVec.z, sMesh.mPositions[a].z); } const float posEpsilon = (maxVec - minVec).Length() * 1e-5f; std::vector avNormals; - avNormals.resize(sMesh->mNormals.size()); + avNormals.resize(sMesh.mNormals.size()); // now generate the spatial sort tree - D3DSSpatialSorter sSort; - for( std::vector::iterator - i = sMesh->mFaces.begin(); - i != sMesh->mFaces.end();++i) + SGSpatialSort sSort; + for( std::vector::iterator + i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) { - sSort.AddFace(&(*i),sMesh->mPositions); + sSort.Add(sMesh.mPositions[(*i).mIndices[0]],(*i).mIndices[0],(*i).iSmoothGroup); + sSort.Add(sMesh.mPositions[(*i).mIndices[1]],(*i).mIndices[1],(*i).iSmoothGroup); + sSort.Add(sMesh.mPositions[(*i).mIndices[2]],(*i).mIndices[2],(*i).iSmoothGroup); } sSort.Prepare(); - for( std::vector::iterator - i = sMesh->mFaces.begin(); - i != sMesh->mFaces.end();++i) + for( std::vector::iterator + i = sMesh.mFaces.begin(); + i != sMesh.mFaces.end();++i) { std::vector poResult; for (unsigned int c = 0; c < 3;++c) { - - sSort.FindPositions(sMesh->mPositions[(*i).mIndices[c]],(*i).iSmoothGroup, + sSort.FindPositions(sMesh.mPositions[(*i).mIndices[c]],(*i).iSmoothGroup, posEpsilon,poResult); aiVector3D vNormals; @@ -121,21 +122,16 @@ void Dot3DSImporter::GenNormals(Dot3DS::Mesh* sMesh) a = poResult.begin(); a != poResult.end();++a) { - vNormals += sMesh->mNormals[(*a)]; + vNormals += sMesh.mNormals[(*a)]; fDiv += 1.0f; } - vNormals.x /= fDiv; - vNormals.y /= fDiv; - vNormals.z /= fDiv; - vNormals.Normalize(); - - // do the common coordinate system adjustment - std::swap(vNormals.y,vNormals.z); - + vNormals.x /= fDiv;vNormals.y /= fDiv;vNormals.z /= fDiv; + //vNormals.Normalize(); avNormals[(*i).mIndices[c]] = vNormals; - poResult.clear(); + //poResult.clear(); } } - sMesh->mNormals = avNormals; - return; + sMesh.mNormals = avNormals; } + +#endif // !! AI_SMOOTHINGGROUPS_INL_INCLUDED diff --git a/code/SplitLargeMeshes.cpp b/code/SplitLargeMeshes.cpp index 49d24a3f8..986508e08 100644 --- a/code/SplitLargeMeshes.cpp +++ b/code/SplitLargeMeshes.cpp @@ -101,7 +101,7 @@ void SplitLargeMeshesProcess_Triangle::Execute( aiScene* pScene) void SplitLargeMeshesProcess_Triangle::SetupProperties( const Importer* pImp) { // get the current value of the split property - this->LIMIT = pImp->GetProperty(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); + this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_TRIANGLE_LIMIT,AI_SLM_DEFAULT_MAX_TRIANGLES); } // ------------------------------------------------------------------------------------------------ // Update a node after some meshes have been split @@ -373,7 +373,7 @@ void SplitLargeMeshesProcess_Vertex::Execute( aiScene* pScene) // Setup properties void SplitLargeMeshesProcess_Vertex::SetupProperties( const Importer* pImp) { - this->LIMIT = pImp->GetProperty(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); + this->LIMIT = pImp->GetPropertyInteger(AI_CONFIG_PP_SLM_VERTEX_LIMIT,AI_SLM_DEFAULT_MAX_VERTICES); } // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. diff --git a/contrib/cppunit-1.12.1/src/cppunit/cppunit.vcproj.ALEXPC.Alex.user b/contrib/cppunit-1.12.1/src/cppunit/cppunit.vcproj.ALEXPC.Alex.user index 2f86a9b52..6a700008b 100644 --- a/contrib/cppunit-1.12.1/src/cppunit/cppunit.vcproj.ALEXPC.Alex.user +++ b/contrib/cppunit-1.12.1/src/cppunit/cppunit.vcproj.ALEXPC.Alex.user @@ -34,7 +34,7 @@ /> + * 1. Remove animation nodes and data from the scene. This allows other + * steps for further optimizations.
+ * 2. Combine very small meshes to larger ones. Only if the meshes + * are used by the same node or by nodes on the same hierarchy (with + * equal local transformations). Unlike PreTransformVertices, the + * OptimizeGraph-step doesn't transform vertices from one space + * another.
+ * 3. Remove hierarchy levels
+ * + * It is recommended to have this step run with the default configuration. + */ + aiProcess_OptimizeGraph = 0x4000 }; +/** @def AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST + * @brief Default postprocess configuration targeted at realtime applications + * which need to load models as fast as possible. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. + */ +#define AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_Triangulate + + + /** @def AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST + * @brief Default postprocess configuration targeted at realtime applications. + * Unlike AI_POSTPROCESS_DEFAULT_REALTIME_FASTEST, this configuration + * performs some extra optimizations. + * + * If you're using DirectX, don't forget to combine this value with + * the #aiProcess_ConvertToLeftHanded step. + */ +#define AI_POSTPROCESS_DEFAULT_REALTIME \ + aiProcess_CalcTangentSpace | \ + aiProcess_GenSmoothNormals | \ + aiProcess_JoinIdenticalVertices | \ + aiProcess_ImproveCacheLocality | \ + aiProcess_LimitBoneWeights | \ + aiProcess_RemoveRedundantMaterials | \ + aiProcess_SplitLargeMeshes | \ + aiProcess_OptimizeGraph | \ + aiProcess_Triangulate + + #ifdef __cplusplus } // end of extern "C" #endif diff --git a/include/aiVector3D.h b/include/aiVector3D.h index 2652009a0..ae10bd109 100644 --- a/include/aiVector3D.h +++ b/include/aiVector3D.h @@ -75,6 +75,9 @@ struct aiVector3D inline bool operator!= (const aiVector3D& other) const {return x != other.x || y != other.y || z != other.z;} + inline aiVector3D& operator= (float f) + {x = y = z = f;return *this;} + #endif // __cplusplus float x, y, z; diff --git a/include/assimp.h b/include/assimp.h index 393cc06ee..37960460d 100644 --- a/include/assimp.h +++ b/include/assimp.h @@ -66,8 +66,8 @@ struct aiString; * @param pFile Path and filename of the file to be imported, * expected to be a null-terminated c-string. NULL is not a valid value. * @param pFlags Optional post processing steps to be executed after -* a successful import. Provide a bitwise combination of the #aiPostProcessSteps -* flags. +* a successful import. Provide a bitwise combination of the +* #aiPostProcessSteps flags. * @return Pointer to the imported data or NULL if the import failed. */ // --------------------------------------------------------------------------- @@ -85,18 +85,19 @@ ASSIMP_API const C_STRUCT aiScene* aiImportFile( const char* pFile, * done with it, call aiReleaseImport() to free the resources associated with * this file. If the import fails, NULL is returned instead. Call * aiGetErrorString() to retrieve a human-readable error text. -* @param pFile aiFileIO structure. All functions pointers must be -* initialized. aiFileIO::OpenFunc() and aiFileIO::CloseFunc() -* will be used to open other files in the fs if the asset to be -* loaded depends on them. NULL is not a valid value. -* @return Pointer to the imported data or NULL if the import failed. -* -* @note The C-API creates a new Importer instance internally for each call -* to this function. Therefore the C-API is thread-safe. +* @param pFile Path and filename of the file to be imported, +* expected to be a null-terminated c-string. NULL is not a valid value. +* @param pFlags Optional post processing steps to be executed after +* a successful import. Provide a bitwise combination of the +* #aiPostProcessSteps flags. +* @param pFS aiFileIO structure. Will be used to open the model file itself +* and any other files the loader needs to open. +* @return Pointer to the imported data or NULL if the import failed. */ // --------------------------------------------------------------------------- ASSIMP_API const C_STRUCT aiScene* aiImportFileEx( - const C_STRUCT aiFileIO* pFile); + const char* pFile, unsigned int pFlags, + C_STRUCT aiFileIO* pFS); // --------------------------------------------------------------------------- @@ -151,6 +152,31 @@ ASSIMP_API void aiGetExtensionList(C_STRUCT aiString* szOut); ASSIMP_API void aiGetMemoryRequirements(const C_STRUCT aiScene* pIn, C_STRUCT aiMemoryInfo* in); + +// --------------------------------------------------------------------------- +/** Set an integer property. This is the C-version of + * #Importer::SetPropertyInteger(). In the C-API properties are shared by + * all imports. It is not possible to specify them per asset. + * + * \param szName Name of the configuration property to be set. All constants + * are defined in the aiConfig.h header file. + * \param value New value for the property + */ +// --------------------------------------------------------------------------- +ASSIMP_API void aiSetImportPropertyInteger(const char* szName, int value); + +// --------------------------------------------------------------------------- +/** @see aiSetImportPropertyInteger() + */ +ASSIMP_API void aiSetImportPropertyFloat(const char* szName, float value); + +// --------------------------------------------------------------------------- +/** @see aiSetImportPropertyInteger() + */ +ASSIMP_API void aiSetImportPropertyString(const char* szName, + const C_STRUCT aiString* st); + + #ifdef __cplusplus } #endif diff --git a/include/assimp.hpp b/include/assimp.hpp index 62ce25884..20d50f337 100644 --- a/include/assimp.hpp +++ b/include/assimp.hpp @@ -49,6 +49,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // STL headers #include +#include #include // public ASSIMP headers @@ -67,6 +68,8 @@ namespace Assimp #define AI_PROPERTY_WAS_NOT_EXISTING 0xffffffff struct aiScene; +struct aiFileIO; +const aiScene* aiImportFileEx( const char*, unsigned int, aiFileIO*); namespace Assimp { @@ -101,28 +104,14 @@ class ASSIMP_API Importer { // used internally friend class BaseProcess; + friend const aiScene* ::aiImportFileEx( const char*, unsigned int, aiFileIO*); -protected: +public: - template - struct PropertyInfo - { - std::string name; - Type value; - - bool operator==(const PropertyInfo& other) const - { - return other.name == this->name && - other.value == this->value; - } - - bool operator!=(const PropertyInfo& other) const - { - return !(other == *this); - } - }; - - typedef PropertyInfo IntPropertyInfo; + typedef uint32_t KeyType; + typedef std::map IntPropertyMap; + typedef std::map FloatPropertyMap; + typedef std::map StringPropertyMap; public: @@ -188,28 +177,66 @@ public: #endif // ------------------------------------------------------------------- - /** Set a configuration property. + /** Set an integer configuration property. * @param szName Name of the property. All supported properties - * are defined in the aiConfig.g header (the constants share the + * are defined in the aiConfig.g header (all constants share the * prefix AI_CONFIG_XXX). * @param iValue New value of the property - * @return Old value of the property or AI_PROPERTY_WAS_NOT_EXISTING - * if the property has not yet been set. + * @param bWasExisting Optional pointer to receive true if the + * property was set before. The new value replaced the old value + * in this case. + * @note Property of different types (float, int, string ..) are kept + * on different stacks, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. */ - int SetProperty(const char* szName, int iValue); + void SetPropertyInteger(const char* szName, int iValue, + bool* bWasExisting = NULL); + + // ------------------------------------------------------------------- + /** Set a floating-point configuration property. + * @see SetPropertyInteger() + */ + void SetPropertyFloat(const char* szName, float fValue, + bool* bWasExisting = NULL); + + // ------------------------------------------------------------------- + /** Set a string configuration property. + * @see SetPropertyInteger() + */ + void SetPropertyString(const char* szName, const std::string& sValue, + bool* bWasExisting = NULL); // ------------------------------------------------------------------- /** Get a configuration property. * @param szName Name of the property. All supported properties - * are defined in the aiConfig.g header (the constants start - * with AI_CONFIG_XXX). - * @param iErrorReturn Value that is returned if the property - * is not found. Note that this value, not the default value - * for the requested property is returned! + * are defined in the aiConfig.g header (all constants share the + * prefix AI_CONFIG_XXX). + * @param iErrorReturn Value that is returned if the property + * is not found. * @return Current value of the property + * @note Property of different types (float, int, string ..) are kept + * on different lists, so calling SetPropertyInteger() for a + * floating-point property has no effect - the loader will call + * GetPropertyFloat() to read the property, but it won't be there. */ - int GetProperty(const char* szName, int iErrorReturn = 0xffffffff) const; + int GetPropertyInteger(const char* szName, + int iErrorReturn = 0xffffffff) const; + + // ------------------------------------------------------------------- + /** Get a floating-point configuration property + * @see GetPropertyInteger() + */ + float GetPropertyFloat(const char* szName, + float fErrorReturn = 10e10f) const; + + // ------------------------------------------------------------------- + /** Get a string configuration property + * @see GetPropertyInteger() + */ + std::string GetPropertyString(const char* szName, + const std::string& sErrorReturn = "") const; // ------------------------------------------------------------------- @@ -278,10 +305,11 @@ public: // ------------------------------------------------------------------- - /** Returns whether a given file extension is supported by ASSIMP + /** Returns whether a given file extension is supported by ASSIMP. * * @param szExtension Extension to be checked. - * Must include a leading dot '.'. Example: ".3ds", ".md3" + * Must include a trailing dot '.'. Example: ".3ds", ".md3". + * Cases-insensitive. * @return true if the extension is supported, false otherwise */ bool IsExtensionSupported(const std::string& szExtension); @@ -298,6 +326,18 @@ public: void GetExtensionList(std::string& szOut); + // ------------------------------------------------------------------- + /** Find the loader corresponding to a specific file extension. + * + * This is quite similar to IsExtensionSupported() except a + * BaseImporter instance is returned. + * @param szExtension Extension to be checke, cases insensitive, + * must include a trailing dot. + * @return NULL if there is no loader for the extension. + */ + BaseImporter* FindLoader (const std::string& szExtension); + + // ------------------------------------------------------------------- /** Returns the scene loaded by the last successful call to ReadFile() * @@ -351,9 +391,17 @@ protected: std::string mErrorString; /** List of integer properties */ - std::vector mIntProperties; + IntPropertyMap mIntProperties; - /** Used for testing */ + /** List of floating-point properties */ + FloatPropertyMap mFloatProperties; + + /** List of string properties */ + StringPropertyMap mStringProperties; + + /** Used for testing - extra verbose mode causes the + validateDataStructure-Step to be executed before + and after every single postprocess step */ bool bExtraVerbose; }; diff --git a/port/jAssimp/src/assimp/ConfigProperty.java b/port/jAssimp/src/assimp/ConfigProperty.java index e8f2b0afc..aba29e631 100644 --- a/port/jAssimp/src/assimp/ConfigProperty.java +++ b/port/jAssimp/src/assimp/ConfigProperty.java @@ -44,7 +44,7 @@ package assimp; /** * Defines configuration properties. - * + *

* Static helper class, can't be instanced. It defines configuration * property keys to be used with Importer.setPropertyInt * @@ -57,7 +57,6 @@ public class ConfigProperty { } - /** * Default value for the CONFIG_PP_SLM_TRIANGLE_LIMIT * configuration property. @@ -79,15 +78,13 @@ public class ConfigProperty { public static final int DEFAULT_LBW_MAX_WEIGHTS = 4; - - - /** * Set the maximum number of vertices in a mesh. *

* This is used by the "SplitLargeMeshes" PostProcess-Step to determine * whether a mesh must be splitted or not. - * \note The default value is DEFAULT_SLM_MAX_TRIANGLES + * note: The default value is DEFAULT_SLM_MAX_TRIANGLES. + * The type of the property is int. */ public static final String CONFIG_PP_SLM_TRIANGLE_LIMIT = "pp.slm.triangle_limit"; @@ -98,7 +95,8 @@ public class ConfigProperty { *

* This is used by the "SplitLargeMeshes" PostProcess-Step to determine * whether a mesh must be splitted or not. - * \note The default value is DEFAULT_SLM_MAX_VERTICES + * note: The default value is DEFAULT_SLM_MAX_VERTICES. + * The type of the property is int. */ public static final String CONFIG_PP_SLM_VERTEX_LIMIT = "pp.slm.vertex_limit"; @@ -108,7 +106,8 @@ public class ConfigProperty { * Set the maximum number of bones affecting a single vertex *

* This is used by the aiProcess_LimitBoneWeights PostProcess-Step. - * \note The default value is DEFAULT_LBW_MAX_WEIGHTS + * note :The default value is DEFAULT_LBW_MAX_WEIGHTS. + * The type of the property is int. */ public static final String CONFIG_PP_LBW_MAX_WEIGHTS = "pp.lbw.weights_limit"; @@ -126,6 +125,7 @@ public class ConfigProperty { * CONFIG_IMPORT_XXX_KEYFRAME options (where XXX is a * placeholder for the file format for which you want to override the * global setting). + * The type of the property is int. */ public static final String CONFIG_IMPORT_GLOBAL_KEYFRAME = "imp.global.kf"; @@ -144,11 +144,60 @@ public class ConfigProperty { * There are some faulty 3DS files on the internet which look * only correctly with pivot points disabled. By default, * this option is disabled. + * note: This is a boolean property stored as an integer, 0 is false */ public static final String CONFIG_IMPORT_3DS_IGNORE_PIVOT = "imp.3ds.nopivot"; - public static final String CONFIG_PP_OG_MAX_DEPTH = "pp.og.max_depth"; - public static final String CONFIG_PP_OG_MIN_TRIS_PER_NODE = "pp.og.min_tris"; - public static final String CONFIG_PP_OG_MAXIMALLY_SMALL = "pp.og.maximally_small"; + + /** + * Specifies the maximum angle that may be between two vertex tangents + * that their tangents and bitangents are smoothed. + *

+ * This applies to the CalcTangentSpace-Step. The angle is specified + * in degrees, so 180 is PI. The default value is + * 45 degrees. The maximum value is 180.f + * The type of the property is float. + */ + public static final String AI_CONFIG_PP_CT_MAX_SMOOTHING_ANGLE + = "pp.ct.max_smoothing"; + + + /** + * Specifies the maximum angle that may be between two face normals + * at the same vertex position that their are smoothed. + *

+ * This applies to the GenSmoothNormals-Step. The angle is specified + * in degrees * 1000, so 180.f is PI. The default value is + * 180 degrees (all vertex normals are smoothed). The maximum value is 180.f + * The type of the property is float. + */ + public static final String AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE + = "pp.gsn.max_smoothing"; + + + /** + * Specifies the minimum number of faces a node should have. + * This is an input parameter to the OptimizeGraph-Step. + *

+ * Nodes whose referenced meshes have less faces than this value + * are propably joined with neighbors with identical world matrices. + * However, it is just a hint to the step. + * The type of the property is int. + */ + public static final String AI_CONFIG_PP_OG_MIN_NUM_FACES + = "pp.og.min_faces"; + + + /** \brief Specifies whether animations are removed from the asset. + * This is an input parameter to the OptimizeGraph-Step. + * + * If an application does not need the animation data, erasing it at the + * beginning of the post-process pipeline allows some steps - including + * OptimizeGraph itself - to apply further optimizations. + * note: This is a boolean property stored as an integer, 0 is false + */ + public static final String AI_CONFIG_PP_OG_REMOVE_ANIMATIONS + = "pp.og.remove_anims"; + } diff --git a/port/jAssimp/src/assimp/Importer.java b/port/jAssimp/src/assimp/Importer.java index 09386d224..06a115330 100644 --- a/port/jAssimp/src/assimp/Importer.java +++ b/port/jAssimp/src/assimp/Importer.java @@ -71,6 +71,38 @@ public class Importer { Type value; } + + /** + * Represents a property list + */ + private class PropertyList extends Vector> { + + public void setProperty(final String prop, final Type val) { + + for (Property i : this) { + if (i.key.equals(prop)) { + i.value = val; + return; + } + } + + Property propNew = new Property(); + propNew.key = prop; + propNew.value = val; + this.add(propNew); + } + + public Type getProperty(final String prop) { + + for (Property i : this) { + if (i.key.equals(prop)) { + return i.value; + } + } + return null; + } + } + /** * Default implementation of IOStream. *
@@ -158,12 +190,14 @@ public class Importer { /** * I/O system to be used */ - private IOSystem ioSystem = null; + private IOSystem ioSystem = new DefaultIOSystem(); /** - * List of config properties + * List of config properties for all supported types: int, float and string */ - private Vector> properties; + private PropertyList properties = new PropertyList(); + private PropertyList propertiesFloat = new PropertyList(); + private PropertyList propertiesString = new PropertyList(); /** @@ -186,14 +220,11 @@ public class Importer { * * @param iVersion Version of the JNI interface to be used. * @throws NativeException Thrown if the jassimp library could not be loaded - * or if the entry point to the module wasn't found. if this exception - * is not thrown, you can assume that jAssimp is fully available. + * or if the entry point to the module wasn't found. if this exception + * is not thrown, you can assume that jAssimp is fully available. */ public Importer(int iVersion) throws NativeException { - // allocate a default I/O system - ioSystem = new DefaultIOSystem(); - if (!bLibInitialized) { /** try to load the jassimp library. First try to load the @@ -319,7 +350,7 @@ public class Importer { * @param path Path to the file to be read * @return null if the import failed, otherwise a valid Scene instance * @throws NativeException This exception is thrown when an unknown error - * occurs in the JNI bridge module. + * occurs in the JNI bridge module. */ public final Scene readFile(String path) throws NativeException { this.scene = new Scene(this); @@ -346,8 +377,10 @@ public class Importer { else if (step.equals(PostProcessStep.PreTransformVertices)) flags |= 0x100; else if (step.equals(PostProcessStep.LimitBoneWeights)) flags |= 0x200; else if (step.equals(PostProcessStep.ValidateDataStructure)) flags |= 0x400; - else if (step.equals(PostProcessStep.FixInfacingNormals)) flags |= 0x800; - else if (step.equals(PostProcessStep.ImproveVertexLocality)) flags |= 0x1600; + else if (step.equals(PostProcessStep.ImproveVertexLocality)) flags |= 0x800; + else if (step.equals(PostProcessStep.RemoveRedundantMaterials)) flags |= 0x1000; + else if (step.equals(PostProcessStep.FixInfacingNormals)) flags |= 0x2000; + else if (step.equals(PostProcessStep.OptimizeGraph)) flags |= 0x4000; } // now load the mesh @@ -418,30 +451,39 @@ public class Importer { * * @param prop Name of the config property * @param val New value for the config property - * @return Old value of the property or PROPERTY_WAS_NOT_EXISTING - * if the property has not yet been set. */ - public final int setPropertyInt(final String prop, int val) { + public final void setPropertyInt(final String prop, int val) { - for (Property i : this.properties) { - if (i.key.equals(prop)) { - int old = i.value; - i.value = val; - - // make sure all changes are sent to the native implementation - this._NativeSetPropertyInt(prop, val, this.getContext()); - return old; - } - } - - Property propNew = new Property(); - propNew.key = prop; - propNew.value = val; - this.properties.add(propNew); - - // make sure all changes are sent to the native implementation + this.properties.setProperty(prop, val); this._NativeSetPropertyInt(prop, val, this.getContext()); - return PROPERTY_WAS_NOT_EXISTING; + } + + + /** + * Set a floating-point property. All supported config properties are + * defined as constants in the ConfigProperty class + * + * @param prop Name of the config property + * @param val New value for the config property + */ + public final void setPropertyFloat(final String prop, float val) { + + this.propertiesFloat.setProperty(prop, val); + this._NativeSetPropertyFloat(prop, val, this.getContext()); + } + + + /** + * Set a string property. All supported config properties are + * defined as constants in the ConfigProperty class + * + * @param prop Name of the config property + * @param val New value for the config property + */ + public final void setPropertyString(final String prop, String val) { + + this.propertiesString.setProperty(prop, val); + this._NativeSetPropertyString(prop, val, this.getContext()); } @@ -457,12 +499,36 @@ public class Importer { */ public final int getPropertyInt(final String prop, int error_return) { - for (Property i : this.properties) { - if (i.key.equals(prop)) { - return i.value; - } - } - return error_return; + Integer i = this.properties.getProperty(prop); + return i != null ? i : error_return; + } + + + /** + * Gets a floating-point config property that has been set using + * setPropertyFloat. All supported config properties are + * defined as constants in the ConfigProperty class + * + * @see getPropertyInt + */ + public final float getPropertyFloat(final String prop, float error_return) { + + Float i = this.propertiesFloat.getProperty(prop); + return i != null ? i : error_return; + } + + + /** + * Gets a string config property that has been set using + * setPropertyString. All supported config properties are + * defined as constants in the ConfigProperty class + * + * @see getPropertyInt + */ + public final String getPropertyString(final String prop, String error_return) { + + String i = this.propertiesString.getProperty(prop); + return i != null ? i : error_return; } /** @@ -531,4 +597,12 @@ public class Importer { * @return 0xffffffff if an error occured */ private native int _NativeSetPropertyInt(String name, int prop, long iContext); + + // float-version + private native int _NativeSetPropertyFloat(String name, + float prop, long iContext); + + // String-version + private native int _NativeSetPropertyString(String name, + String prop, long iContext); } diff --git a/port/jAssimp/src/assimp/PostProcessStep.java b/port/jAssimp/src/assimp/PostProcessStep.java index e14cbead4..7a096de28 100644 --- a/port/jAssimp/src/assimp/PostProcessStep.java +++ b/port/jAssimp/src/assimp/PostProcessStep.java @@ -174,7 +174,6 @@ public class PostProcessStep { public static final PostProcessStep LimitBoneWeights = new PostProcessStep("LimitBoneWeights"); - /** * Validates the aiScene data structure before it is returned. * This makes sure that all indices are valid, all animations and @@ -187,19 +186,6 @@ public class PostProcessStep { new PostProcessStep("ValidateDataStructure"); - /** This step tries to determine which meshes have normal vectors - * that are facing inwards. The algorithm is simple but effective: - * the bounding box of all vertices + their normals is compared against - * the volume of the bounding box of all vertices without their normals. - * This works well for most objects, problems might occur with planar - * surfaces. However the step tries to filter such cases out. - * The step inverts all infacing normals. Generally it is recommended - * to enable this step. - */ - public static final PostProcessStep FixInfacingNormals = - new PostProcessStep("FixInfacingNormals"); - - /** Reorders triangles for vertex cache locality and thus better performance. * The step tries to improve the ACMR (average post-transform vertex cache * miss ratio) for all meshes. The step runs in O(n) and is roughly @@ -210,6 +196,46 @@ public class PostProcessStep { new PostProcessStep("ImproveVertexLocality"); + /** Searches for redundant materials and removes them. + * + * This is especially useful in combination with the PretransformVertices + * and OptimizeGraph steps. Both steps join small meshes, but they + * can't do that if two meshes have different materials. + */ + public static final PostProcessStep RemoveRedundantMaterials = + new PostProcessStep("RemoveRedundantMaterials"); + + /** This step tries to determine which meshes have normal vectors + * that are facing inwards. The algorithm is simple but effective: + * the bounding box of all vertices + their normals is compared against + * the volume of the bounding box of all vertices without their normals. + * This works well for most objects, problems might occur with planar + * surfaces. However, the step tries to filter such cases. + * The step inverts all infacing normals. Generally it is recommended + * to enable this step, although the result is not always correct. + */ + public static final PostProcessStep FixInfacingNormals = + new PostProcessStep("FixInfacingNormals"); + + /** This step performs some optimizations on the node graph. + * + * It is incompatible to the PreTransformVertices-Step. Some configuration + * options exist, see aiConfig.h for more details. + * Generally, two actions are available:
+ * 1. Remove animation nodes and data from the scene. This allows other + * steps for further optimizations.
+ * 2. Combine very small meshes to larger ones. Only if the meshes + * are used by the same node or by nodes on the same hierarchy (with + * equal local transformations). Unlike PreTransformVertices, the + * OptimizeGraph-step doesn't transform vertices from one space + * another.
+ * 3. Remove hierarchy levels
+ * + * It is recommended to have this step run with the default configuration. + */ + public static final PostProcessStep OptimizeGraph = + new PostProcessStep("OptimizeGraph"); + private final String myName; // for debug only private PostProcessStep(String name) { diff --git a/test/unit/utImporter.cpp b/test/unit/utImporter.cpp new file mode 100644 index 000000000..9be44816d --- /dev/null +++ b/test/unit/utImporter.cpp @@ -0,0 +1,107 @@ +#include "utImporter.h" + + +CPPUNIT_TEST_SUITE_REGISTRATION (ImporterTest); + +#define AIUT_DEF_ERROR_TEXT "sorry, this is a test" + + +bool TestPlugin :: CanRead( const std::string& pFile, + IOSystem* pIOHandler) const +{ + 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); + + // todo ... make case-insensitive + return (extension == ".apple" || extension == ".mac" || + extension == ".linux" || extension == ".windows" ); +} + +void TestPlugin :: GetExtensionList(std::string& append) +{ + append.append("*.apple;*.mac;*.linux;*.windows"); +} + +void TestPlugin :: InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler) +{ + throw new ImportErrorException(AIUT_DEF_ERROR_TEXT); +} + + +void ImporterTest :: setUp (void) +{ + pImp = new Importer(); +} + +void ImporterTest :: tearDown (void) +{ + delete pImp; +} + +void ImporterTest :: testIntProperty (void) +{ + bool b; + pImp->SetPropertyInteger("quakquak",1503,&b); + CPPUNIT_ASSERT(!b); + CPPUNIT_ASSERT(1503 == pImp->GetPropertyInteger("quakquak",0)); + CPPUNIT_ASSERT(314159 == pImp->GetPropertyInteger("not_there",314159)); + + pImp->SetPropertyInteger("quakquak",1504,&b); + CPPUNIT_ASSERT(b); +} + +void ImporterTest :: testFloatProperty (void) +{ + bool b; + pImp->SetPropertyFloat("quakquak",1503.f,&b); + CPPUNIT_ASSERT(!b); + CPPUNIT_ASSERT(1503.f == pImp->GetPropertyFloat("quakquak",0.f)); + CPPUNIT_ASSERT(314159.f == pImp->GetPropertyFloat("not_there",314159.f)); +} + +void ImporterTest :: testStringProperty (void) +{ + bool b; + pImp->SetPropertyString("quakquak","test",&b); + CPPUNIT_ASSERT(!b); + CPPUNIT_ASSERT("test" == pImp->GetPropertyString("quakquak","weghwekg")); + CPPUNIT_ASSERT("ILoveYou" == pImp->GetPropertyString("not_there","ILoveYou")); +} + +void ImporterTest :: testPluginInterface (void) +{ + pImp->RegisterLoader(new TestPlugin()); + CPPUNIT_ASSERT(pImp->IsExtensionSupported(".apple")); + CPPUNIT_ASSERT(pImp->IsExtensionSupported(".mac")); + CPPUNIT_ASSERT(pImp->IsExtensionSupported(".linux")); + CPPUNIT_ASSERT(pImp->IsExtensionSupported(".windows")); + + TestPlugin* p = (TestPlugin*) pImp->FindLoader(".windows"); + CPPUNIT_ASSERT(NULL != p); + + try { + p->InternReadFile("",0,NULL); + } + catch ( ImportErrorException* ex) + { + CPPUNIT_ASSERT(ex->GetErrorText() == AIUT_DEF_ERROR_TEXT); + + // unregister the plugin and delete it + pImp->UnregisterLoader(p); + delete p; + + return; + } + CPPUNIT_ASSERT(false); // control shouldn't reach this point +} + +void ImporterTest :: testExtensionCheck (void) +{ + std::string s; + pImp->GetExtensionList(s); + + // todo .. +} diff --git a/test/unit/utImporter.h b/test/unit/utImporter.h new file mode 100644 index 000000000..79aa6a03d --- /dev/null +++ b/test/unit/utImporter.h @@ -0,0 +1,57 @@ +#ifndef TESTIMPORTER_H +#define TESTIMPORTER_H + +#include +#include + +#include + + +using namespace std; +using namespace Assimp; + +class ImporterTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (ImporterTest); + CPPUNIT_TEST (testIntProperty); + CPPUNIT_TEST (testFloatProperty); + CPPUNIT_TEST (testStringProperty); + CPPUNIT_TEST (testPluginInterface); + CPPUNIT_TEST (testExtensionCheck); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp (void); + void tearDown (void); + + protected: + + void testIntProperty (void); + void testFloatProperty (void); + void testStringProperty (void); + + void testPluginInterface (void); + void testExtensionCheck (void); + + private: + + Importer* pImp; +}; + +class TestPlugin : public BaseImporter +{ +public: + + // overriden + bool CanRead( const std::string& pFile, + IOSystem* pIOHandler) const; + + // overriden + void GetExtensionList(std::string& append); + + // overriden + void InternReadFile( const std::string& pFile, + aiScene* pScene, IOSystem* pIOHandler); +}; + +#endif diff --git a/test/unit/utJoinVertices.cpp b/test/unit/utJoinVertices.cpp index e69de29bb..e938f5277 100644 --- a/test/unit/utJoinVertices.cpp +++ b/test/unit/utJoinVertices.cpp @@ -0,0 +1,85 @@ + +#include "utJoinVertices.h" + + +CPPUNIT_TEST_SUITE_REGISTRATION (JoinVerticesTest); + +void JoinVerticesTest :: setUp (void) +{ + // construct the process + this->piProcess = new JoinVerticesProcess(); + + // create a quite small mesh for testing purposes - + // the mesh itself is *something* but it has redundant vertices + this->pcMesh = new aiMesh(); + + pcMesh->mNumVertices = 900; + aiVector3D*& pv = pcMesh->mVertices = new aiVector3D[900]; + for (unsigned int i = 0; i < 3;++i) + { + const unsigned int base = i*300; + for (unsigned int a = 0; a < 300;++a) + { + pv[base+a].x = pv[base+a].y = pv[base+a].z = (float)a; + } + } + + // generate faces - each vertex is referenced once + pcMesh->mNumFaces = 300; + pcMesh->mFaces = new aiFace[300]; + for (unsigned int i = 0,p = 0; i < 300;++i) + { + aiFace& face = pcMesh->mFaces[i]; + face.mIndices = new unsigned int[ face.mNumIndices = 3 ]; + for (unsigned int a = 0; a < 3;++a) + face.mIndices[a] = p++; + } + + // generate extra members - set them to zero to make sure they're identical + pcMesh->mTextureCoords[0] = new aiVector3D[900]; + for (unsigned int i = 0; i < 900;++i)pcMesh->mTextureCoords[0][i] = 0.f; + + pcMesh->mNormals = new aiVector3D[900]; + for (unsigned int i = 0; i < 900;++i)pcMesh->mNormals[i] = 0.f; + + pcMesh->mTangents = new aiVector3D[900]; + for (unsigned int i = 0; i < 900;++i)pcMesh->mTangents[i] = 0.f; + + pcMesh->mBitangents = new aiVector3D[900]; + for (unsigned int i = 0; i < 900;++i)pcMesh->mBitangents[i] = 0.f; +} + +void JoinVerticesTest :: tearDown (void) +{ + delete this->pcMesh; + delete this->piProcess; +} + +void JoinVerticesTest :: testProcess(void) +{ + // execute the step on the given data + this->piProcess->ProcessMesh(this->pcMesh,0); + + // the number of faces shouldn't change + CPPUNIT_ASSERT(pcMesh->mNumFaces == 300); + CPPUNIT_ASSERT(pcMesh->mNumVertices == 300); + + CPPUNIT_ASSERT(NULL != pcMesh->mNormals); + CPPUNIT_ASSERT(NULL != pcMesh->mTangents); + CPPUNIT_ASSERT(NULL != pcMesh->mBitangents); + CPPUNIT_ASSERT(NULL != pcMesh->mTextureCoords[0]); + + // the order doesn't care + float fSum = 0.f; + for (unsigned int i = 0; i < 300;++i) + { + aiVector3D& v = pcMesh->mVertices[i]; + fSum += v.x + v.y + v.z; + + CPPUNIT_ASSERT(!pcMesh->mNormals[i].x); + CPPUNIT_ASSERT(!pcMesh->mTangents[i].x); + CPPUNIT_ASSERT(!pcMesh->mBitangents[i].x); + CPPUNIT_ASSERT(!pcMesh->mTextureCoords[0][i].x); + } + CPPUNIT_ASSERT(fSum == 150.f*299.f*3.f); // gaussian sum equation +} \ No newline at end of file diff --git a/test/unit/utJoinVertices.h b/test/unit/utJoinVertices.h new file mode 100644 index 000000000..41361825d --- /dev/null +++ b/test/unit/utJoinVertices.h @@ -0,0 +1,36 @@ +#ifndef TESTLBW_H +#define TESTLBW_H + +#include +#include + +#include +#include + + +using namespace std; +using namespace Assimp; + +class JoinVerticesTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (JoinVerticesTest); + CPPUNIT_TEST (testProcess); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp (void); + void tearDown (void); + + protected: + + void testProcess (void); + + + private: + + JoinVerticesProcess* piProcess; + aiMesh* pcMesh; + +}; + +#endif diff --git a/test/unit/utLimitBoneWeights.h b/test/unit/utLimitBoneWeights.h index c662b9b19..61495874b 100644 --- a/test/unit/utLimitBoneWeights.h +++ b/test/unit/utLimitBoneWeights.h @@ -4,8 +4,6 @@ #include #include -#include -#include #include #include diff --git a/test/unit/utOptimizeGraph.cpp b/test/unit/utOptimizeGraph.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/test/unit/utOptimizeGraph.h b/test/unit/utOptimizeGraph.h new file mode 100644 index 000000000..2c92f0b22 --- /dev/null +++ b/test/unit/utOptimizeGraph.h @@ -0,0 +1,35 @@ +#ifndef TESTOG_H +#define TESTOG_H + +#include +#include + +#include +#include + + +using namespace std; +using namespace Assimp; + +class OptimizeGraphProcessTest : public CPPUNIT_NS :: TestFixture +{ + CPPUNIT_TEST_SUITE (OptimizeGraphProcessTest); + CPPUNIT_TEST (testProcess); + CPPUNIT_TEST_SUITE_END (); + + public: + void setUp (void); + void tearDown (void); + + protected: + + void testProcess (void); + + + private: + + OptimizeGraphProcess* piProcess; + aiScene* pcMesh; +}; + +#endif diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 3202ebf44..7151e6a1c 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -134,7 +134,7 @@ DWORD WINAPI LoadThreadProc(LPVOID lpParameter) aiProcess_ConvertToLeftHanded | // convert everything to D3D left handed space aiProcess_SplitLargeMeshes | // split large, unrenderable meshes into submeshes aiProcess_ValidateDataStructure | aiProcess_ImproveCacheLocality - | aiProcess_RemoveRedundantMaterials | aiProcess_FixInfacingNormals); // validate the output data structure + | aiProcess_RemoveRedundantMaterials ); // validate the output data structure // get the end time of zje operation, calculate delta t double fEnd = (double)timeGetTime(); diff --git a/workspaces/jidea5.1/jAssimp.ipr b/workspaces/jidea5.1/jAssimp.ipr index 9a09146f2..b35bfceac 100644 --- a/workspaces/jidea5.1/jAssimp.ipr +++ b/workspaces/jidea5.1/jAssimp.ipr @@ -178,7 +178,7 @@ - + diff --git a/workspaces/vc8/UnitTest.vcproj b/workspaces/vc8/UnitTest.vcproj index 623ebeac6..cc2ac55ed 100644 --- a/workspaces/vc8/UnitTest.vcproj +++ b/workspaces/vc8/UnitTest.vcproj @@ -683,6 +683,10 @@ RelativePath="..\..\test\unit\utGenNormals.cpp" > + + @@ -699,6 +703,10 @@ RelativePath="..\..\test\unit\utMaterialSystem.cpp" > + + @@ -733,6 +741,14 @@ RelativePath="..\..\test\unit\utGenNormals.h" > + + + + @@ -741,6 +757,10 @@ RelativePath="..\..\test\unit\utMaterialSystem.h" > + + diff --git a/workspaces/vc8/assimp.vcproj b/workspaces/vc8/assimp.vcproj index 139cc9424..df8a9c088 100644 --- a/workspaces/vc8/assimp.vcproj +++ b/workspaces/vc8/assimp.vcproj @@ -734,6 +734,10 @@ RelativePath="..\..\code\FixNormalsStep.h" > + + @@ -770,6 +774,10 @@ RelativePath="..\..\code\MaterialSystem.h" > + + @@ -790,6 +798,18 @@ RelativePath="..\..\code\RemoveRedundantMaterials.h" > + + + + + + @@ -868,10 +888,6 @@ RelativePath="..\..\code\3DSLoader.h" > - - + + + + + + @@ -1150,6 +1178,10 @@ RelativePath="..\..\code\RemoveRedundantMaterials.cpp" > + + @@ -1196,18 +1228,10 @@ RelativePath="..\..\code\3DSConverter.cpp" > - - - - + + + + - - - - - - - - @@ -262,6 +246,26 @@ RelativePath="..\..\port\jAssimp\jni_bridge\JNILogger.h" > + + + + + + + + + +