From 2da2835b29355a0b8f077fee466aba7a4148c1e1 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Sun, 19 Oct 2008 11:32:33 +0000 Subject: [PATCH] Fixed bug in the AC loader causing lines to load incorrectly. Seems to work now. Added some more essential includes to AssimpPCH.hAdded support for line and point meshes to most steps - I did nto yet adapt all unit tests, so meshes with mixed primitive types are not absolutely safe at the moment. Added camera and light support to the PretransformVert step. Fixed some small inaccuracies and fixed a bug reported by Mark Sibly causing all transformations to be invalid. However the step is nto yet completely correct, there are still some small artifacts. Updated light and camera data structures, added temporary validation code for the Renamed AI_SCENE_FLAGS_ANIM_SKELETON_ONLY to a more generic AI_SCENE_FLAGS_INCOMPLETE flag. Fixed bug in the OFF loader causing meshes with polygons to crash Added line support to the DXF loader - seems to fail for the moment cause of SortByPType. Added support for lights and cameras to NFF, implemented another NFF format subtype (file starts with 'nff'). Implemented NFF 'tpp' chunk and a corresponding texture extension. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@185 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/ACLoader.cpp | 31 +- code/ACLoader.h | 2 +- code/AssimpPCH.h | 81 +- code/BaseImporter.h | 2 +- code/DXFLoader.cpp | 40 +- code/DXFLoader.h | 5 +- code/FindInvalidDataProcess.cpp | 9 +- code/GenFaceNormalsProcess.cpp | 22 +- code/GenVertexNormalsProcess.cpp | 39 +- code/Importer.cpp | 182 +++-- code/ImproveCacheLocality.cpp | 35 +- code/JoinVerticesProcess.cpp | 2 + code/MD2Loader.cpp | 17 +- code/MD2Loader.h | 4 +- code/MD5Loader.cpp | 2 +- code/MaterialSystem.cpp | 21 +- code/MaterialSystem.h | 6 + code/NFFLoader.cpp | 757 +++++++++++++----- code/NFFLoader.h | 54 +- code/OFFLoader.cpp | 34 +- code/PretransformVertices.cpp | 190 ++++- code/RemoveComments.cpp | 25 +- code/RemoveVCProcess.cpp | 48 +- code/SMDLoader.cpp | 8 +- ...{FaceEdgeAdjacency.h => SceneCombiner.cpp} | 0 code/SceneCombiner.h | 0 code/SortByPTypeProcess.cpp | 161 ++-- code/SortByPTypeProcess.h | 7 + code/StreamReader.h | 6 +- code/ValidateDataStructure.cpp | 410 ++++++---- code/ValidateDataStructure.h | 33 +- code/fast_atof.h | 2 +- code/makefile.mingw | 2 +- include/BoostWorkaround/boost/multi_array.hpp | 0 include/aiCamera.h | 35 +- include/aiConfig.h | 30 +- include/aiDefines.h | 1 + include/aiLight.h | 47 +- include/aiMaterial.h | 14 + include/aiMatrix4x4.inl | 14 +- include/aiMesh.h | 48 +- include/aiScene.h | 182 ++++- include/aiTexture.h | 36 +- include/aiTypes.h | 32 +- tools/assimp_view/assimp_view.cpp | 3 +- workspaces/vc8/UnitTest.vcproj | 8 + workspaces/vc8/assimp.vcproj | 8 + 47 files changed, 1886 insertions(+), 809 deletions(-) rename code/{FaceEdgeAdjacency.h => SceneCombiner.cpp} (100%) create mode 100644 code/SceneCombiner.h create mode 100644 include/BoostWorkaround/boost/multi_array.hpp diff --git a/code/ACLoader.cpp b/code/ACLoader.cpp index 1dcd7af88..558d2351f 100644 --- a/code/ACLoader.cpp +++ b/code/ACLoader.cpp @@ -126,9 +126,13 @@ bool AC3DImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) cons if( pos == std::string::npos)return false; std::string extension = pFile.substr( pos); - return !(extension.length() != 3 || extension[0] != '.' || - extension[1] != 'a' && extension[1] != 'A' || - extension[2] != 'c' && extension[2] != 'C'); + for( std::string::iterator it = extension.begin(); it != extension.end(); ++it) + *it = tolower( *it); + + if( extension == ".ac" || extension == "ac") + return true; + + return false; } // ------------------------------------------------------------------------------------------------ @@ -308,9 +312,16 @@ void AC3DImporter::ConvertMaterial(const Object& object, matDest.AddProperty(&matSrc.emis,1,AI_MATKEY_COLOR_EMISSIVE); matDest.AddProperty(&matSrc.spec,1,AI_MATKEY_COLOR_SPECULAR); - float f = 1.f - matSrc.trans; + int n; + if (matSrc.shin) + { + n = aiShadingMode_Phong; + matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); + } + else n = aiShadingMode_Gouraud; + matDest.AddProperty(&n,1,AI_MATKEY_SHADING_MODEL); - matDest.AddProperty(&matSrc.shin,1,AI_MATKEY_SHININESS); + float f = 1.f - matSrc.trans; matDest.AddProperty(&f,1,AI_MATKEY_OPACITY); } @@ -352,6 +363,13 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, faces->mIndices = new unsigned int[1]; faces->mIndices[0] = i; } + + // use the primary material in this case. this should be the + // default material if all objects of the file contain points + // and no faces. + mesh->mMaterialIndex = 0; + outMaterials.push_back(new MaterialHelper()); + ConvertMaterial(object, materials[0], *outMaterials.back()); } else { @@ -501,7 +519,7 @@ aiNode* AC3DImporter::ConvertObjectSection(Object& object, face.mNumIndices = 2; face.mIndices = new unsigned int[2]; face.mIndices[0] = cur++; - face.mIndices[1] = cur; + face.mIndices[1] = cur++; // copy vertex positions *vertices++ = object.vertices[(*it2).first]; @@ -660,6 +678,7 @@ void AC3DImporter::InternReadFile( const std::string& pFile, if (1 != rootObjects.size())delete root; // build output arrays + ai_assert(!meshes.empty()); pScene->mNumMeshes = (unsigned int)meshes.size(); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; ::memcpy(pScene->mMeshes,&meshes[0],pScene->mNumMeshes*sizeof(void*)); diff --git a/code/ACLoader.h b/code/ACLoader.h index fb13fbf66..a3ce9e94f 100644 --- a/code/ACLoader.h +++ b/code/ACLoader.h @@ -162,7 +162,7 @@ protected: */ void GetExtensionList(std::string& append) { - append.append("*.ac"); + append.append("*.ac;*.acc"); } // ------------------------------------------------------------------- diff --git a/code/AssimpPCH.h b/code/AssimpPCH.h index 463c75337..b48b1b0f1 100644 --- a/code/AssimpPCH.h +++ b/code/AssimpPCH.h @@ -1,9 +1,72 @@ +/* +--------------------------------------------------------------------------- +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 ASSIMP_PCH_INCLUDED #define ASSIMP_PCH_INCLUDED -// STL headers +// ******************************************************************* +// Print detailled memory allocation statistics? In this case we'll +// need to overload all C++ memory management functions. It is assumed +// that old C routines, such as malloc(), are NOT used in Assimp. +// ******************************************************************* +#ifdef ASSIMP_BUILD_MEMORY_STATISTICS + + void *operator new (size_t); + void operator delete (void *); + void *operator new[] (size_t); + void operator delete[] (void *); + +#endif + +// ******************************************************************* +// If we have at least VC8 some C string manipulation functions +// are mapped to their safe _s counterparts (e.g. _itoa_s). +// ******************************************************************* +# if _MSC_VER >= 1400 && !(defined _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES) +# define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1 +# endif + +// ******************************************************************* +// STL headers - we need quite a lot of them +// ******************************************************************* #include #include #include @@ -16,7 +79,10 @@ #include #include + +// ******************************************************************* // public ASSIMP headers +// ******************************************************************* #include "../include/DefaultLogger.h" #include "../include/IOStream.h" #include "../include/IOSystem.h" @@ -24,21 +90,32 @@ #include "../include/aiPostProcess.h" #include "../include/assimp.hpp" + +// ******************************************************************* // internal headers that are nearly always required +// ******************************************************************* #include "MaterialSystem.h" #include "StringComparison.h" -#include "ByteSwap.h" +#include "StreamReader.h" #include "qnan.h" +// ******************************************************************* // boost headers - take them from the workaround dir if possible +// ******************************************************************* #ifdef ASSIMP_BUILD_BOOST_WORKAROUND # include "../include/BoostWorkaround/boost/scoped_ptr.hpp" # include "../include/BoostWorkaround/boost/format.hpp" +# include "../include/BoostWorkaround/boost/multi_array.hpp" + #else +// NOTE: boost::multi_array is nto yet supported by the workaround +#define AI_BUILD_NO_BVH_IMPORTER + # include # include +# include #endif diff --git a/code/BaseImporter.h b/code/BaseImporter.h index 835f43645..822e1cc6d 100644 --- a/code/BaseImporter.h +++ b/code/BaseImporter.h @@ -178,7 +178,7 @@ protected: * a face is unique. Or the other way round: a vertex index may * not occur twice in a single aiMesh. * - * If the "AnimationSkeletonOnly"-Flag is not set:
+ * If the AI_SCENE_FLAGS_INCOMPLETE-Flag is not set:
* - at least one mesh must be there
* - at least one material must be there
* - there may be no meshes with 0 vertices or faces
diff --git a/code/DXFLoader.cpp b/code/DXFLoader.cpp index 0dff1d753..07b459f32 100644 --- a/code/DXFLoader.cpp +++ b/code/DXFLoader.cpp @@ -246,12 +246,16 @@ void DXFImporter::InternReadFile( const std::string& pFile, { aiFace& face = pMesh->mFaces[i]; - // check whether we need four indices here - if (vp[3] != vp[2]) + // check whether we need four,three or two indices here + if (vp[1] == vp[2]) { - face.mNumIndices = 4; + face.mNumIndices = 2; } - else face.mNumIndices = 3; + else if (vp[3] == vp[2]) + { + face.mNumIndices = 3; + } + else face.mNumIndices = 4; face.mIndices = new unsigned int[face.mNumIndices]; for (unsigned int a = 0; a < face.mNumIndices;++a) @@ -323,7 +327,7 @@ bool DXFImporter::ParseEntities() { if (!groupCode) { - if (!::strcmp(cursor,"3DFACE")) + if (!::strcmp(cursor,"3DFACE") || !::strcmp(cursor,"LINE") || !::strcmp(cursor,"3DLINE")) if (!Parse3DFace()) return false; else bRepeat = true; if (!::strcmp(cursor,"POLYLINE") || !::strcmp(cursor,"LWPOLYLINE")) @@ -524,6 +528,10 @@ bool DXFImporter::Parse3DFace() aiVector3D vip[4]; // -- vectors are initialized to zero aiColor4D clr(g_clrInvalid); + + // this is also used for for parsing line entities + bool bThird = false; + while (GetNextToken()) { switch (groupCode) @@ -556,22 +564,28 @@ bool DXFImporter::Parse3DFace() case 31: vip[1].z = fast_atof(cursor);break; // x position of the third corner - case 12: vip[2].x = fast_atof(cursor);break; + case 12: vip[2].x = fast_atof(cursor); + bThird = true;break; // y position of the third corner - case 22: vip[2].y = -fast_atof(cursor);break; + case 22: vip[2].y = -fast_atof(cursor); + bThird = true;break; // z position of the third corner - case 32: vip[2].z = fast_atof(cursor);break; + case 32: vip[2].z = fast_atof(cursor); + bThird = true;break; // x position of the fourth corner - case 13: vip[3].x = fast_atof(cursor);break; + case 13: vip[3].x = fast_atof(cursor); + bThird = true;break; // y position of the fourth corner - case 23: vip[3].y = -fast_atof(cursor);break; + case 23: vip[3].y = -fast_atof(cursor); + bThird = true;break; // z position of the fourth corner - case 33: vip[3].z = fast_atof(cursor);break; + case 33: vip[3].z = fast_atof(cursor); + bThird = true;break; // color case 62: clr = g_aclrDxfIndexColors[strtol10(cursor) % AI_DXF_NUM_INDEX_COLORS]; break; @@ -579,6 +593,8 @@ bool DXFImporter::Parse3DFace() if (ret)break; } + if (!bThird)vip[2] = vip[1]; + // use a default layer if necessary if (!out)SetDefaultLayer(out); @@ -592,3 +608,5 @@ bool DXFImporter::Parse3DFace() out->vColors.push_back(clr); return ret; } + + diff --git a/code/DXFLoader.h b/code/DXFLoader.h index 901c04d1e..f20cd3f71 100644 --- a/code/DXFLoader.h +++ b/code/DXFLoader.h @@ -74,7 +74,10 @@ protected: char name[4096]; - // face buffer - order is x,y,z,w v1,v2,v3 (w is equal to z if unused) + // face buffer - order is x,y,z v1,v2,v3,v4 + // if v2 = v3: line + // elsif v3 = v2: triangle + // else: polygon std::vector vPositions; std::vector vColors; }; diff --git a/code/FindInvalidDataProcess.cpp b/code/FindInvalidDataProcess.cpp index ab388a577..fc8a5a328 100644 --- a/code/FindInvalidDataProcess.cpp +++ b/code/FindInvalidDataProcess.cpp @@ -91,7 +91,11 @@ void UpdateMeshReferences(aiNode* node, const std::vector& meshMap } // just let the members that are unused, that's much cheaper // than a full array realloc'n'copy party ... - node->mNumMeshes = out; + if(!(node->mNumMeshes = out)) + { + delete[] node->mMeshes; + node->mMeshes = NULL; + } } // recursively update all children for (unsigned int i = 0; i < node->mNumChildren;++i) @@ -173,7 +177,8 @@ inline const char* ValidateArrayContents(const aiVector3D* arr, unsi } if (i && v != arr[i-1])b = true; } - if (!b)return "All vectors are identical"; + if (!b) + return "All vectors are identical"; return NULL; } diff --git a/code/GenFaceNormalsProcess.cpp b/code/GenFaceNormalsProcess.cpp index 63956139f..5ad484729 100644 --- a/code/GenFaceNormalsProcess.cpp +++ b/code/GenFaceNormalsProcess.cpp @@ -97,11 +97,31 @@ bool GenFaceNormalsProcess::GenMeshFaceNormals (aiMesh* pMesh) { if (NULL != pMesh->mNormals)return false; - pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) + { + DefaultLogger::get()->info("Normal vectors are undefined for line and point meshes"); + return false; + } + // allocate an array to hold the output normals + pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + const float qnan = std::numeric_limits::quiet_NaN(); + + // iterate through all faces and compute per-face normals but store + // them per-vertex. for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) + { + // either a point or a line -> no well-defined normal vector + for (unsigned int i = 0;i < face.mNumIndices;++i) + pMesh->mNormals[face.mIndices[i]] = qnan; + continue; + } aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; diff --git a/code/GenVertexNormalsProcess.cpp b/code/GenVertexNormalsProcess.cpp index a9d4072ec..8a3e89a88 100644 --- a/code/GenVertexNormalsProcess.cpp +++ b/code/GenVertexNormalsProcess.cpp @@ -110,10 +110,30 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int { if (NULL != pMesh->mNormals)return false; + // If the mesh consists of lines and/or points but not of + // triangles or higher-order polygons the normal vectors + // are undefined. + if (!(pMesh->mPrimitiveTypes & (aiPrimitiveType_TRIANGLE | aiPrimitiveType_POLYGON))) + { + DefaultLogger::get()->info("Normal vectors are undefined for line and point meshes"); + return false; + } + + // allocate an array to hold the output normals + const float qnan = std::numeric_limits::quiet_NaN(); pMesh->mNormals = new aiVector3D[pMesh->mNumVertices]; + + // compute per-face normals but store them per-vertex for( unsigned int a = 0; a < pMesh->mNumFaces; a++) { const aiFace& face = pMesh->mFaces[a]; + if (face.mNumIndices < 3) + { + // either a point or a line -> no normal vector + for (unsigned int i = 0;i < face.mNumIndices;++i) + pMesh->mNormals[face.mIndices[i]] = qnan; + continue; + } aiVector3D* pV1 = &pMesh->mVertices[face.mIndices[0]]; aiVector3D* pV2 = &pMesh->mVertices[face.mIndices[1]]; @@ -167,9 +187,10 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int aiVector3D pcNor; for (unsigned int a = 0; a < verticesFound.size(); ++a) { - register unsigned int vidx = verticesFound[a]; - pcNor += pMesh->mNormals[vidx]; + const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; + if (is_not_qnan(v.x))pcNor += v; } + pcNor.Normalize(); // write the smoothed normal back to all affected normals @@ -183,7 +204,7 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int } else { - const float fLimit = cos(configMaxAngle); + const float fLimit = ::cos(configMaxAngle); for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { // get all vertices that share this one ... @@ -192,16 +213,18 @@ bool GenVertexNormalsProcess::GenMeshVertexNormals (aiMesh* pMesh, unsigned int aiVector3D pcNor; for (unsigned int a = 0; a < verticesFound.size(); ++a) { - register unsigned int vidx = verticesFound[a]; + const aiVector3D& v = pMesh->mNormals[verticesFound[a]]; // check whether the angle between the two normals is not too large - if (pMesh->mNormals[vidx] * pMesh->mNormals[i] < fLimit) + // HACK: if v.x is qnan the dot product will become qnan, too + // therefore the comparison against fLimit should be false + // in every case. Contact me if you disagree with this assumption + if (v * pMesh->mNormals[i] < fLimit) continue; - pcNor += pMesh->mNormals[vidx]; + pcNor += v; } - pcNor.Normalize(); - pcNew[i] = pcNor; + pcNew[i] = pcNor.Normalize(); } } diff --git a/code/Importer.cpp b/code/Importer.cpp index f2a09e038..5792d210f 100644 --- a/code/Importer.cpp +++ b/code/Importer.cpp @@ -176,10 +176,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # include "SortByPTypeProcess.h" //#endif - - using namespace Assimp; + + // ------------------------------------------------------------------------------------------------ // Constructor. Importer::Importer() : @@ -276,6 +276,9 @@ Importer::Importer() : mPostProcessingSteps.push_back( new RemoveVCProcess()); #endif +#if (!defined AI_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) + mPostProcessingSteps.push_back( new PretransformVertices()); +#endif #if (!defined AI_BUILD_NO_TRIANGULATE_PROCESS) mPostProcessingSteps.push_back( new TriangulateProcess()); #endif @@ -294,9 +297,6 @@ Importer::Importer() : #if (!defined AI_BUILD_NO_OPTIMIZEGRAPH_PROCESS) mPostProcessingSteps.push_back( new OptimizeGraphProcess()); #endif -#if (!defined AI_BUILD_NO_PRETRANSFORMVERTICES_PROCESS) - mPostProcessingSteps.push_back( new PretransformVertices()); -#endif #if (!defined AI_BUILD_NO_FIXINFACINGNORMALS_PROCESS) mPostProcessingSteps.push_back( new FixInfacingNormalsProcess()); #endif @@ -488,105 +488,121 @@ const aiScene* Importer::ReadFile( const std::string& pFile, unsigned int pFlags // validate the flags ai_assert(ValidateFlags(pFlags)); - // check whether this Importer instance has already loaded - // a scene. In this case we need to delete the old one - if (this->mScene) + // put a large try block around everything to catch all std::exception's + // that might be thrown by STL containers or by new(). + // ImportErrorException's are throw by ourselves and caught elsewhere. + try { - delete mScene; - this->mScene = NULL; - } - - // first check if the file is accessable at all - if( !mIOHandler->Exists( pFile)) - { - mErrorString = "Unable to open file \"" + pFile + "\"."; - DefaultLogger::get()->error(mErrorString); - return NULL; - } - - // find an worker class which can handle the file - BaseImporter* imp = NULL; - for( unsigned int a = 0; a < mImporter.size(); a++) - { - if( mImporter[a]->CanRead( pFile, mIOHandler)) + // check whether this Importer instance has already loaded + // a scene. In this case we need to delete the old one + if (this->mScene) { - imp = mImporter[a]; - break; + DefaultLogger::get()->debug("The previous scene has been deleted"); + delete mScene; + this->mScene = NULL; } - } - // put a proper error message if no suitable importer was found - if( !imp) - { - mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; - DefaultLogger::get()->error(mErrorString); - return NULL; - } - - // dispatch the reading to the worker class for this format - imp->SetupProperties( this ); - mScene = imp->ReadFile( pFile, mIOHandler); - - // if successful, apply all active post processing steps to the imported data - if( mScene) - { -#ifdef _DEBUG - if (bExtraVerbose) + // first check if the file is accessable at all + if( !mIOHandler->Exists( pFile)) { + mErrorString = "Unable to open file \"" + pFile + "\"."; + DefaultLogger::get()->error(mErrorString); + return NULL; + } + + // find an worker class which can handle the file + BaseImporter* imp = NULL; + for( unsigned int a = 0; a < mImporter.size(); a++) + { + if( mImporter[a]->CanRead( pFile, mIOHandler)) + { + imp = mImporter[a]; + break; + } + } + + // put a proper error message if no suitable importer was found + if( !imp) + { + mErrorString = "No suitable reader found for the file format of file \"" + pFile + "\"."; + DefaultLogger::get()->error(mErrorString); + return NULL; + } + + // dispatch the reading to the worker class for this format + DefaultLogger::get()->info("Found a matching importer for this file format"); + imp->SetupProperties( this ); + mScene = imp->ReadFile( pFile, mIOHandler); + + // if successful, apply all active post processing steps to the imported data + DefaultLogger::get()->info("Import succesful, entering postprocessing-steps"); + if( mScene) + { +#ifdef _DEBUG + if (bExtraVerbose) + { #if (!defined AI_BUILD_NO_VALIDATEDS_PROCESS) - DefaultLogger::get()->error("Extra verbose mode not available, library" - " wasn't build with the ValidateDS-Step"); + DefaultLogger::get()->error("Extra verbose mode not available, library" + " wasn't build with the ValidateDS-Step"); #endif - pFlags |= aiProcess_ValidateDataStructure; - - // use the MSB to tell the ValidateDS-Step that e're in extra verbose mode - // TODO: temporary solution, clean up later - mScene->mFlags |= 0x80000000; - } -#else - if (bExtraVerbose)DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting"); -#endif // ! DEBUG - for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) - { - BaseProcess* process = mPostProcessingSteps[a]; - if( process->IsActive( pFlags)) - { - process->SetupProperties( this ); - process->ExecuteOnScene ( this ); + pFlags |= aiProcess_ValidateDataStructure; } - if( !mScene)break; +#else + if (bExtraVerbose)DefaultLogger::get()->warn("Not a debug build, ignoring extra verbose setting"); +#endif // ! DEBUG + for( unsigned int a = 0; a < mPostProcessingSteps.size(); a++) + { + BaseProcess* process = mPostProcessingSteps[a]; + if( process->IsActive( pFlags)) + { + process->SetupProperties( this ); + process->ExecuteOnScene ( this ); + } + if( !mScene)break; #ifdef _DEBUG #ifndef AI_BUILD_NO_VALIDATEDS_PROCESS - continue; + continue; #endif - // if the extra verbose mode is active execute the - // VaidateDataStructureStep again after each step - if (bExtraVerbose && a) - { - DefaultLogger::get()->debug("Extra verbose: revalidating data structures"); - ((ValidateDSProcess*)mPostProcessingSteps[0])->ExecuteOnScene (this); - if( !mScene) + // if the extra verbose mode is active execute the + // VaidateDataStructureStep again after each step + if (bExtraVerbose && a) { - DefaultLogger::get()->error("Extra verbose: failed to revalidate data structures"); - break; + DefaultLogger::get()->debug("Extra verbose: revalidating data structures"); + ((ValidateDSProcess*)mPostProcessingSteps[0])->ExecuteOnScene (this); + if( !mScene) + { + DefaultLogger::get()->error("Extra verbose: failed to revalidate data structures"); + break; + } } +#endif // ! DEBUG } -#endif // ! DEBUG } -#ifdef _DEBUG - if (bExtraVerbose)mScene->mFlags &= ~0x80000000; -#endif // ! DEBUG - } - // if failed, extract the error string - else if( !mScene)mErrorString = imp->GetErrorText(); + // if failed, extract the error string + else if( !mScene)mErrorString = imp->GetErrorText(); - // clear any data allocated by post-process steps - mPPShared->Clean(); + // clear any data allocated by post-process steps + mPPShared->Clean(); + + } + catch (std::exception &e) + { +#if (defined _MSC_VER) && (defined _CPPRTTI) && (defined _DEBUG) + + // if we have RTTI get the full name of the exception that occured + mErrorString = std::string(typeid( e ).name()) + ": " + e.what(); +#else + mErrorString = std::string("std::exception: ") + e.what(); +#endif + + DefaultLogger::get()->error(mErrorString); + delete mScene;mScene = NULL; + } // either successful or failure - the pointer expresses it anyways return mScene; diff --git a/code/ImproveCacheLocality.cpp b/code/ImproveCacheLocality.cpp index 21cff2944..81270ad79 100644 --- a/code/ImproveCacheLocality.cpp +++ b/code/ImproveCacheLocality.cpp @@ -84,6 +84,12 @@ bool ImproveCacheLocalityProcess::IsActive( unsigned int pFlags) const // Executes the post processing step on the given imported data. void ImproveCacheLocalityProcess::Execute( aiScene* pScene) { + if (!pScene->mNumMeshes) + { + DefaultLogger::get()->debug("ImproveCacheLocalityProcess skipped; there are no meshes"); + return; + } + DefaultLogger::get()->debug("ImproveCacheLocalityProcess begin"); for( unsigned int a = 0; a < pScene->mNumMeshes; a++) @@ -102,7 +108,14 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN // check whether the input data is valid -> // - there must be vertices and faces (haha) // - all faces must be triangulated - if (!pMesh->HasFaces() || !pMesh->HasPositions())return; + if (!pMesh->HasFaces() || !pMesh->HasPositions()) + return; + + if (pMesh->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) + { + DefaultLogger::get()->error("This algorithm works on triangle meshes only"); + return; + } // find the input ACMR ... unsigned int* piFIFOStack = new unsigned int[this->configCacheDepth]; @@ -116,26 +129,6 @@ void ImproveCacheLocalityProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshN const aiFace* const pcEnd = pMesh->mFaces+pMesh->mNumFaces; for (const aiFace* pcFace = pMesh->mFaces;pcFace != pcEnd;++pcFace) { - if (3 != pcFace->mNumIndices) - { - DefaultLogger::get()->error("Unable to improve cache locality of non-triangulated faces"); - delete[] piFIFOStack; - return; - } - - // although it has not been tested, I'm quite sure degenerated triangles - // would crash if the algorithm was applied to them - -#if (defined _DEBUG) - if (pcFace->mIndices[0] == pcFace->mIndices[1] || - pcFace->mIndices[2] == pcFace->mIndices[1] || - pcFace->mIndices[2] == pcFace->mIndices[0]) - { - DefaultLogger::get()->error("ImproveCacheLocalityProcess: There may be no degenerated triangles "); - return; - } -#endif - for (unsigned int qq = 0; qq < 3;++qq) { bool bInCache = false; diff --git a/code/JoinVerticesProcess.cpp b/code/JoinVerticesProcess.cpp index 0f41e803d..301960e58 100644 --- a/code/JoinVerticesProcess.cpp +++ b/code/JoinVerticesProcess.cpp @@ -109,6 +109,8 @@ void JoinVerticesProcess::Execute( aiScene* pScene) DefaultLogger::get()->info(szBuff); } } + + pScene->mFlags |= AI_SCENE_FLAGS_NON_VERBOSE_FORMAT; } // ------------------------------------------------------------------------------------------------ diff --git a/code/MD2Loader.cpp b/code/MD2Loader.cpp index 2a756d9ac..909bddde6 100644 --- a/code/MD2Loader.cpp +++ b/code/MD2Loader.cpp @@ -183,12 +183,12 @@ void MD2Importer::InternReadFile( const std::string& pFile, if( fileSize < sizeof(MD2::Header)) throw new ImportErrorException( "MD2 File is too small"); - std::vector mBuffer2(fileSize); + std::vector mBuffer2(fileSize); file->Read(&mBuffer2[0], 1, fileSize); mBuffer = &mBuffer2[0]; - m_pcHeader = (const MD2::Header*)mBuffer; + m_pcHeader = (BE_NCONST MD2::Header*)mBuffer; #ifdef AI_BUILD_BIG_ENDIAN @@ -229,7 +229,7 @@ void MD2Importer::InternReadFile( const std::string& pFile, pcMesh->mPrimitiveTypes = aiPrimitiveType_TRIANGLE; // navigate to the begin of the frame data - const MD2::Frame* pcFrame = (const MD2::Frame*) ((uint8_t*) + BE_NCONST MD2::Frame* pcFrame = (BE_NCONST MD2::Frame*) ((uint8_t*) m_pcHeader + m_pcHeader->offsetFrames); pcFrame += configFrameID; @@ -239,11 +239,11 @@ void MD2Importer::InternReadFile( const std::string& pFile, m_pcHeader + m_pcHeader->offsetTriangles); // navigate to the begin of the tex coords data - const MD2::TexCoord* pcTexCoords = (const MD2::TexCoord*) ((uint8_t*) + BE_NCONST MD2::TexCoord* pcTexCoords = (BE_NCONST MD2::TexCoord*) ((uint8_t*) m_pcHeader + m_pcHeader->offsetTexCoords); // navigate to the begin of the vertex data - const MD2::Vertex* pcVerts = (const MD2::Vertex*) (pcFrame->vertices); + BE_NCONST MD2::Vertex* pcVerts = (BE_NCONST MD2::Vertex*) (pcFrame->vertices); #ifdef AI_BUILD_BIG_ENDIAN for (uint32_t i = 0; i< m_pcHeader->numTriangles) @@ -410,14 +410,11 @@ void MD2Importer::InternReadFile( const std::string& pFile, } 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 = pcTexCoords[iIndex].s / fDivisorU; - v = pcTexCoords[iIndex].t / fDivisorV; - pcOut.x = u; - pcOut.y = 1.0f - v; // FIXME: Is this correct for MD2? + pcOut.x = pcTexCoords[iIndex].s / fDivisorU; + pcOut.y = 1.f- pcTexCoords[iIndex].t / fDivisorV; } } // FIX: flip the face order for use with OpenGL diff --git a/code/MD2Loader.h b/code/MD2Loader.h index b0c38073e..faeaeb8e2 100644 --- a/code/MD2Loader.h +++ b/code/MD2Loader.h @@ -113,10 +113,10 @@ protected: unsigned int configFrameID; /** Header of the MD2 file */ - const MD2::Header* m_pcHeader; + BE_NCONST MD2::Header* m_pcHeader; /** Buffer to hold the loaded file */ - const unsigned char* mBuffer; + BE_NCONST uint8_t* mBuffer; /** Size of the file, in bytes */ unsigned int fileSize; diff --git a/code/MD5Loader.cpp b/code/MD5Loader.cpp index 9944a81de..799e7d2e3 100644 --- a/code/MD5Loader.cpp +++ b/code/MD5Loader.cpp @@ -107,7 +107,7 @@ void MD5Importer::InternReadFile( if (!bHadMD5Mesh && !bHadMD5Anim) throw new ImportErrorException("Failed to read valid data from this MD5"); - if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY; + if (!bHadMD5Mesh)pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; } // ------------------------------------------------------------------------------------------------ void MD5Importer::LoadFileIntoMemory (IOStream* file) diff --git a/code/MaterialSystem.cpp b/code/MaterialSystem.cpp index 480cc6aee..da512dc1c 100644 --- a/code/MaterialSystem.cpp +++ b/code/MaterialSystem.cpp @@ -242,16 +242,16 @@ MaterialHelper::MaterialHelper() // ------------------------------------------------------------------------------------------------ MaterialHelper::~MaterialHelper() { - for (unsigned int i = 0; i < this->mNumProperties;++i) + Clear(); +} +// ------------------------------------------------------------------------------------------------ +void MaterialHelper::Clear() +{ + for (unsigned int i = 0; i < mNumProperties;++i) { - // be careful ... - if(NULL != this->mProperties[i]) - { - delete[] this->mProperties[i]->mData; - delete this->mProperties[i]; - } + // delete this entry + delete mProperties[i]; } - return; } // ------------------------------------------------------------------------------------------------ uint32_t MaterialHelper::ComputeHash() @@ -282,7 +282,6 @@ aiReturn MaterialHelper::RemoveProperty (const char* pKey) if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey.data, pKey )) { // delete this entry - delete[] this->mProperties[i]->mData; delete this->mProperties[i]; // collapse the array behind --. @@ -318,7 +317,6 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput, if (0 == ASSIMP_stricmp( this->mProperties[i]->mKey.data, pKey )) { // delete this entry - delete[] this->mProperties[i]->mData; delete this->mProperties[i]; iOutIndex = i; } @@ -402,8 +400,7 @@ void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest, for (unsigned int q = 0; q < iOldNum;++q) { prop = pcDest->mProperties[q]; - if (propSrc->mKey.length == prop->mKey.length && - !ASSIMP_stricmp(propSrc->mKey.data,prop->mKey.data)) + if (!ASSIMP_stricmp(propSrc->mKey.data,prop->mKey.data)) { delete prop; diff --git a/code/MaterialSystem.h b/code/MaterialSystem.h index e9ca949cb..5a92b210e 100644 --- a/code/MaterialSystem.h +++ b/code/MaterialSystem.h @@ -105,6 +105,12 @@ public: aiReturn RemoveProperty (const char* pKey); + // ------------------------------------------------------------------- + /** Removes all properties from the material + */ + void Clear(); + + // ------------------------------------------------------------------- /** Computes a hash (hopefully unique) from all material properties * The hash value must be updated after material properties have diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index d074f6193..030bac373 100644 --- a/code/NFFLoader.cpp +++ b/code/NFFLoader.cpp @@ -74,10 +74,19 @@ bool NFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const if( pos == std::string::npos)return false; std::string extension = pFile.substr( pos); - return !(extension.length() != 4 || extension[0] != '.' || - extension[1] != 'n' && extension[1] != 'N' || - extension[2] != 'f' && extension[2] != 'F' || - extension[3] != 'f' && extension[3] != 'F'); + // extensions: enff and nff + if (!extension.length() || extension[0] != '.')return false; + if (extension.length() == 4) + { + return !(extension[1] != 'n' && extension[1] != 'N' || + extension[2] != 'f' && extension[2] != 'F' || + extension[3] != 'f' && extension[3] != 'F'); + } + else return !( extension.length() != 5 || + extension[1] != 'e' && extension[1] != 'E' || + extension[2] != 'n' && extension[2] != 'N' || + extension[3] != 'f' && extension[3] != 'F' || + extension[4] != 'f' && extension[4] != 'F'); } // ------------------------------------------------------------------------------------------------ @@ -87,9 +96,9 @@ bool NFFImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const // ------------------------------------------------------------------------------------------------ #define AI_NFF_PARSE_TRIPLE(v) \ - AI_NFF_PARSE_FLOAT(v.x) \ - AI_NFF_PARSE_FLOAT(v.y) \ - AI_NFF_PARSE_FLOAT(v.z) + AI_NFF_PARSE_FLOAT(v[0]) \ + AI_NFF_PARSE_FLOAT(v[1]) \ + AI_NFF_PARSE_FLOAT(v[2]) // ------------------------------------------------------------------------------------------------ #define AI_NFF_PARSE_SHAPE_INFORMATION() \ @@ -125,252 +134,504 @@ void NFFImporter::InternReadFile( const std::string& pFile, // the pointers below easier. std::vector meshes; std::vector meshesWithNormals; + std::vector meshesWithUVCoords; std::vector meshesLocked; + + char line[4096]; + const char* sz; + + // camera parameters + aiVector3D camPos, camUp(0.f,1.f,0.f), camLookAt(0.f,0.f,1.f); + float angle; + aiVector2D resolution; + + bool hasCam = false; + MeshInfo* currentMeshWithNormals = NULL; MeshInfo* currentMesh = NULL; + MeshInfo* currentMeshWithUVCoords = NULL; ShadingInfo s; // current material info // degree of tesselation unsigned int iTesselation = 4; - char line[4096]; - const char* sz; - unsigned int sphere = 0,cylinder = 0,cone = 0,numNamed = 0, - dodecahedron = 0,octahedron = 0,tetrahedron = 0, hexahedron = 0; + // some temporary variables we need to parse the file + unsigned int sphere = 0, + cylinder = 0, + cone = 0, + numNamed = 0, + dodecahedron = 0, + octahedron = 0, + tetrahedron = 0, + hexahedron = 0; - while (GetNextLine(buffer,line)) + // lights imported from the file + std::vector lights; + + // check whether this is the NFF2 file format + if (TokenMatch(buffer,"nff",3)) { - if ('p' == line[0]) + // another NFF file format ... just a raw parser has been implemented + // no support for textures yet, I don't think it is worth the effort + // http://ozviz.wasp.uwa.edu.au/~pbourke/dataformats/nff/nff2.html + + while (GetNextLine(buffer,line)) { - MeshInfo* out = NULL; - // 'pp' - polygon patch primitive - if ('p' == line[1]) + sz = line; + if (TokenMatch(sz,"version",7)) { - if (meshesWithNormals.empty()) - { - meshesWithNormals.push_back(MeshInfo(true)); - currentMeshWithNormals = &meshesWithNormals.back(); - } - - sz = &line[2];out = currentMeshWithNormals; + DefaultLogger::get()->info("NFF (alt.) file format: " + std::string(sz)); } - // 'p' - polygon primitive - else + else if (TokenMatch(sz,"viewpos",7)) { - if (meshes.empty()) - { - meshes.push_back(MeshInfo(false)); - currentMesh = &meshes.back(); - } - sz = &line[1];out = currentMesh; + AI_NFF_PARSE_TRIPLE(camPos); + hasCam = true; } - SkipSpaces(sz,&sz); - m = strtol10(sz); - - // ---- flip the face order - out->vertices.resize(out->vertices.size()+m); - if (out == currentMeshWithNormals) + else if (TokenMatch(sz,"viewdir",7)) { - out->normals.resize(out->vertices.size()); + AI_NFF_PARSE_TRIPLE(camLookAt); + hasCam = true; } - for (unsigned int n = 0; n < m;++n) + else if (TokenMatch(sz,"//",2)) { - if(!GetNextLine(buffer,line)) - { - DefaultLogger::get()->error("NFF: Unexpected EOF was encountered"); - continue; - } + // comment ... + DefaultLogger::get()->info(sz); + } + else if (!IsSpace(*sz)) + { + // must be a new object + meshes.push_back(MeshInfo(PatchType_Simple)); + MeshInfo& mesh = meshes.back(); - aiVector3D v; sz = &line[0]; - AI_NFF_PARSE_TRIPLE(v); - out->vertices[out->vertices.size()-n-1] = v; + if (!GetNextLine(buffer,line)) + {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read number of vertices");break;} - if (out == currentMeshWithNormals) + SkipSpaces(line,&sz); + unsigned int num = ::strtol10(sz,&sz); + + std::vector tempPositions; + std::vector outPositions; + mesh.vertices.reserve(num*3); + mesh.colors.reserve (num*3); + tempPositions.reserve(num); + for (unsigned int i = 0; i < num; ++i) { + if (!GetNextLine(buffer,line)) + {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read vertices");break;} + + sz = line; + aiVector3D v; AI_NFF_PARSE_TRIPLE(v); - out->normals[out->vertices.size()-n-1] = v; + tempPositions.push_back(v); } - } - out->faces.push_back(m); - } - // 'f' - shading information block - else if ('f' == line[0] && IsSpace(line[1])) - { - SkipSpaces(&line[1],&sz); + if (!GetNextLine(buffer,line)) + {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read number of faces");break;} - // read just the RGB colors, the rest is ignored for the moment - sz = fast_atof_move(sz, (float&)s.color.r); - SkipSpaces(&sz); - sz = fast_atof_move(sz, (float&)s.color.g); - SkipSpaces(&sz); - sz = fast_atof_move(sz, (float&)s.color.b); + if (!num)throw new ImportErrorException("NFF2: There are zero vertices"); - // check whether we have this material already - - // although we have the RRM-Step, this is necessary here. - // otherwise we would generate hundreds of small meshes - // with just a few faces - this is surely never wanted. - currentMesh = currentMeshWithNormals = NULL; - for (std::vector::iterator it = meshes.begin(), end = meshes.end(); - it != end;++it) - { - if ((*it).bLocked)continue; - if ((*it).shader == s) + SkipSpaces(line,&sz); + num = ::strtol10(sz,&sz); + mesh.faces.reserve(num); + + for (unsigned int i = 0; i < num; ++i) { - if ((*it).bHasNormals)currentMeshWithNormals = &(*it); - else currentMesh = &(*it); + if (!GetNextLine(buffer,line)) + {DefaultLogger::get()->warn("NFF2: Unexpected EOF, can't read faces");break;} + + SkipSpaces(line,&sz); + unsigned int idx, numIdx = ::strtol10(sz,&sz); + if (numIdx) + { + mesh.faces.push_back(numIdx); + for (unsigned int a = 0; a < numIdx;++a) + { + SkipSpaces(sz,&sz); + idx = ::strtol10(sz,&sz); + if (idx >= (unsigned int)tempPositions.size()) + { + DefaultLogger::get()->error("NFF2: Index overflow"); + idx = 0; + } + mesh.vertices.push_back(tempPositions[idx]); + } + } + + SkipSpaces(sz,&sz); + idx = ::strtol_cppstyle(sz,&sz); + aiColor4D clr; + clr.r = ((numIdx >> 8u) & 0xf) / 16.f; + clr.g = ((numIdx >> 4u) & 0xf) / 16.f; + clr.b = ((numIdx) & 0xf) / 16.f; + clr.a = 1.f; + for (unsigned int a = 0; a < numIdx;++a) + mesh.colors.push_back(clr); + } + if (!num)throw new ImportErrorException("NFF2: There are zero faces"); + } + } + camLookAt = camLookAt + camPos; + } + else // "Normal" Neutral file format that is quite more common + { + while (GetNextLine(buffer,line)) + { + sz = line; + if ('p' == line[0] || TokenMatch(sz,"tpp",3)) + { + MeshInfo* out = NULL; + + // 'tpp' - texture polygon patch primitive + if ('t' == line[0]) + { + if (meshesWithUVCoords.empty()) + { + meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals)); + currentMeshWithUVCoords = &meshesWithUVCoords.back(); + } + + out = currentMeshWithUVCoords; + } + // 'pp' - polygon patch primitive + else if ('p' == line[1]) + { + if (meshesWithNormals.empty()) + { + meshesWithNormals.push_back(MeshInfo(PatchType_Normals)); + currentMeshWithNormals = &meshesWithNormals.back(); + } + + sz = &line[2];out = currentMeshWithNormals; + } + // 'p' - polygon primitive + else + { + if (meshes.empty()) + { + meshes.push_back(MeshInfo(PatchType_Simple)); + currentMesh = &meshes.back(); + } + sz = &line[1];out = currentMesh; + } + SkipSpaces(sz,&sz); + m = strtol10(sz); + + // ---- flip the face order + out->vertices.resize(out->vertices.size()+m); + if (out != currentMesh) + { + out->normals.resize(out->vertices.size()); + } + if (out == currentMeshWithUVCoords) + { + out->uvs.resize(out->vertices.size()); + } + for (unsigned int n = 0; n < m;++n) + { + if(!GetNextLine(buffer,line)) + { + DefaultLogger::get()->error("NFF: Unexpected EOF was encountered"); + continue; + } + + aiVector3D v; sz = &line[0]; + AI_NFF_PARSE_TRIPLE(v); + out->vertices[out->vertices.size()-n-1] = v; + + if (out != currentMesh) + { + AI_NFF_PARSE_TRIPLE(v); + out->normals[out->vertices.size()-n-1] = v; + } + if (out == currentMeshWithUVCoords) + { + // FIX: in one test file this wraps over multiple lines + SkipSpaces(&sz); + if (IsLineEnd(*sz)) + { + GetNextLine(buffer,line); + sz = line; + } + AI_NFF_PARSE_FLOAT(v.x); + SkipSpaces(&sz); + if (IsLineEnd(*sz)) + { + GetNextLine(buffer,line); + sz = line; + } + AI_NFF_PARSE_FLOAT(v.y); + v.y = 1.f - v.y; + out->uvs[out->vertices.size()-n-1] = v; + } + } + out->faces.push_back(m); + } + // 'f' - shading information block + else if (TokenMatch(sz,"f",1)) + { + float d; + + // read the RGB colors + AI_NFF_PARSE_TRIPLE(s.color); + + // read the other properties + AI_NFF_PARSE_FLOAT(s.diffuse); + AI_NFF_PARSE_FLOAT(s.specular); + AI_NFF_PARSE_FLOAT(d); // skip shininess and transmittance + AI_NFF_PARSE_FLOAT(d); + AI_NFF_PARSE_FLOAT(s.refracti); + + // if the next one is NOT a number we assume it is a texture file name + // this feature is used by some NFF files on the internet and it has + // been implemented as it can be really useful + SkipSpaces(&sz); + if (!IsNumeric(*sz)) + { + // TODO: Support full file names with spaces and quotation marks ... + const char* p = sz; + while (!IsSpaceOrNewLine( *sz ))++sz; + + unsigned int diff = (unsigned int)(sz-p); + if (diff) + { + s.texFile = std::string(p,diff); + } + } + else + { + AI_NFF_PARSE_FLOAT(s.ambient); // optional + } + + // check whether we have this material already - + // although we have the RRM-Step, this is necessary here. + // otherwise we would generate hundreds of small meshes + // with just a few faces - this is surely never wanted. + currentMesh = currentMeshWithNormals = currentMeshWithUVCoords = NULL; + for (std::vector::iterator it = meshes.begin(), end = meshes.end(); + it != end;++it) + { + if ((*it).bLocked)continue; + if ((*it).shader == s) + { + switch ((*it).pType) + { + case PatchType_Normals: + currentMeshWithNormals = &(*it); + break; + + case PatchType_Simple: + currentMesh = &(*it); + break; + + default: + currentMeshWithUVCoords = &(*it); + break; + }; + } + } + + if (!currentMesh) + { + meshes.push_back(MeshInfo(PatchType_Simple)); + currentMesh = &meshes.back(); + currentMesh->shader = s; + } + + if (!currentMeshWithNormals) + { + meshesWithNormals.push_back(MeshInfo(PatchType_Normals)); + currentMeshWithNormals = &meshesWithNormals.back(); + currentMeshWithNormals->shader = s; + } + + if (!currentMeshWithUVCoords) + { + meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals)); + currentMeshWithUVCoords = &meshesWithUVCoords.back(); + currentMeshWithUVCoords->shader = s; } } - - if (!currentMesh) + // 'l' - light source + else if (TokenMatch(sz,"l",1)) { - meshes.push_back(MeshInfo(false)); - currentMesh = &meshes.back(); - currentMesh->shader = s; + lights.push_back(Light()); + Light& light = lights.back(); + + AI_NFF_PARSE_TRIPLE(light.position); + AI_NFF_PARSE_FLOAT (light.intensity); + AI_NFF_PARSE_TRIPLE(light.color); + } + // 's' - sphere + else if (TokenMatch(sz,"s",1)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + + AI_NFF_PARSE_SHAPE_INFORMATION(); + + // we don't need scaling or translation here - we do it in the node's transform + StandardShapes::MakeSphere(iTesselation, currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::sprintf(currentMesh.name,"sphere_%i",sphere++); + } + // 'dod' - dodecahedron + else if (TokenMatch(sz,"dod",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + + AI_NFF_PARSE_SHAPE_INFORMATION(); + + // we don't need scaling or translation here - we do it in the node's transform + StandardShapes::MakeDodecahedron(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++); } - if (!currentMeshWithNormals) + // 'oct' - octahedron + else if (TokenMatch(sz,"oct",3)) { - meshesWithNormals.push_back(MeshInfo(true)); - currentMeshWithNormals = &meshesWithNormals.back(); - currentMeshWithNormals->shader = s; + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; + + AI_NFF_PARSE_SHAPE_INFORMATION(); + + // we don't need scaling or translation here - we do it in the node's transform + StandardShapes::MakeOctahedron(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + + // generate a name for the mesh + ::sprintf(currentMesh.name,"octahedron_%i",octahedron++); } - } - // 's' - sphere - else if ('s' == line[0] && IsSpace(line[1])) - { - meshesLocked.push_back(MeshInfo(false,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - sz = &line[1]; - AI_NFF_PARSE_SHAPE_INFORMATION(); + // 'tet' - tetrahedron + else if (TokenMatch(sz,"tet",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeSphere(iTesselation, currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + AI_NFF_PARSE_SHAPE_INFORMATION(); - // generate a name for the mesh - ::sprintf(currentMesh.name,"sphere_%i",sphere++); - } - // 'dod' - dodecahedron - else if (!strncmp(line,"dod",3) && IsSpace(line[3])) - { - meshesLocked.push_back(MeshInfo(false,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; + // we don't need scaling or translation here - we do it in the node's transform + StandardShapes::MakeTetrahedron(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - sz = &line[4]; - AI_NFF_PARSE_SHAPE_INFORMATION(); + // generate a name for the mesh + ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++); + } - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeDodecahedron(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + // 'hex' - hexahedron + else if (TokenMatch(sz,"hex",3)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshesLocked.back(); + currentMesh.shader = s; - // generate a name for the mesh - ::sprintf(currentMesh.name,"dodecahedron_%i",dodecahedron++); - } + AI_NFF_PARSE_SHAPE_INFORMATION(); - // 'oct' - octahedron - else if (!strncmp(line,"oct",3) && IsSpace(line[3])) - { - meshesLocked.push_back(MeshInfo(false,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; + // we don't need scaling or translation here - we do it in the node's transform + StandardShapes::MakeHexahedron(currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - sz = &line[4]; - AI_NFF_PARSE_SHAPE_INFORMATION(); + // generate a name for the mesh + ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++); + } + // 'c' - cone + else if (TokenMatch(sz,"c",1)) + { + meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); + MeshInfo& currentMesh = meshes.back(); + currentMesh.shader = s; - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeOctahedron(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); + aiVector3D center1, center2; float radius1, radius2; + AI_NFF_PARSE_TRIPLE(center1); + AI_NFF_PARSE_FLOAT(radius1); + AI_NFF_PARSE_TRIPLE(center2); + AI_NFF_PARSE_FLOAT(radius2); - // generate a name for the mesh - ::sprintf(currentMesh.name,"octahedron_%i",octahedron++); - } + // compute the center point of the cone/cylinder + center2 = (center2-center1)/2.f; + currentMesh.center = center1+center2; + center1 = -center2; - // 'tet' - tetrahedron - else if (!strncmp(line,"tet",3) && IsSpace(line[3])) - { - meshesLocked.push_back(MeshInfo(false,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; + // generate the cone - it consists of simple triangles + StandardShapes::MakeCone(center1, radius1, center2, radius2, iTesselation, currentMesh.vertices); + currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - sz = &line[4]; - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeTetrahedron(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"tetrahedron_%i",tetrahedron++); - } - - // 'hex' - hexahedron - else if (!strncmp(line,"hex",3) && IsSpace(line[3])) - { - meshesLocked.push_back(MeshInfo(false,true)); - MeshInfo& currentMesh = meshesLocked.back(); - currentMesh.shader = s; - - sz = &line[4]; - AI_NFF_PARSE_SHAPE_INFORMATION(); - - // we don't need scaling or translation here - we do it in the node's transform - StandardShapes::MakeHexahedron(currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - ::sprintf(currentMesh.name,"hexahedron_%i",hexahedron++); - } - - // 'tess' - tesselation - else if (!strncmp(line,"tess",4) && IsSpace(line[4])) - { - sz = &line[5];SkipSpaces(&sz); - iTesselation = strtol10(sz); - } - // 'c' - cone - else if ('c' == line[0] && IsSpace(line[1])) - { - meshesLocked.push_back(MeshInfo(false,true)); - MeshInfo& currentMesh = meshes.back(); - currentMesh.shader = s; - - sz = &line[1]; - aiVector3D center1, center2; float radius1, radius2; - AI_NFF_PARSE_TRIPLE(center1); - AI_NFF_PARSE_FLOAT(radius1); - AI_NFF_PARSE_TRIPLE(center2); - AI_NFF_PARSE_FLOAT(radius2); - - // compute the center point of the cone/cylinder - center2 = (center2-center1)/2.f; - currentMesh.center = center1+center2; - center1 = -center2; - - // generate the cone - it consists of simple triangles - StandardShapes::MakeCone(center1, radius1, center2, radius2, iTesselation, currentMesh.vertices); - currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - - // generate a name for the mesh - if (radius1 != radius2) - ::sprintf(currentMesh.name,"cone_%i",cone++); - else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++); - } - // '#' - comment - else if ('#' == line[0]) - { - const char* sz;SkipSpaces(&line[1],&sz); - if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz); + // generate a name for the mesh + if (radius1 != radius2) + ::sprintf(currentMesh.name,"cone_%i",cone++); + else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++); + } + // 'tess' - tesselation + else if (TokenMatch(sz,"tess",4)) + { + SkipSpaces(&sz); + iTesselation = strtol10(sz); + } + // 'from' - camera position + else if (TokenMatch(sz,"from",4)) + { + AI_NFF_PARSE_TRIPLE(camPos); + hasCam = true; + } + // 'at' - camera look-at vector + else if (TokenMatch(sz,"at",2)) + { + AI_NFF_PARSE_TRIPLE(camLookAt); + hasCam = true; + } + // 'up' - camera up vector + else if (TokenMatch(sz,"up",2)) + { + AI_NFF_PARSE_TRIPLE(camUp); + hasCam = true; + } + // 'angle' - (half?) camera field of view + else if (TokenMatch(sz,"angle",5)) + { + AI_NFF_PARSE_FLOAT(angle); + hasCam = true; + } + // 'resolution' - used to compute the screen aspect + else if (TokenMatch(sz,"resolution",10)) + { + AI_NFF_PARSE_FLOAT(resolution.x); + AI_NFF_PARSE_FLOAT(resolution.y); + hasCam = true; + } + // 'pb' - bezier patch. Not supported yet + else if (TokenMatch(sz,"pb",2)) + { + DefaultLogger::get()->error("NFF: Encountered unsupported ID: bezier patch"); + } + // 'pn' - NURBS. Not supported yet + else if (TokenMatch(sz,"pn",2) || TokenMatch(sz,"pnn",3)) + { + DefaultLogger::get()->error("NFF: Encountered unsupported ID: NURBS"); + } + // '' - comment + else if ('#' == line[0]) + { + const char* sz;SkipSpaces(&line[1],&sz); + if (!IsLineEnd(*sz))DefaultLogger::get()->info(sz); + } } } // copy all arrays into one large - meshes.reserve(meshes.size()+meshesLocked.size()+meshesWithNormals.size()); - meshes.insert(meshes.end(),meshesLocked.begin(),meshesLocked.end()); - meshes.insert(meshes.end(),meshesWithNormals.begin(),meshesWithNormals.end()); + meshes.reserve (meshes.size()+meshesLocked.size()+meshesWithNormals.size()+meshesWithUVCoords.size()); + meshes.insert (meshes.end(),meshesLocked.begin(),meshesLocked.end()); + meshes.insert (meshes.end(),meshesWithNormals.begin(),meshesWithNormals.end()); + meshes.insert (meshes.end(),meshesWithUVCoords.begin(),meshesWithUVCoords.end()); // now generate output meshes. first find out how many meshes we'll need std::vector::const_iterator it = meshes.begin(), end = meshes.end(); @@ -388,7 +649,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, // sub nodes for named objects such as spheres and cones. aiNode* const root = new aiNode(); root->mName.Set(""); - root->mNumChildren = numNamed; + root->mNumChildren = numNamed + (hasCam ? 1 : 0) + (unsigned int) lights.size(); root->mNumMeshes = pScene->mNumMeshes-numNamed; aiNode** ppcChildren; @@ -398,6 +659,49 @@ void NFFImporter::InternReadFile( const std::string& pFile, if (root->mNumChildren) ppcChildren = root->mChildren = new aiNode*[root->mNumChildren]; + // generate the camera + if (hasCam) + { + aiNode* nd = *ppcChildren = new aiNode(); + nd->mName.Set(""); + nd->mParent = root; + + // allocate the camera in the scene + pScene->mNumCameras = 1; + pScene->mCameras = new aiCamera*[1]; + aiCamera* c = pScene->mCameras[0] = new aiCamera; + + c->mName = nd->mName; // make sure the names are identical + c->mHorizontalFOV = AI_DEG_TO_RAD( angle ); + c->mLookAt = camLookAt - camPos; + c->mPosition = camPos; + c->mUp = camUp; + c->mAspect = resolution.x / resolution.y; + ++ppcChildren; + } + + // generate light sources + if (!lights.empty()) + { + pScene->mNumLights = (unsigned int)lights.size(); + pScene->mLights = new aiLight*[pScene->mNumLights]; + for (unsigned int i = 0; i < pScene->mNumLights;++i,++ppcChildren) + { + const Light& l = lights[i]; + + aiNode* nd = *ppcChildren = new aiNode(); + nd->mParent = root; + + nd->mName.length = ::sprintf(nd->mName.data,"",i); + + // allocate the light in the scene data structure + aiLight* out = pScene->mLights[i] = new aiLight(); + out->mName = nd->mName; // make sure the names are identical + out->mType = aiLightSource_POINT; + out->mColorDiffuse = out->mColorSpecular = l.color * l.intensity; + out->mPosition = l.position; + } + } if (!pScene->mNumMeshes)throw new ImportErrorException("NFF: No meshes loaded"); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; @@ -436,14 +740,38 @@ void NFFImporter::InternReadFile( const std::string& pFile, // copy vertex positions mesh->mVertices = new aiVector3D[mesh->mNumVertices]; - ::memcpy(mesh->mVertices,&src.vertices[0],sizeof(aiVector3D)*mesh->mNumVertices); - if (src.bHasNormals) + ::memcpy(mesh->mVertices,&src.vertices[0], + sizeof(aiVector3D)*mesh->mNumVertices); + + // NFF2: there could be vertex colors + if (!src.colors.empty()) + { + ai_assert(src.colors.size() == src.vertices.size()); + + // copy vertex colors + mesh->mColors[0] = new aiColor4D[mesh->mNumVertices]; + ::memcpy(mesh->mColors[0],&src.colors[0], + sizeof(aiColor4D)*mesh->mNumVertices); + } + + if (src.pType != PatchType_Simple) { ai_assert(src.normals.size() == src.vertices.size()); // copy normal vectors mesh->mNormals = new aiVector3D[mesh->mNumVertices]; - ::memcpy(mesh->mNormals,&src.normals[0],sizeof(aiVector3D)*mesh->mNumVertices); + ::memcpy(mesh->mNormals,&src.normals[0], + sizeof(aiVector3D)*mesh->mNumVertices); + } + + if (src.pType == PatchType_UVAndNormals) + { + ai_assert(src.uvs.size() == src.vertices.size()); + + // copy texture coordinates + mesh->mTextureCoords[0] = new aiVector3D[mesh->mNumVertices]; + ::memcpy(mesh->mTextureCoords[0],&src.uvs[0], + sizeof(aiVector3D)*mesh->mNumVertices); } // generate faces @@ -459,8 +787,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, } // generate a material for the mesh - MaterialHelper* pcMat = (MaterialHelper*)(pScene-> - mMaterials[m] = new MaterialHelper()); + MaterialHelper* pcMat = (MaterialHelper*)(pScene->mMaterials[m] = new MaterialHelper()); mesh->mMaterialIndex = m++; @@ -468,8 +795,16 @@ void NFFImporter::InternReadFile( const std::string& pFile, s.Set(AI_DEFAULT_MATERIAL_NAME); pcMat->AddProperty(&s, AI_MATKEY_NAME); - pcMat->AddProperty(&src.shader.color,1,AI_MATKEY_COLOR_DIFFUSE); - pcMat->AddProperty(&src.shader.color,1,AI_MATKEY_COLOR_SPECULAR); + aiColor3D c = src.shader.color * src.shader.diffuse; + pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE); + c = src.shader.color * src.shader.specular; + pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR); + + if (src.shader.texFile.length()) + { + s.Set(src.shader.texFile); + pcMat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } } pScene->mRootNode = root; } diff --git a/code/NFFLoader.h b/code/NFFLoader.h index 2c2421d93..579d8886d 100644 --- a/code/NFFLoader.h +++ b/code/NFFLoader.h @@ -78,7 +78,7 @@ protected: */ void GetExtensionList(std::string& append) { - append.append("*.nff"); + append.append("*.nff;*.enff"); } // ------------------------------------------------------------------- @@ -95,35 +95,71 @@ private: struct ShadingInfo { ShadingInfo() - : color(0.6f,0.6f,0.6f,1.0f) + : color(0.6f,0.6f,0.6f) + , diffuse (1.f) + , specular (1.f) + , ambient (0.1f) + , refracti (1.f) {} - aiColor4D color; - //float diffuse, specular; --- not implemented yet + aiColor3D color; + float diffuse, specular, ambient, refracti; + std::string texFile; + + // shininess is ignored for the moment bool operator == (const ShadingInfo& other) const - {return color == other.color;} + { + return color == other.color && + diffuse == other.diffuse && + specular == other.specular && + ambient == other.ambient && + refracti == other.refracti && + texFile == other.texFile; + } + }; + + // describes a NFF light source + struct Light + { + Light() + : color (1.f,1.f,1.f) + , intensity (1.f) + {} + + aiVector3D position; + float intensity; + aiColor3D color; + }; + + enum PatchType + { + PatchType_Simple = 0x0, + PatchType_Normals = 0x1, + PatchType_UVAndNormals = 0x2 }; // describes a NFF mesh struct MeshInfo { - MeshInfo(bool bHN, bool bL = false) - : bHasNormals(bHN) + MeshInfo(PatchType _pType, bool bL = false) + : pType(_pType) , bLocked(bL) { name[0] = '\0'; // by default meshes are unnamed } ShadingInfo shader; - bool bHasNormals, bLocked; + PatchType pType; + bool bLocked; // for spheres, cones and cylinders: center point of the object aiVector3D center, radius; char name[128]; - std::vector vertices, normals; + std::vector vertices, normals, uvs; + std::vector colors; // for NFF2 std::vector faces; }; }; diff --git a/code/OFFLoader.cpp b/code/OFFLoader.cpp index c0405c9ba..2a375a498 100644 --- a/code/OFFLoader.cpp +++ b/code/OFFLoader.cpp @@ -109,8 +109,6 @@ void OFFImporter::InternReadFile( const std::string& pFile, pScene->mMeshes = new aiMesh*[ pScene->mNumMeshes = 1 ]; aiMesh* mesh = pScene->mMeshes[0] = new aiMesh(); - mesh->mNumVertices = numFaces*3; - aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; aiFace* faces = mesh->mFaces = new aiFace [mesh->mNumFaces = numFaces]; std::vector tempPositions(numVertices); @@ -131,21 +129,43 @@ void OFFImporter::InternReadFile( const std::string& pFile, fast_atof_move(sz,(float&)v.z); } - // now read all faces lines - for (unsigned int i = 0, p = 0; i< mesh->mNumFaces;++i) + + // First find out how many vertices we'll need + const char* old = buffer; + for (unsigned int i = 0; i< mesh->mNumFaces;++i) { if(!GetNextLine(buffer,line)) { DefaultLogger::get()->error("OFF: The number of faces in the header is incorrect"); break; } - unsigned int idx;sz = line;SkipSpaces(&sz); - if(!(faces->mNumIndices = strtol10(sz,&sz)) || faces->mNumIndices > 100) + sz = line;SkipSpaces(&sz); + if(!(faces->mNumIndices = strtol10(sz,&sz)) || faces->mNumIndices > 9) { DefaultLogger::get()->error("OFF: Faces with zero indices aren't allowed"); --mesh->mNumFaces; continue; } + mesh->mNumVertices += faces->mNumIndices; + ++faces; + } + + if (!mesh->mNumVertices) + throw new ImportErrorException("OFF: There are no valid faces"); + + // allocate storage for the output vertices + aiVector3D* verts = mesh->mVertices = new aiVector3D[mesh->mNumVertices]; + + // second: now parse all face indices + buffer = old;faces = mesh->mFaces; + for (unsigned int i = 0, p = 0; i< mesh->mNumFaces;) + { + if(!GetNextLine(buffer,line))break; + + unsigned int idx; + sz = line;SkipSpaces(&sz); + if(!(idx = strtol10(sz,&sz)) || idx > 9) + continue; faces->mIndices = new unsigned int [faces->mNumIndices]; for (unsigned int m = 0; m < faces->mNumIndices;++m) @@ -156,10 +176,10 @@ void OFFImporter::InternReadFile( const std::string& pFile, DefaultLogger::get()->error("OFF: Vertex index is out of range"); idx = numVertices-1; } - faces->mIndices[m] = p++; *verts++ = tempPositions[idx]; } + ++i; ++faces; } diff --git a/code/PretransformVertices.cpp b/code/PretransformVertices.cpp index 3d8fdafe3..5ec467139 100644 --- a/code/PretransformVertices.cpp +++ b/code/PretransformVertices.cpp @@ -48,23 +48,30 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; +// some array offsets +#define AI_PTVS_VERTEX 0x0 +#define AI_PTVS_FACE 0x1 + // ------------------------------------------------------------------------------------------------ // 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 { return (pFlags & aiProcess_PreTransformVertices) != 0; } + // ------------------------------------------------------------------------------------------------ // Count the number of nodes unsigned int CountNodes( aiNode* pcNode ) @@ -76,14 +83,20 @@ unsigned int CountNodes( aiNode* pcNode ) } return iRet; } + // ------------------------------------------------------------------------------------------------ // Get a bitwise combination identifying the vertex format of a mesh unsigned int GetMeshVFormat(aiMesh* pcMesh) { - if (0xdeadbeef == pcMesh->mNumUVComponents[0]) - return pcMesh->mNumUVComponents[1]; + // the vertex format is stored in aiMesh::mBones for later retrieval. + // there isn't a good reason to compute it a few hundred times + // from scratch. The pointer is unused as animations are lost + // during PretransformVertices. + if (pcMesh->mBones) + return (unsigned int)pcMesh->mBones; unsigned int iRet = 0; + ai_assert(NULL != pcMesh->mVertices); // normals if (pcMesh->HasNormals())iRet |= 0x1; @@ -92,23 +105,25 @@ unsigned int GetMeshVFormat(aiMesh* pcMesh) // texture coordinates unsigned int p = 0; - ai_assert(4 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); + ai_assert(8 >= AI_MAX_NUMBER_OF_TEXTURECOORDS); while (pcMesh->HasTextureCoords(p)) { - iRet |= (0x100 << p++); + iRet |= (0x100 << p); if (3 == pcMesh->mNumUVComponents[p]) - iRet |= (0x1000 << p++); + iRet |= (0x10000 << p); + + ++p; } // vertex colors p = 0; - while (pcMesh->HasVertexColors(p))iRet |= (0x10000 << p++); + ai_assert(8 >= AI_MAX_NUMBER_OF_COLOR_SETS); + while (pcMesh->HasVertexColors(p))iRet |= (0x1000000 << p++); // store the value for later use - pcMesh->mNumUVComponents[0] = 0xdeadbeef; - pcMesh->mNumUVComponents[1] = iRet; - + pcMesh->mBones = (aiBone**)iRet; return iRet; } + // ------------------------------------------------------------------------------------------------ // Count the number of vertices in the whole scene and a given // material index @@ -132,9 +147,6 @@ void CountVerticesAndFaces( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, return; } -#define AI_PTVS_VERTEX 0x0 -#define AI_PTVS_FACE 0x1 - // ------------------------------------------------------------------------------------------------ // Collect vertex/face data void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, @@ -157,11 +169,14 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, aiMatrix4x4 mWorldIT = pcNode->mTransformation; mWorldIT.Inverse().Transpose(); + // TODO: implement Inverse() for aiMatrix3x3 + aiMatrix3x3 m = aiMatrix3x3(mWorldIT); + // copy normals, transform them to worldspace for (unsigned int n = 0; n < pcMesh->mNumVertices;++n) { pcMeshOut->mNormals[aiCurrent[AI_PTVS_VERTEX]+n] = - mWorldIT * pcMesh->mNormals[n]; + m * pcMesh->mNormals[n]; } } if (iVFormat & 0x2) @@ -185,7 +200,7 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, ++p; } p = 0; - while (iVFormat & (0x10000 << p)) + while (iVFormat & (0x1000000 << p)) { // copy vertex colors memcpy(pcMeshOut->mColors[p] + aiCurrent[AI_PTVS_VERTEX], @@ -213,6 +228,23 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, // just make sure the array won't be deleted by the // aiFace destructor ... pcMesh->mFaces[planck].mIndices = NULL; + + // FIX: update the mPrimitiveTypes member of the mesh + switch (pcMesh->mFaces[planck].mNumIndices) + { + case 0x1: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POINT; + break; + case 0x2: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; + case 0x3: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; + default: + pcMeshOut->mPrimitiveTypes |= aiPrimitiveType_POLYGON; + break; + }; } aiCurrent[AI_PTVS_VERTEX] += pcMesh->mNumVertices; aiCurrent[AI_PTVS_FACE] += pcMesh->mNumFaces; @@ -225,11 +257,12 @@ void CollectData( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, } return; } + // ------------------------------------------------------------------------------------------------ // Get a list of all vertex formats that occur for a given material index // The output list contains duplicate elements void GetVFormatList( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, - std::list& aiOut) + std::list& aiOut) { for (unsigned int i = 0; i < pcNode->mNumMeshes;++i) { @@ -245,13 +278,14 @@ void GetVFormatList( aiScene* pcScene, aiNode* pcNode, unsigned int iMat, } return; } + // ------------------------------------------------------------------------------------------------ // Compute the absolute transformation matrices of each node void ComputeAbsoluteTransform( aiNode* pcNode ) { if (pcNode->mParent) { - pcNode->mTransformation = pcNode->mTransformation*pcNode->mParent->mTransformation; + pcNode->mTransformation = pcNode->mParent->mTransformation*pcNode->mTransformation; } for (unsigned int i = 0;i < pcNode->mNumChildren;++i) @@ -260,18 +294,37 @@ void ComputeAbsoluteTransform( aiNode* pcNode ) } return; } + // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. void PretransformVertices::Execute( aiScene* pScene) { DefaultLogger::get()->debug("PretransformVerticesProcess begin"); + const unsigned int iOldMeshes = pScene->mNumMeshes; + const unsigned int iOldAnimationChannels = pScene->mNumAnimations; + const unsigned int iOldNodes = CountNodes(pScene->mRootNode); + // first compute absolute transformation matrices for all nodes ComputeAbsoluteTransform(pScene->mRootNode); + // delete aiMesh::mBones for all meshes. The bones are + // removed during this step and we need the pointer as + // temporary storage + for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { + aiMesh* mesh = pScene->mMeshes[i]; + + for (unsigned int a = 0; a < mesh->mNumBones;++a) + delete mesh->mBones[a]; + + delete[] mesh->mBones; + mesh->mBones = NULL; + } + // now build a list of output meshes std::vector apcOutMeshes; - apcOutMeshes.reserve(pScene->mNumMaterials*2); + apcOutMeshes.reserve(pScene->mNumMaterials<<1u); std::list aiVFormats; for (unsigned int i = 0; i < pScene->mNumMaterials;++i) { @@ -306,12 +359,12 @@ void PretransformVertices::Execute( aiScene* pScene) while ((*j) & (0x100 << iFaces)) { pcMesh->mTextureCoords[iFaces] = new aiVector3D[iVertices]; - if ((*j) & (0x1000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; + if ((*j) & (0x10000 << iFaces))pcMesh->mNumUVComponents[iFaces] = 3; else pcMesh->mNumUVComponents[iFaces] = 2; iFaces++; } iFaces = 0; - while ((*j) & (0x10000 << iFaces)) + while ((*j) & (0x1000000 << iFaces)) pcMesh->mColors[iFaces] = new aiColor4D[iVertices]; // fill the mesh ... @@ -324,28 +377,66 @@ void PretransformVertices::Execute( aiScene* pScene) // remove all animations from the scene for (unsigned int i = 0; i < pScene->mNumAnimations;++i) delete pScene->mAnimations[i]; - pScene->mAnimations = NULL; + delete[] pScene->mAnimations; + + pScene->mAnimations = NULL; pScene->mNumAnimations = 0; // now delete all meshes in the scene and build a new mesh list for (unsigned int i = 0; i < pScene->mNumMeshes;++i) + { delete pScene->mMeshes[i]; - if (apcOutMeshes.size() != pScene->mNumMeshes) + + // invalidate the contents of the old mesh array. We will most + // likely have less output meshes now, so the last entries of + // the mesh array are not overridden. We set them to NULL to + // make sure the developer gets notified when his application + // attempts to access these fields ... + AI_DEBUG_INVALIDATE_PTR( pScene->mMeshes[i] ); + } + + pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); + if (apcOutMeshes.size() > pScene->mNumMeshes) { delete[] pScene->mMeshes; - pScene->mNumMeshes = (unsigned int)apcOutMeshes.size(); pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; } for (unsigned int i = 0; i < pScene->mNumMeshes;++i) pScene->mMeshes[i] = apcOutMeshes[i]; + // --- we need to keep all cameras and lights + for (unsigned int i = 0; i < pScene->mNumCameras;++i) + { + aiCamera* cam = pScene->mCameras[i]; + const aiNode* nd = pScene->mRootNode->FindNode(cam->mName); + ai_assert(NULL != nd); + + // multiply all properties of the camera with the absolute + // transformation of the corresponding node + cam->mPosition = nd->mTransformation * cam->mPosition; + cam->mLookAt = aiMatrix3x3( nd->mTransformation ) * cam->mLookAt; + cam->mUp = aiMatrix3x3( nd->mTransformation ) * cam->mUp; + } + + for (unsigned int i = 0; i < pScene->mNumLights;++i) + { + aiLight* l = pScene->mLights[i]; + const aiNode* nd = pScene->mRootNode->FindNode(l->mName); + ai_assert(NULL != nd); + + // multiply all properties of the camera with the absolute + // transformation of the corresponding node + l->mPosition = nd->mTransformation * l->mPosition; + l->mDirection = aiMatrix3x3( nd->mTransformation ) * l->mDirection; + } + // now delete all nodes in the scene and build a new // flat node graph with a root node and some level 1 children delete pScene->mRootNode; pScene->mRootNode = new aiNode(); pScene->mRootNode->mName.Set(""); - if (1 == pScene->mNumMeshes) + if (1 == pScene->mNumMeshes && !pScene->mNumLights && !pScene->mNumCameras) { pScene->mRootNode->mNumMeshes = 1; pScene->mRootNode->mMeshes = new unsigned int[1]; @@ -353,22 +444,59 @@ void PretransformVertices::Execute( aiScene* pScene) } else { - pScene->mRootNode->mNumChildren = pScene->mNumMeshes; - pScene->mRootNode->mChildren = new aiNode*[pScene->mNumMeshes]; - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { - aiNode* pcNode = pScene->mRootNode->mChildren[i] = new aiNode(); - pcNode->mName.length = sprintf(pcNode->mName.data,"dummy_%i",i); + pScene->mRootNode->mNumChildren = pScene->mNumMeshes+pScene->mNumLights+pScene->mNumCameras; + aiNode** nodes = pScene->mRootNode->mChildren = new aiNode*[pScene->mRootNode->mNumChildren]; + // generate mesh nodes + for (unsigned int i = 0; i < pScene->mNumMeshes;++i,++nodes) + { + aiNode* pcNode = *nodes = new aiNode(); + pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ::sprintf(pcNode->mName.data,"mesh_%i",i); + + // setup mesh indices pcNode->mNumMeshes = 1; pcNode->mMeshes = new unsigned int[1]; pcNode->mMeshes[0] = i; + } + // generate light nodes + for (unsigned int i = 0; i < pScene->mNumLights;++i,++nodes) + { + aiNode* pcNode = *nodes = new aiNode(); pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ::sprintf(pcNode->mName.data,"light_%i",i); + pScene->mLights[i]->mName = pcNode->mName; + } + // generate camera nodes + for (unsigned int i = 0; i < pScene->mNumCameras;++i,++nodes) + { + aiNode* pcNode = *nodes = new aiNode(); + pcNode->mParent = pScene->mRootNode; + pcNode->mName.length = ::sprintf(pcNode->mName.data,"cam_%i",i); + pScene->mCameras[i]->mName = pcNode->mName; } } - DefaultLogger::get()->debug("PretransformVerticesProcess finished. All " - "vertices are in worldspace now"); + // print statistics + if (!DefaultLogger::isNullLogger()) + { + char buffer[4096]; + + DefaultLogger::get()->debug("PretransformVerticesProcess finished"); + + ::sprintf(buffer,"Removed %i nodes and %i animation channels (%i output nodes)", + iOldNodes,iOldAnimationChannels,CountNodes(pScene->mRootNode)); + DefaultLogger::get()->info(buffer); + + ::sprintf(buffer,"Kept %i lights and %i cameras", + pScene->mNumLights,pScene->mNumCameras); + DefaultLogger::get()->info(buffer); + + ::sprintf(buffer,"Moved %i meshes to WCS (number of output meshes: %i)", + iOldMeshes,pScene->mNumMeshes); + DefaultLogger::get()->info(buffer); + } + return; } diff --git a/code/RemoveComments.cpp b/code/RemoveComments.cpp index cf8ccc895..fc40cf9aa 100644 --- a/code/RemoveComments.cpp +++ b/code/RemoveComments.cpp @@ -44,6 +44,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "AssimpPCH.h" #include "RemoveComments.h" +#include "ParsingUtils.h" namespace Assimp { @@ -56,17 +57,17 @@ void CommentRemover::RemoveLineComments(const char* szComment, ai_assert(NULL != szComment && NULL != szBuffer && *szComment); const size_t len = ::strlen(szComment); - while (*szBuffer) { - if (0 == ::strncmp(szBuffer,szComment,len)) + if (!::strncmp(szBuffer,szComment,len)) { - while (*szBuffer != '\r' && *szBuffer != '\n' && *szBuffer) + while (!IsLineEnd(*szBuffer)) *szBuffer++ = chReplacement; } ++szBuffer; } } + // ------------------------------------------------------------------------------------------------ void CommentRemover::RemoveMultiLineComments(const char* szCommentStart, const char* szCommentEnd,char* szBuffer, @@ -74,32 +75,30 @@ void CommentRemover::RemoveMultiLineComments(const char* szCommentStart, { // validate parameters ai_assert(NULL != szCommentStart && NULL != szCommentEnd && - NULL != szBuffer && '\0' != *szCommentStart && '\0' != *szCommentEnd); + NULL != szBuffer && *szCommentStart && *szCommentEnd); - const size_t len = ::strlen(szCommentEnd); + const size_t len = ::strlen(szCommentEnd); const size_t len2 = ::strlen(szCommentStart); while (*szBuffer) { - if (0 == ::strncmp(szBuffer,szCommentStart,len2)) + if (!::strncmp(szBuffer,szCommentStart,len2)) { while (*szBuffer) { - - if (0 == ::strncmp(szBuffer,szCommentEnd,len)) + if (!::strncmp(szBuffer,szCommentEnd,len)) { for (unsigned int i = 0; i < len;++i) *szBuffer++ = chReplacement; - goto __continue_outer; // WUHHHAAAAHHAA! + + break; } *szBuffer++ = chReplacement; } - return; + if (!(*szBuffer))return; + continue; } ++szBuffer; -__continue_outer: - int i = 4; // NOP dummy - ++i; } } diff --git a/code/RemoveVCProcess.cpp b/code/RemoveVCProcess.cpp index 168e6134b..62d0897c6 100644 --- a/code/RemoveVCProcess.cpp +++ b/code/RemoveVCProcess.cpp @@ -185,7 +185,30 @@ void RemoveVCProcess::Execute( aiScene* pScene) bHas = true; ArrayDelete(pScene->mTextures,pScene->mNumTextures); } -#if 0 + + // handle materials + if ( configDeleteFlags & aiComponent_MATERIALS && pScene->mNumMaterials) + { + bHas = true; + for (unsigned int i = 1;i < pScene->mNumMaterials;++i) + delete pScene->mMaterials[i]; + + MaterialHelper* helper = (MaterialHelper*) pScene->mMaterials[0]; + helper->Clear(); + + // gray + aiColor3D clr(0.6f,0.6f,0.6f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_DIFFUSE); + + // add a small ambient color value + clr = aiColor3D(0.05f,0.05f,0.05f); + helper->AddProperty(&clr,1,AI_MATKEY_COLOR_AMBIENT); + + aiString s; + s.Set("Dummy_MaterialsRemoved"); + helper->AddProperty(&s,AI_MATKEY_NAME); + } + // handle light sources if ( configDeleteFlags & aiComponent_LIGHTS) { @@ -198,7 +221,7 @@ void RemoveVCProcess::Execute( aiScene* pScene) } // handle camneras - if ( configDeleteFlags & aiComponent_CAMERA) + if ( configDeleteFlags & aiComponent_CAMERAS) { // mask nodes for removal MaskNodes(pScene->mRootNode,pScene->mLights,pScene->mNumLights, @@ -207,13 +230,12 @@ void RemoveVCProcess::Execute( aiScene* pScene) bHas = true; ArrayDelete(pScene->mCameras,pScene->mNumCameras); } -#endif + // handle meshes if (configDeleteFlags & aiComponent_MESHES) { bHas = true; ArrayDelete(pScene->mMeshes,pScene->mNumMeshes); - pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY; } else { @@ -232,7 +254,6 @@ void RemoveVCProcess::Execute( aiScene* pScene) // MSB>>1 means: NO, DON'T REMOVE ME (Veto) if (bMasked) { -#if 0 if (pScene->mNumLights) { MaskNodes(pScene->mRootNode,pScene->mLights,pScene->mNumLights, @@ -243,7 +264,6 @@ void RemoveVCProcess::Execute( aiScene* pScene) MaskNodes(pScene->mRootNode,pScene->mCameras,pScene->mNumCameras, AI_RC_UINT_MSB_2); } -#endif if (!(configDeleteFlags & aiComponent_BONEWEIGHTS)) { for (unsigned int i = 0; i < pScene->mNumMeshes;++i) @@ -259,15 +279,14 @@ void RemoveVCProcess::Execute( aiScene* pScene) std::list dummy; UpdateNodeGraph(pScene->mRootNode,dummy, true); - // the root node will neever be deleted + // the root node will never be deleted } - // now check whether the result contains - // !0 animations (+ the AI_SCENE_FLAGS_ANIM_SKELETON_ONLY flag) OR - // !0 meshes - if (!pScene->mNumAnimations && !pScene->mNumMeshes) + // now check whether the result is still a full scene + if (!pScene->mNumMeshes || !pScene->mNumMaterials) { - throw new ImportErrorException("No valid data structure remaining"); + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; + DefaultLogger::get()->debug("Setting AI_SCENE_FLAGS_INCOMPLETE flag"); } if (bHas)DefaultLogger::get()->info("RemoveVCProcess finished. Data structure cleanup has been done."); @@ -291,6 +310,11 @@ bool RemoveVCProcess::ProcessMesh(aiMesh* pMesh) { bool ret = false; + // if all materials have been deleted let the material + // index of the mesh point to the created default material + if ( configDeleteFlags & aiComponent_MATERIALS) + pMesh->mMaterialIndex = 0; + // handle normals if (configDeleteFlags & aiComponent_NORMALS && pMesh->mNormals) { diff --git a/code/SMDLoader.cpp b/code/SMDLoader.cpp index bd1f56455..df3050a38 100644 --- a/code/SMDLoader.cpp +++ b/code/SMDLoader.cpp @@ -156,7 +156,7 @@ void SMDImporter::InternReadFile( } // set the flag in the scene structure which indicates // that there is nothing than an animation skeleton - pScene->mFlags |= AI_SCENE_FLAGS_ANIM_SKELETON_ONLY; + pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; } if (!asBones.empty()) @@ -179,7 +179,7 @@ void SMDImporter::InternReadFile( // compute absolute bone transformation matrices ComputeAbsoluteBoneTransformations(); } - if (!(pScene->mFlags & AI_SCENE_FLAGS_ANIM_SKELETON_ONLY)) + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { // create output meshes CreateOutputMeshes(); @@ -462,7 +462,7 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) void SMDImporter::CreateOutputNodes() { pScene->mRootNode = new aiNode(); - if (!(pScene->mFlags & AI_SCENE_FLAGS_ANIM_SKELETON_ONLY)) + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { // create one root node that renders all meshes pScene->mRootNode->mNumMeshes = pScene->mNumMeshes; @@ -475,7 +475,7 @@ void SMDImporter::CreateOutputNodes() AddBoneChildren(pScene->mRootNode,(uint32_t)-1); // if we have only one bone we can even remove the root node - if (pScene->mFlags & AI_SCENE_FLAGS_ANIM_SKELETON_ONLY && + if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE && 1 == pScene->mRootNode->mNumChildren) { aiNode* pcOldRoot = pScene->mRootNode; diff --git a/code/FaceEdgeAdjacency.h b/code/SceneCombiner.cpp similarity index 100% rename from code/FaceEdgeAdjacency.h rename to code/SceneCombiner.cpp diff --git a/code/SceneCombiner.h b/code/SceneCombiner.h new file mode 100644 index 000000000..e69de29bb diff --git a/code/SortByPTypeProcess.cpp b/code/SortByPTypeProcess.cpp index b7b9f2613..7f1fb742f 100644 --- a/code/SortByPTypeProcess.cpp +++ b/code/SortByPTypeProcess.cpp @@ -55,7 +55,7 @@ using namespace Assimp; // Constructor to be privately used by Importer DeterminePTypeHelperProcess ::DeterminePTypeHelperProcess() { - // nothing to do here + bSpeedFlag = false; } // ------------------------------------------------------------------------------------------------ @@ -73,84 +73,97 @@ bool DeterminePTypeHelperProcess::IsActive( unsigned int pFlags) const return true; } +// ------------------------------------------------------------------------------------------------ +// called as a request to the step to update its configuration +void DeterminePTypeHelperProcess::SetupProperties(const Importer* pImp) +{ + bSpeedFlag = (pImp->GetPropertyInteger(AI_CONFIG_FAVOUR_SPEED,0) ? true : false); +} + // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. void DeterminePTypeHelperProcess::Execute( aiScene* pScene) { + DefaultLogger::get()->debug("DeterminePTypeHelper begin"); for (unsigned int i = 0; i < pScene->mNumMeshes;++i) { aiMesh* mesh = pScene->mMeshes[i]; + + // if the speed flag is not set search whether there are any degenerated + // primitives in the mesh + if (false && !bSpeedFlag) + { + unsigned int deg = 0; + for (unsigned int a = 0; a < mesh->mNumFaces; ++a) + { + aiFace& face = mesh->mFaces[a]; + bool first = true; + + // check whether the face contains degenerated entries + for (register unsigned int i = 0; i < face.mNumIndices; ++i) + { + for (register unsigned int a = i+1; a < face.mNumIndices; ++a) + { + if (mesh->mVertices[face.mIndices[i]] == mesh->mVertices[face.mIndices[a]]) + { + // we have found a matching vertex position + // remove the corresponding index from the array + for (unsigned int m = a; m < face.mNumIndices-1; ++m) + { + face.mIndices[m] = face.mIndices[m+1]; + } + --a; + --face.mNumIndices; + + // NOTE: we set the removed vertex index to an unique value + // to make sure the developer gets notified when his + // application attemps to access this data. + face.mIndices[face.mNumIndices] = 0xdeadbeef; + + if(first) + { + ++deg; + first = false; + } + } + } + } + } + if (deg) + { + char s[64]; + ::_itoa(deg,s,10); + DefaultLogger::get()->warn(std::string("Found ") + s + " degenerated primitives"); + } + } + if (!mesh->mPrimitiveTypes) { - bool bDeg = false; for (unsigned int a = 0; a < mesh->mNumFaces; ++a) { aiFace& face = mesh->mFaces[a]; switch (face.mNumIndices) { case 3u: + mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; + break; - // check whether the triangle is degenerated - if (mesh->mVertices[face.mIndices[0]] == mesh->mVertices[face.mIndices[1]] || - mesh->mVertices[face.mIndices[1]] == mesh->mVertices[face.mIndices[2]]) - { - face.mNumIndices = 2; - unsigned int* pi = new unsigned int[2]; - pi[0] = face.mIndices[0]; - pi[1] = face.mIndices[2]; - delete[] face.mIndices; - face.mIndices = pi; - - bDeg = true; - } - else if (mesh->mVertices[face.mIndices[2]] == mesh->mVertices[face.mIndices[0]]) - { - face.mNumIndices = 2; - unsigned int* pi = new unsigned int[2]; - pi[0] = face.mIndices[0]; - pi[1] = face.mIndices[1]; - delete[] face.mIndices; - face.mIndices = pi; - - bDeg = true; - } - else - { - mesh->mPrimitiveTypes |= aiPrimitiveType_TRIANGLE; - break; - } case 2u: + mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; + break; - // check whether the line is degenerated - if (mesh->mVertices[face.mIndices[0]] == mesh->mVertices[face.mIndices[1]]) - { - face.mNumIndices = 1; - unsigned int* pi = new unsigned int[1]; - pi[0] = face.mIndices[0]; - delete[] face.mIndices; - face.mIndices = pi; - - bDeg = true; - } - else - { - mesh->mPrimitiveTypes |= aiPrimitiveType_LINE; - break; - } case 1u: mesh->mPrimitiveTypes |= aiPrimitiveType_POINT; break; + default: mesh->mPrimitiveTypes |= aiPrimitiveType_POLYGON; break; } } - if (bDeg) - { - DefaultLogger::get()->warn("Found degenerated primitives"); - } } } + DefaultLogger::get()->debug("DeterminePTypeHelper finished"); } @@ -218,7 +231,15 @@ void UpdateNodes(const std::vector& replaceMeshIndex, aiNode* node // Executes the post processing step on the given imported data. void SortByPTypeProcess::Execute( aiScene* pScene) { - if (!pScene->mNumMeshes)return; + if (!pScene->mNumMeshes) + { + DefaultLogger::get()->debug("SortByPTypeProcess skipped, there are no meshes"); + return; + } + + DefaultLogger::get()->debug("SortByPTypeProcess begin"); + + unsigned int aiNumMeshesPerPType[4] = {0,0,0,0}; std::vector outMeshes; outMeshes.reserve(pScene->mNumMeshes<<1u); @@ -232,10 +253,26 @@ void SortByPTypeProcess::Execute( aiScene* pScene) // if there's just one primitive type in the mesh there's nothing to do for us unsigned int num = 0; - if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) ++num; - if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) ++num; - if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) ++num; - if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) ++num; + if (mesh->mPrimitiveTypes & aiPrimitiveType_POINT) + { + ++aiNumMeshesPerPType[0]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_LINE) + { + ++aiNumMeshesPerPType[1]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_TRIANGLE) + { + ++aiNumMeshesPerPType[2]; + ++num; + } + if (mesh->mPrimitiveTypes & aiPrimitiveType_POLYGON) + { + ++aiNumMeshesPerPType[3]; + ++num; + } if (1 == num) { @@ -436,5 +473,17 @@ void SortByPTypeProcess::Execute( aiScene* pScene) pScene->mMeshes = new aiMesh*[pScene->mNumMeshes]; ::memcpy(pScene->mMeshes,&outMeshes[0],pScene->mNumMeshes*sizeof(void*)); } + + if (!DefaultLogger::isNullLogger()) + { + char buffer[1024]; + ::sprintf(buffer,"Points: %i, Lines: %i, Triangles: %i, Polygons: %i (Meshes)", + aiNumMeshesPerPType[0], + aiNumMeshesPerPType[1], + aiNumMeshesPerPType[2], + aiNumMeshesPerPType[3]); + DefaultLogger::get()->info(buffer); + DefaultLogger::get()->debug("SortByPTypeProcess finished"); + } } diff --git a/code/SortByPTypeProcess.h b/code/SortByPTypeProcess.h index 0d174f116..2a0e4e693 100644 --- a/code/SortByPTypeProcess.h +++ b/code/SortByPTypeProcess.h @@ -72,6 +72,13 @@ public: // ------------------------------------------------------------------- void Execute( aiScene* pScene); + + // ------------------------------------------------------------------- + void SetupProperties(const Importer* pImp); + +private: + + bool bSpeedFlag; }; #if (!defined AI_BUILD_NO_SORTBYPTYPE_PROCESS) diff --git a/code/StreamReader.h b/code/StreamReader.h index 88aea62db..dcabc90c3 100644 --- a/code/StreamReader.h +++ b/code/StreamReader.h @@ -70,11 +70,9 @@ public: inline StreamReader(IOStream* stream) { ai_assert(NULL != stream); - - this->input = input; this->stream = stream; - size_t s = stream->GetFileSize(); + size_t s = stream->FileSize(); if (!s)throw new ImportErrorException("File is empty"); current = buffer = new int8_t[s]; @@ -188,4 +186,4 @@ private: }; -#endif // !! AI_STREAMREADER_H_INCLUDED \ No newline at end of file +#endif // !! AI_STREAMREADER_H_INCLUDED diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index 98a208e54..32dcec314 100644 --- a/code/ValidateDataStructure.cpp +++ b/code/ValidateDataStructure.cpp @@ -56,11 +56,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. using namespace Assimp; -#if _MSC_VER >= 1400 -# define vsprintf vsprintf_s -# define sprintf sprintf_s -#endif - // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ValidateDSProcess::ValidateDSProcess() @@ -126,114 +121,200 @@ void ValidateDSProcess::ReportWarning(const char* msg,...) va_end(args); DefaultLogger::get()->warn("Validation warning: " + std::string(szBuffer,iLen)); } + +// ------------------------------------------------------------------------------------------------ +inline int HasNameMatch(const aiString& in, aiNode* node) +{ + int result = (node->mName == in ? 1 : 0 ); + for (unsigned int i = 0; i < node->mNumChildren;++i) + { + result += HasNameMatch(in,node->mChildren[i]); + } + return result; +} + +// ------------------------------------------------------------------------------------------------ +template +inline void ValidateDSProcess::DoValidation(T** parray, unsigned int size, + const char* firstName, const char* secondName) +{ + // validate all entries + if (size) + { + if (!parray) + { + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", + firstName, secondName, size); + } + for (unsigned int i = 0; i < size;++i) + { + if (!parray[i]) + { + ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)", + firstName,i,secondName,size); + } + Validate(parray[i]); + } + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline void ValidateDSProcess::DoValidationEx(T** parray, unsigned int size, + const char* firstName, const char* secondName) +{ + // validate all entries + if (size) + { + if (!parray) + { + ReportError("aiScene::%s is NULL (aiScene::%s is %i)", + firstName, secondName, size); + } + for (unsigned int i = 0; i < size;++i) + { + if (!parray[i]) + { + ReportError("aiScene::%s[%i] is NULL (aiScene::%s is %i)", + firstName,i,secondName,size); + } + Validate(parray[i]); + + // check whether there are duplicate names + for (unsigned int a = i+1; a < size;++a) + { + if (parray[i]->mName == parray[a]->mName) + { + this->ReportError("aiScene::%s[%i] has the same name as " + "aiScene::%s[%i]",firstName, i,secondName, a); + } + } + } + } +} + +// ------------------------------------------------------------------------------------------------ +template +inline void ValidateDSProcess::DoValidationWithNameCheck(T** array, + unsigned int size, const char* firstName, + const char* secondName) +{ + // validate all entries + DoValidationEx(array,size,firstName,secondName); + + for (unsigned int i = 0; i < size;++i) + { + int res = HasNameMatch(array[i]->mName,mScene->mRootNode); + if (!res) + { + ReportError("aiScene::%s[%i] has no corresponding node in the scene graph (%s)", + firstName,i,array[i]->mName.data); + } + else if (1 != res) + { + ReportError("aiScene::%s[%i]: there are more than one nodes with %s as name", + firstName,i,array[i]->mName.data); + } + } +} + // ------------------------------------------------------------------------------------------------ // Executes the post processing step on the given imported data. void ValidateDSProcess::Execute( aiScene* pScene) { this->mScene = pScene; DefaultLogger::get()->debug("ValidateDataStructureProcess begin"); + + // validate the node graph of the scene + Validate(pScene->mRootNode); + + // at least one of the mXXX arrays must be non-empty or we'll flag + // the sebe as invalid + bool has = false; // validate all meshes - if (pScene->mNumMeshes) + if (pScene->mNumMeshes) { - if (!pScene->mMeshes) - { - this->ReportError("aiScene::mMeshes is NULL (aiScene::mNumMeshes is %i)", - pScene->mNumMeshes); - } - for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { - if (!pScene->mMeshes[i]) - { - this->ReportError("aiScene::mMeshes[%i] is NULL (aiScene::mNumMeshes is %i)", - i,pScene->mNumMeshes); - } - this->Validate(pScene->mMeshes[i]); - } + has = true; + DoValidation(pScene->mMeshes,pScene->mNumMeshes,"mMeshes","mNumMeshes"); } - else if (!(this->mScene->mFlags & AI_SCENE_FLAGS_ANIM_SKELETON_ONLY)) + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { - this->ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there"); + ReportError("aiScene::mNumMeshes is 0. At least one mesh must be there"); } - + // validate all animations - if (pScene->mNumAnimations) + if (pScene->mNumAnimations) { - if (!pScene->mAnimations) - { - this->ReportError("aiScene::mAnimations is NULL (aiScene::mNumAnimations is %i)", - pScene->mNumAnimations); - } - for (unsigned int i = 0; i < pScene->mNumAnimations;++i) - { - if (!pScene->mAnimations[i]) - { - this->ReportError("aiScene::mAnimations[%i] is NULL (aiScene::mNumAnimations is %i)", - i,pScene->mNumAnimations); - } - this->Validate(pScene->mAnimations[i]); - - // check whether there are duplicate animation names - for (unsigned int a = i+1; a < pScene->mNumAnimations;++a) - { - if (pScene->mAnimations[i]->mName == pScene->mAnimations[a]->mName) - { - this->ReportError("aiScene::mAnimations[%i] has the same name as " - "aiScene::mAnimations[%i]",i,a); - } - } - } + has = true; + DoValidation(pScene->mAnimations,pScene->mNumAnimations, + "mAnimations","mNumAnimations"); } - else if (this->mScene->mFlags & AI_SCENE_FLAGS_ANIM_SKELETON_ONLY) + + // validate all cameras + if (pScene->mNumCameras) { - this->ReportError("aiScene::mNumAnimations is 0 and the " - "AI_SCENE_FLAGS_ANIM_SKELETON_ONLY flag is set."); + has = true; + DoValidationWithNameCheck(pScene->mCameras,pScene->mNumCameras, + "mCameras","mNumCameras"); } - // validate all textures - if (pScene->mNumTextures) + // validate all lights + if (pScene->mNumLights) { - if (!pScene->mTextures) - { - this->ReportError("aiScene::mTextures is NULL (aiScene::mNumTextures is %i)", - pScene->mNumTextures); - } - for (unsigned int i = 0; i < pScene->mNumTextures;++i) - { - if (!pScene->mTextures[i]) - { - this->ReportError("aiScene::mTextures[%i] is NULL (aiScene::mNumTextures is %i)", - i,pScene->mNumTextures); - } - this->Validate(pScene->mTextures[i]); - } + has = true; + DoValidationWithNameCheck(pScene->mLights,pScene->mNumLights, + "mLights","mNumLights"); } - + // validate all materials - if (pScene->mNumMaterials) + if (pScene->mNumMaterials) { - if (!pScene->mMaterials) - { - this->ReportError("aiScene::mMaterials is NULL (aiScene::mNumMaterials is %i)", - pScene->mNumMaterials); - } - for (unsigned int i = 0; i < pScene->mNumMaterials;++i) - { - if (!pScene->mMaterials[i]) - { - this->ReportError("aiScene::mMaterials[%i] is NULL (aiScene::mNumMaterials is %i)", - i,pScene->mNumMaterials); - } - this->Validate(pScene->mMaterials[i]); - } + has = true; + DoValidation(pScene->mCameras,pScene->mNumCameras,"mMaterials","mNumMaterials"); + } + else if (!(mScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) + { + ReportError("aiScene::mNumMaterials is 0. At least one material must be there"); } - else this->ReportError("aiScene::mNumMaterials is 0. At least one material must be there."); - - // validate the node graph of the scene - this->Validate(pScene->mRootNode); + if (!has)ReportError("The aiScene data structure is empty"); DefaultLogger::get()->debug("ValidateDataStructureProcess end"); } + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiLight* pLight) +{ + if (pLight->mType == aiLightSource_UNDEFINED) + ReportError("aiLight::mType is aiLightSource_UNDEFINED"); + + if (!pLight->mAttenuationConstant && + !pLight->mAttenuationLinear && + !pLight->mAttenuationQuadratic) + { + ReportError("aiLight::mAttenuationXXX - all are zero"); + } + + if (pLight->mAngleInnerCone > pLight->mAngleOuterCone) + ReportError("aiLight::mAngleInnerCone is larger than aiLight::mAngleOuterCone"); + + if (pLight->mColorDiffuse.IsBlack() && pLight->mColorAmbient.IsBlack() + && pLight->mColorSpecular.IsBlack()) + { + ReportError("aiLight::mColorXXX - all are black and won't have any influence"); + } +} + +// ------------------------------------------------------------------------------------------------ +void ValidateDSProcess::Validate( const aiCamera* pCamera) +{ + if (pCamera->mClipPlaneFar <= pCamera->mClipPlaneNear) + ReportError("aiCamera::mClipPlaneFar must be >= aiCamera::mClipPlaneNear"); + + if (!pCamera->mHorizontalFOV || pCamera->mHorizontalFOV >= (float)AI_MATH_PI) + ReportError("%f is not a valid value for aiCamera::mHorizontalFOV",pCamera->mHorizontalFOV); +} + // ------------------------------------------------------------------------------------------------ void ValidateDSProcess::Validate( const aiMesh* pMesh) { @@ -288,96 +369,91 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh) if (!face.mIndices)this->ReportError("aiMesh::mFaces[%i].mIndices is NULL",i); } - if (this->mScene->mFlags & AI_SCENE_FLAGS_ANIM_SKELETON_ONLY) + // positions must always be there ... + if (!pMesh->mNumVertices || !pMesh->mVertices && !mScene->mFlags) { - if (pMesh->mNumVertices || pMesh->mVertices || - pMesh->mNumFaces || pMesh->mFaces) + this->ReportError("The mesh contains no vertices"); + } + + // if tangents are there there must also be bitangent vectors ... + if ((pMesh->mTangents != NULL) != (pMesh->mBitangents != NULL)) + { + this->ReportError("If there are tangents there must also be bitangent vectors"); + } + + // faces, too + if (!pMesh->mNumFaces || !pMesh->mFaces && !mScene->mFlags) + { + this->ReportError("The mesh contains no faces"); + } + + // now check whether the face indexing layout is correct: + // unique vertices, pseudo-indexed. + std::vector abRefList; + abRefList.resize(pMesh->mNumVertices,false); + for (unsigned int i = 0; i < pMesh->mNumFaces;++i) + { + aiFace& face = pMesh->mFaces[i]; + for (unsigned int a = 0; a < face.mNumIndices;++a) { - this->ReportWarning("The mesh contains vertices and faces although " - "the AI_SCENE_FLAGS_ANIM_SKELETON_ONLY flag is set"); + if (face.mIndices[a] >= pMesh->mNumVertices) + { + this->ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); + } + // the MSB flag is temporarily used by the extra verbose + // mode to tell us that the JoinVerticesProcess might have + // been executed already. + if ( !(this->mScene->mFlags & AI_SCENE_FLAGS_NON_VERBOSE_FORMAT ) && abRefList[face.mIndices[a]]) + { + ReportError("aiMesh::mVertices[%i] is referenced twice - second " + "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); + } + abRefList[face.mIndices[a]] = true; } } - else + // check whether there are vertices that aren't referenced by a face + for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { - // positions must always be there ... - if (!pMesh->mNumVertices || !pMesh->mVertices) - { - this->ReportError("The mesh contains no vertices"); - } - - // faces, too - if (!pMesh->mNumFaces || !pMesh->mFaces) - { - this->ReportError("The mesh contains no faces"); - } - - // now check whether the face indexing layout is correct: - // unique vertices, pseudo-indexed. - std::vector abRefList; - abRefList.resize(pMesh->mNumVertices,false); - for (unsigned int i = 0; i < pMesh->mNumFaces;++i) - { - aiFace& face = pMesh->mFaces[i]; - for (unsigned int a = 0; a < face.mNumIndices;++a) - { - if (face.mIndices[a] >= pMesh->mNumVertices) - { - this->ReportError("aiMesh::mFaces[%i]::mIndices[%i] is out of range",i,a); - } - // the MSB flag is temporarily used by the extra verbose - // mode to tell us that the JoinVerticesProcess might have - // been executed already. - if ( !(this->mScene->mFlags & 0x80000000 ) && abRefList[face.mIndices[a]]) - { - this->ReportError("aiMesh::mVertices[%i] is referenced twice - second " - "time by aiMesh::mFaces[%i]::mIndices[%i]",face.mIndices[a],i,a); - } - abRefList[face.mIndices[a]] = true; - } - } - // check whether there are vertices that aren't referenced by a face - for (unsigned int i = 0; i < pMesh->mNumVertices;++i) - { - if (!abRefList[i])this->ReportError("aiMesh::mVertices[%i] is not referenced",i); - } - abRefList.clear(); - - // texture channel 2 may not be set if channel 1 is zero ... - { - unsigned int i = 0; - for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - { - if (!pMesh->HasTextureCoords(i))break; - } - for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) - if (pMesh->HasTextureCoords(i)) - { - this->ReportError("Texture coordinate channel %i is existing, " - "although the previous channel was NULL.",i); - } - } - // the same for the vertex colors - { - unsigned int i = 0; - for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) - { - if (!pMesh->HasVertexColors(i))break; - } - for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) - if (pMesh->HasVertexColors(i)) - { - this->ReportError("Vertex color channel %i is existing, " - "although the previous channel was NULL.",i); - } - } + if (!abRefList[i])this->ReportError("aiMesh::mVertices[%i] is not referenced",i); } + abRefList.clear(); + + // texture channel 2 may not be set if channel 1 is zero ... + { + unsigned int i = 0; + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + { + if (!pMesh->HasTextureCoords(i))break; + } + for (;i < AI_MAX_NUMBER_OF_TEXTURECOORDS;++i) + if (pMesh->HasTextureCoords(i)) + { + ReportError("Texture coordinate channel %i is existing, " + "although the previous channel was NULL.",i); + } + } + // the same for the vertex colors + { + unsigned int i = 0; + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) + { + if (!pMesh->HasVertexColors(i))break; + } + for (;i < AI_MAX_NUMBER_OF_COLOR_SETS;++i) + if (pMesh->HasVertexColors(i)) + { + ReportError("Vertex color channel %i is existing, " + "although the previous channel was NULL.",i); + } + } + // now validate all bones if (pMesh->HasBones()) { if (!pMesh->mBones) { - this->ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)", + ReportError("aiMesh::mBones is NULL (aiMesh::mNumBones is %i)", pMesh->mNumBones); } float* afSum = NULL; @@ -397,7 +473,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh) this->ReportError("aiMesh::mBones[%i] is NULL (aiMesh::mNumBones is %i)", i,pMesh->mNumBones); } - this->Validate(pMesh,pMesh->mBones[i],afSum); + Validate(pMesh,pMesh->mBones[i],afSum); for (unsigned int a = i+1; a < pMesh->mNumBones;++a) { @@ -414,7 +490,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh) { if (afSum[i] && (afSum[i] <= 0.995 || afSum[i] >= 1.005)) { - this->ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); + ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); } } delete[] afSum; diff --git a/code/ValidateDataStructure.h b/code/ValidateDataStructure.h index 342ffb5e9..491f05176 100644 --- a/code/ValidateDataStructure.h +++ b/code/ValidateDataStructure.h @@ -145,10 +145,22 @@ protected: * @param pTexture Input texture */ void Validate( const aiTexture* pTexture); + + // ------------------------------------------------------------------- + /** Validates a light source + * @param pLight Input light + */ + void Validate( const aiLight* pLight); + + // ------------------------------------------------------------------- + /** Validates a camera + * @param pCamera Input camera + */ + void Validate( const aiCamera* pCamera); // ------------------------------------------------------------------- /** Validates a bone animation channel - * @param pAnimation Input animation + * @param pAnimation Animation channel. * @param pBoneAnim Input bone animation */ void Validate( const aiAnimation* pAnimation, @@ -168,9 +180,28 @@ protected: private: + // template to validate one of the aiScene::mXXX arrays + template + inline void DoValidation(T** array, unsigned int size, + const char* firstName, const char* secondName); + + // extended version: checks whethr T::mName occurs twice + template + inline void DoValidationEx(T** array, unsigned int size, + const char* firstName, const char* secondName); + + // extension to the first template which does also search + // the nodegraph for an item with the same name + template + inline void DoValidationWithNameCheck(T** array, unsigned int size, + const char* firstName, const char* secondName); + aiScene* mScene; }; + + + } // end of namespace Assimp #endif // AI_VALIDATEPROCESS_H_INC diff --git a/code/fast_atof.h b/code/fast_atof.h index 4903de574..819345e68 100644 --- a/code/fast_atof.h +++ b/code/fast_atof.h @@ -139,7 +139,7 @@ inline unsigned int strtol_cppstyle( const char* in, const char** out=0) { if ('0' == in[0]) { - return 'x' == in[1] ? strtol10(in+2,out) : strtol8(in+1,out); + return 'x' == in[1] ? strtol16(in+2,out) : strtol8(in+1,out); } return strtol10(in, out); } diff --git a/code/makefile.mingw b/code/makefile.mingw index c9201ee70..97a293ec2 100644 --- a/code/makefile.mingw +++ b/code/makefile.mingw @@ -74,7 +74,7 @@ all: $(TARGET) $(TARGET): $(OBJECTS) gcc -o $@ $(OBJECTS) -shared -lstdc++ %.o:%.cpp - $(CXX) -g -Wall -c $? -o $@ -I../include -I"C:\Program Files\boost\boost_1_35_0" -I"%BOOST_DIR%" + $(CXX) -g -Wall -c $? -o $@ -I../include -I"C:\Program Files\boost\boost_1_35_0" clean: del *.o diff --git a/include/BoostWorkaround/boost/multi_array.hpp b/include/BoostWorkaround/boost/multi_array.hpp new file mode 100644 index 000000000..e69de29bb diff --git a/include/aiCamera.h b/include/aiCamera.h index a02acb2e5..cfba52ddf 100644 --- a/include/aiCamera.h +++ b/include/aiCamera.h @@ -67,6 +67,35 @@ struct aiCamera */ aiString mName; + /** Position of the camera relative to the coordinate space + * defined by the corresponding node. + * + * The default value is 0|0|0. + */ + aiVector3D mPosition; + + + /** 'Up' - vector of the camera coordinate system relative to + * the coordinate space defined by the corresponding node. + * + * The 'right' vector of the camera coordinate system is + * the cross product of the up and lookAt vectors. + * The default value is 0|1|0. The vector + * may be normalized, but it needn't. + */ + aiVector3D mUp; + + + /** 'LookAt' - vector of the camera coordinate system relative to + * the coordinate space defined by the corresponding node. + * + * This is the viewing direction of the user. + * The default value is 0|0|1. The vector + * may be normalized, but it needn't. + */ + aiVector3D mLookAt; + + /** Half horizontal field of view angle, in radians. * * The field of view angle is the angle between the center @@ -105,7 +134,9 @@ struct aiCamera #ifdef __cplusplus aiCamera() - : mHorizontalFOV (0.25f * (float)AI_MATH_PI) + : mUp (0.f,1.f,0.f) + , mLookAt (0.f,0.f,1.f) + , mHorizontalFOV (0.25f * (float)AI_MATH_PI) , mClipPlaneNear (0.1f) , mClipPlaneFar (1000.f) , mAspect (0.f) @@ -120,4 +151,4 @@ struct aiCamera } #endif -#endif // AI_CAMERA_H_INC \ No newline at end of file +#endif // AI_CAMERA_H_INC diff --git a/include/aiConfig.h b/include/aiConfig.h index 09b9e3de5..f2cc82e47 100644 --- a/include/aiConfig.h +++ b/include/aiConfig.h @@ -40,8 +40,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Defines constants for configurable properties */ -#ifndef AI_CONFIG_H_INC -#define AI_CONFIG_H_INC +#ifndef __AI_CONFIG_H_INC__ +#define __AI_CONFIG_H_INC__ // --------------------------------------------------------------------------- /** \brief Set the maximum number of vertices in a mesh. @@ -138,7 +138,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * This applies to the GenSmoothNormals-Step. The angle is specified * in degrees, so 180 is PI. The default value is * 175 degrees (all vertex normals are smoothed). The maximum value is 175 - * Property type: float. + * Property type: float. Warning: seting this option may cause a severe + * loss of performance. */ #define AI_CONFIG_PP_GSN_MAX_SMOOTHING_ANGLE "pp.gsn.max_smoothing" @@ -169,20 +170,23 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // --------------------------------------------------------------------------- /** \brief Sets the colormap (= palette) to be used to decode embedded - * textures in MDL files. + * textures in MDL (Quake or 3DGS) files. * * This must be a valid path to a file. The file is 768 (256*3) bytes - * large and contains RGB tripels for each of the 256 palette entries. - * The default value is colormap.lmp. If the file is nto found, - * a default palette is used. + * large and contains RGB triplets for each of the 256 palette entries. + * The default value is colormap.lmp. If the file is not found, + * a default palette (from Quake 1) is used. * Property type: string. */ #define AI_CONFIG_IMPORT_MDL_COLORMAP "imp.mdl.color_map" - - // --------------------------------------------------------------------------- +/** \brief Enumerates components of the aiScene and aiMesh data structures + * that can be excluded from the import with the RemoveComponent step. + * + * See the documentation to #aiProcess_RemoveComment for more details. + */ enum aiComponent { //! Normal vectors @@ -220,9 +224,13 @@ enum aiComponent //! cameras are removed. aiComponent_CAMERAS = 0x200, - //! Removes all meshes (aiScene::mMeshes). The - //! #AI_SCENE_FLAGS_ANIM_SKELETON_ONLY flag is set in aiScene::mFlags. + //! Removes all meshes (aiScene::mMeshes). aiComponent_MESHES = 0x400, + + //! Removes all materials. One default material will + //! be generated, so aiScene::mNumMaterials will be 1. + //! This makes no real sense without the aiComponent_TEXTURES flag. + aiComponent_MATERIALS = 0x800 }; #define aiComponent_COLORSn(n) (1u << (n+20u)) diff --git a/include/aiDefines.h b/include/aiDefines.h index 86298bcac..dc6046ab5 100644 --- a/include/aiDefines.h +++ b/include/aiDefines.h @@ -120,6 +120,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # define AI_FORCE_INLINE __forceinline + #else # define ASSIMP_API # define AI_FORCE_INLINE inline diff --git a/include/aiLight.h b/include/aiLight.h index 76e271d01..20fae683b 100644 --- a/include/aiLight.h +++ b/include/aiLight.h @@ -70,6 +70,8 @@ enum aiLightSourceType //! A spot light source emmits light in a specific //! angle. It has a position and a direction it is pointing to. + //! A good example for a spot light is a light spot in + //! sport arenas. aiLightSource_SPOT = 0x3 }; @@ -82,7 +84,7 @@ enum aiLightSourceType */ struct aiLight { - /** The name of the light sources. + /** The name of the light source. * * There must be a node in the scenegraph with the same name. * This node specifies the position of the light in the scene @@ -91,20 +93,23 @@ struct aiLight aiString mName; /** The type of the light source. + * + * aiLightSource_UNDEFINED is nto a valid value for this member. */ aiLightSourceType mType; /** Position of the light source in space. Relative to the - * node corresponding to the light. + * transformation of the node corresponding to the light. * * The position is undefined for directional lights. */ aiVector3D mPosition; /** Direction of the light source in space. Relative to the - * node corresponding to the light. + * transformation of the node corresponding to the light. * - * The direction is undefined for point lights. + * The direction is undefined for point lights. The vector + * may be normalized, but it needn't. */ aiVector3D mDirection; @@ -115,7 +120,7 @@ struct aiLight * @code * Atten = 1/( att0 + att1 * d + att2 * d*d) * @endcode - * This member corresponds to the att01 variable in the equation. + * This member corresponds to the att0 variable in the equation. */ float mAttenuationConstant; @@ -126,7 +131,7 @@ struct aiLight * @code * Atten = 1/( att0 + att1 * d + att2 * d*d) * @endcode - * This member corresponds to the att02 variable in the equation. + * This member corresponds to the att1 variable in the equation. */ float mAttenuationLinear; @@ -137,28 +142,33 @@ struct aiLight * @code * Atten = 1/( att0 + att1 * d + att2 * d*d) * @endcode - * This member corresponds to the att03 variable in the equation. + * This member corresponds to the att2 variable in the equation. */ float mAttenuationQuadratic; /** Diffuse color of the light source * - * The color has no alpha component which wouldn't make - * sense for light sources. + * The diffuse light color is multiplied with the diffuse + * material color to obtain the final color that contributes + * to the diffuse shading term. */ aiColor3D mColorDiffuse; /** Specular color of the light source * - * The color has no alpha component which wouldn't make - * sense for light sources. + * The specular light color is multiplied with the specular + * material color to obtain the final color that contributes + * to the specular shading term. */ aiColor3D mColorSpecular; /** Ambient color of the light source * - * The color has no alpha component which wouldn't make - * sense for light sources. + * The ambient light color is multiplied with the ambient + * material color to obtain the final color that contributes + * to the ambient shading term. Most renderers will ignore + * this value it, is just a remaining of the fixed-function pipeline + * that is still supported by quite many file formats. */ aiColor3D mColorAmbient; @@ -168,18 +178,19 @@ struct aiLight * angle. The angle is given in radians. It is 2PI for point * lights and undefined for directional lights. */ - float mAngleOuterCone; + float mAngleInnerCone; /** Outer angle of a spot light's light cone. * * The spot light does not affect objects outside this angle. * The angle is given in radians. It is 2PI for point lights and - * undefined for directional lights. + * undefined for directional lights. The outer angle must be + * greater than or equal to the inner angle. * It is assumed that the application uses a smooth * interpolation between the inner and the outer cone of the * spot light. */ - float mAngleInnerCone; + float mAngleOuterCone; #ifdef __cplusplus @@ -188,8 +199,8 @@ struct aiLight , mAttenuationConstant (0.f) , mAttenuationLinear (1.f) , mAttenuationQuadratic (0.f) - , mAngleOuterCone ((float)AI_MATH_TWO_PI) , mAngleInnerCone ((float)AI_MATH_TWO_PI) + , mAngleOuterCone ((float)AI_MATH_TWO_PI) { } @@ -201,4 +212,4 @@ struct aiLight #endif -#endif // !! __AI_LIGHT_H_INC__ \ No newline at end of file +#endif // !! __AI_LIGHT_H_INC__ diff --git a/include/aiMaterial.h b/include/aiMaterial.h index 2243ca4b3..3bac27502 100644 --- a/include/aiMaterial.h +++ b/include/aiMaterial.h @@ -240,6 +240,20 @@ struct aiMaterialProperty * is never 0 */ char* mData; + +#ifdef __cplusplus + + aiMaterialProperty() + { + mData = NULL; + } + + ~aiMaterialProperty() + { + delete[] mData; + } + +#endif }; #ifdef __cplusplus diff --git a/include/aiMatrix4x4.inl b/include/aiMatrix4x4.inl index b863ead78..4eca01d6f 100644 --- a/include/aiMatrix4x4.inl +++ b/include/aiMatrix4x4.inl @@ -173,21 +173,15 @@ inline void aiMatrix4x4::Decompose (aiVector3D& scaling, aiQuaternion& rotation, // and remove all scaling from the matrix if(scaling.x) { - vRows[0].x /= scaling.x; - vRows[0].y /= scaling.x; - vRows[0].z /= scaling.x; + vRows[0] /= scaling.x; } if(scaling.y) { - vRows[1].x /= scaling.y; - vRows[1].y /= scaling.y; - vRows[1].z /= scaling.y; + vRows[1] /= scaling.y; } if(scaling.z) { - vRows[2].x /= scaling.z; - vRows[2].y /= scaling.z; - vRows[2].z /= scaling.z; + vRows[2] /= scaling.z; } // build a 3x3 rotation matrix @@ -217,7 +211,7 @@ inline void aiMatrix4x4::FromEulerAngles(float x, float y, float z) { aiMatrix4x4& _this = *this; - const float A = ::cos(x); + const float A = ::cos(x); const float B = ::sin(x); const float C = ::cos(y); const float D = ::sin(y); diff --git a/include/aiMesh.h b/include/aiMesh.h index ff0984d35..66564eee7 100644 --- a/include/aiMesh.h +++ b/include/aiMesh.h @@ -41,8 +41,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /** @file Declares the data structures in which the imported geometry is returned by ASSIMP: aiMesh, aiFace and aiBone data structures. */ -#ifndef AI_MESH_H_INC -#define AI_MESH_H_INC +#ifndef __AI_MESH_H_INC__ +#define __AI_MESH_H_INC__ #include "aiTypes.h" @@ -58,7 +58,6 @@ extern "C" { * Point and line primitives are rarely used and are NOT supported. However, * a load could pass them as degenerated triangles. */ -// --------------------------------------------------------------------------- struct aiFace { //! Number of indices defining this face. 3 for a triangle, >3 for polygon @@ -130,7 +129,6 @@ struct aiFace // --------------------------------------------------------------------------- /** A single influence of a bone on a vertex. */ -// --------------------------------------------------------------------------- struct aiVertexWeight { //! Index of the vertex which is influenced by the bone. @@ -161,7 +159,6 @@ struct aiVertexWeight * in the frame hierarchy and by which it can be addressed by animations. * In addition it has a number of influences on vertices. */ -// --------------------------------------------------------------------------- struct aiBone { //! The name of the bone. @@ -218,7 +215,6 @@ struct aiBone * \note Some internal structures expect (and assert) this value * to be at least 4 */ -// --------------------------------------------------------------------------- # define AI_MAX_NUMBER_OF_COLOR_SETS 0x4 #endif // !! AI_MAX_NUMBER_OF_COLOR_SETS @@ -234,7 +230,6 @@ struct aiBone * \note Some internal structures expect (and assert) this value * to be at least 4 */ -// --------------------------------------------------------------------------- # define AI_MAX_NUMBER_OF_TEXTURECOORDS 0x4 // NOTE (Aramis): If you change these values, make sure that you also @@ -248,33 +243,34 @@ struct aiBone #endif // !! AI_MAX_NUMBER_OF_TEXTURECOORDS -#define AI_MESH_SMOOTHING_ANGLE_NOT_SET (10e10f) - // --------------------------------------------------------------------------- /** Enumerates the types of geometric primitives supported by Assimp. */ -// --------------------------------------------------------------------------- enum aiPrimitiveType { /** A point primitive. + * * This is just a single vertex in the virtual world, * #aiFace contains just one index for such a primitive. */ aiPrimitiveType_POINT = 0x1, /** A line primitive. + * * This is a line defined through a start and an end position. * #aiFace contains exactly two indices for such a primitive. */ aiPrimitiveType_LINE = 0x2, /** A triangular primitive. + * * A triangle consists of three indices. */ aiPrimitiveType_TRIANGLE = 0x4, /** A higher-level polygon with more than 3 edges. + * * A triangle is a polygon, but polygon in this context means * "all polygons that are not triangles". The "Triangulate"-Step * is provided for your convinience, it splits all polygons in @@ -298,9 +294,9 @@ enum aiPrimitiveType * * A Mesh uses only a single material which is referenced by a material ID. * \note The mPositions member is not optional, although a Has()-Method is -* provided for it. +* provided for it. However, positions *could* be missing if the +* AI_SCENE_FLAGS_INCOMPLETE flag is set in aiScene::mFlags. */ -// --------------------------------------------------------------------------- struct aiMesh { /** Bitwise combination of the members of the #aiPrimitiveType enum. @@ -328,14 +324,23 @@ struct aiMesh /** Vertex normals. * The array contains normalized vectors, NULL if not present. - * The array is mNumVertices in size. + * The array is mNumVertices in size. Normals are undefined for + * point and line primitives. A mesh consisting of points and + * lines only may not have normal vectors. Meshes with mixed + * primitive types (i.e. lines and triangles) may have normals, + * but the normals for vertices that are only referenced by + * point or line primitives are undefined and set to QNaN. */ C_STRUCT aiVector3D* mNormals; /** Vertex tangents. * The tangent of a vertex points in the direction of the positive * X texture axis. The array contains normalized vectors, NULL if - * not present. The array is mNumVertices in size. + * not present. The array is mNumVertices in size. A mesh consisting + * of points and lines only may not have normal vectors. Meshes with + * mixed primitive types (i.e. lines and triangles) may have + * normals, but the normals for vertices that are only referenced by + * point or line primitives are undefined and set to QNaN. * @note If the mesh contains tangents, it automatically also * contains bitangents. */ @@ -368,6 +373,7 @@ struct aiMesh * or cube maps). If the value is 2 for a given channel n, the * component p.z of mTextureCoords[n][p] is set to 0.0f. * If the value is 1 for a given channel, p.y is set to 0.0f, too. + * If this value is 0, 2 should be assumed. * @note 4D coords are not supported */ unsigned int mNumUVComponents[AI_MAX_NUMBER_OF_TEXTURECOORDS]; @@ -375,7 +381,8 @@ struct aiMesh /** The faces the mesh is contstructed from. * Each face referres to a number of vertices by their indices. * This array is always present in a mesh, its size is given - * in mNumFaces. + * in mNumFaces. If the AI_SCENE_FLAGS_NON_VERBOSE_FORMAT + * is NOT set each face references an unique set of vertices. */ C_STRUCT aiFace* mFaces; @@ -422,7 +429,7 @@ struct aiMesh //! Deletes all storage allocated for the mesh ~aiMesh() { - if ( mNumVertices) // fix to make this work for invalid scenes, too + if ( mNumVertices ) // fix to make this work for invalid scenes, too { delete [] mVertices; delete [] mNormals; @@ -433,7 +440,7 @@ struct aiMesh for( unsigned int a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) delete [] mColors[a]; } - if ( mNumBones) // fix to make this work for invalid scenes, too + if ( mNumBones && mBones) // fix to make this work for invalid scenes, too { for( unsigned int a = 0; a < mNumBones; a++) delete mBones[a]; @@ -461,6 +468,9 @@ struct aiMesh { return mNormals != NULL; } //! Check whether the mesh contains tangent and bitangent vectors + //! It is not possible that it contains tangents and no bitangents + //! (or the other way round). The existence of one of them + //! implies that the second is there, too. inline bool HasTangentsAndBitangents() const { return mTangents != NULL && mBitangents != NULL; } @@ -478,7 +488,7 @@ struct aiMesh //! \param pIndex Index of the texture coordinates set inline bool HasTextureCoords( unsigned int pIndex) const { - if( pIndex > AI_MAX_NUMBER_OF_TEXTURECOORDS) + if( pIndex >= AI_MAX_NUMBER_OF_TEXTURECOORDS) return false; else return mTextureCoords[pIndex] != NULL; @@ -494,5 +504,5 @@ struct aiMesh } #endif -#endif // AI_MESH_H_INC +#endif // __AI_MESH_H_INC diff --git a/include/aiScene.h b/include/aiScene.h index 05c490890..8f6a0b9ee 100644 --- a/include/aiScene.h +++ b/include/aiScene.h @@ -40,13 +40,15 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /** @file Defines the data structures in which the imported scene is returned. */ -#ifndef AI_SCENE_H_INC -#define AI_SCENE_H_INC +#ifndef __AI_SCENE_H_INC__ +#define __AI_SCENE_H_INC__ #include "aiTypes.h" -#include "aiMesh.h" -#include "aiMaterial.h" #include "aiTexture.h" +#include "aiMesh.h" +#include "aiLight.h" +#include "aiCamera.h" +#include "aiMaterial.h" #include "aiAnim.h" #ifdef __cplusplus @@ -80,8 +82,8 @@ struct aiNode /** The number of child nodes of this node. */ unsigned int mNumChildren; - /** The child nodes of this node. NULL if mNumChildren is 0. */ + /** The child nodes of this node. NULL if mNumChildren is 0. */ C_STRUCT aiNode** mChildren; /** The number of meshes of this node. */ @@ -113,8 +115,9 @@ struct aiNode /** Destructor */ ~aiNode() { - // delete al children recursively - if (mChildren) // fix to make the d'tor work for invalid scenes, too + // delete all children recursively + // to make sure we won't crash if the data is invalid ... + if (mChildren && mNumChildren) { for( unsigned int a = 0; a < mNumChildren; a++) delete mChildren[a]; @@ -122,17 +125,66 @@ struct aiNode } delete [] mMeshes; } + + /** Searches for a node with a specific name, beginning at this + * nodes. Normally you will call this method on the root node + * of the scene. + * + * @param name Name to seach for + * @return NULL or a valid Node if the search was successful. + */ + inline aiNode* FindNode(const aiString& name) + { + if (mName == name)return this; + for (unsigned int i = 0; i < mNumChildren;++i) + { + aiNode* p = mChildren[i]->FindNode(name); + if (p)return p; + } + // there is definitely no sub node with this name + return NULL; + } + #endif // __cplusplus }; -//! @def AI_SCENE_FLAGS_ANIM_SKELETON_ONLY -//! Specifies that no full model but only an animation skeleton has been -//! imported. There are no materials in this case. There are no -//! textures in this case. But there is a node graph, animation channels -//! and propably meshes with bones. Validation of meshes is less strict -//! with this flag, so be careful. -#define AI_SCENE_FLAGS_ANIM_SKELETON_ONLY 0x1 +// --------------------------------------------------------------------------- +/** @def AI_SCENE_FLAGS_INCOMPLETE + * Specifies that the scene data structure that was imported is not complete. + * This flag bypasses some internal validations and allows the import + * of animation skeletons, material libraries or camera animation paths + * using Assimp. Most applications won't support such data. + */ +#define AI_SCENE_FLAGS_INCOMPLETE 0x1 + + +/** @def AI_SCENE_FLAGS_VALIDATED + * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) + * if the validation is successful. In a validated scene you can be sure that + * any cross references in the data structure (e.g. vertex indices) are valid. + */ +#define AI_SCENE_FLAGS_VALIDATED 0x2 + + +/** @def AI_SCENE_FLAGS_VALIDATION_WARNING + * This flag is set by the validation postprocess-step (aiPostProcess_ValidateDS) + * if the validation is successful but some issues have been found. + * This can for example mean that a texture that does not exist is referenced + * by a material or that the bone weights for a vertex don't sum to 1.0 ... . + * In most cases you should still be able to use the import. This flag could + * be useful for applications which don't capture Assimp's log output. + */ +#define AI_SCENE_FLAGS_VALIDATION_WARNING 0x4 + + +/** @def AI_SCENE_FLAGS_NON_VERBOSE_FORMAT + * This flag is currently only set by the aiProcess_JoinIdenticalVertices step. + * It indicates that the vertices of the output meshes aren't in the internal + * verbose format anymore. In the verbose format all vertices are unique, + * no vertex is ever referenced by more than one face. + */ +#define AI_SCENE_FLAGS_NON_VERBOSE_FORMAT 0x8 // --------------------------------------------------------------------------- /** The root structure of the imported data. @@ -143,15 +195,20 @@ struct aiNode struct aiScene { - /** Any combination of the AI_SCENE_FLAGS_XXX flags */ + /** Any combination of the AI_SCENE_FLAGS_XXX flags. By default + * this value is 0, no flags are set. Most applications will + * want to reject all scenes with the AI_SCENE_FLAGS_INCOMPLETE + * bit set. + */ unsigned int mFlags; /** The root node of the hierarchy. * * There will always be at least the root node if the import - * was successful. Presence of further nodes depends on the - * format and content of the imported file. + * was successful (and no special flags have been set). + * Presence of further nodes depends on the format and content + * of the imported file. */ C_STRUCT aiNode* mRootNode; @@ -163,7 +220,9 @@ struct aiScene /** The array of meshes. * * Use the indices given in the aiNode structure to access - * this array. The array is mNumMeshes in size. + * this array. The array is mNumMeshes in size. If the + * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always + * be at least ONE material. */ C_STRUCT aiMesh** mMeshes; @@ -175,7 +234,9 @@ struct aiScene /** The array of materials. * * Use the index given in each aiMesh structure to access this - * array. The array is mNumMaterials in size. + * array. The array is mNumMaterials in size. If the + * AI_SCENE_FLAGS_INCOMPLETE flag is not set there will always + * be at least ONE material. */ C_STRUCT aiMaterial** mMaterials; @@ -200,10 +261,36 @@ struct aiScene * * Not many file formats embedd their textures into the file. * An example is Quake's MDL format (which is also used by - * some GameStudio™ versions) + * some GameStudio versions) */ C_STRUCT aiTexture** mTextures; + + /** The number of light sources in the scene. Light sources + are fully optional, in most cases this attribute will be 0 */ + unsigned int mNumLights; + + /** The array of light sources. + * + * All light sources imported from the given file are + * listed here. The array is mNumLights in size. + */ + C_STRUCT aiLight** mLights; + + + /** The number of cameras in the scene. Cameras + are fully optional, in most cases this attribute will be 0 */ + unsigned int mNumCameras; + + /** The array of cameras. + * + * All cameras imported from the given file are listed here. + * The array is mNumCameras in size. The first camera in the + * array (if existing) is the default camera view into + * the scene. + */ + C_STRUCT aiCamera** mCameras; + #ifdef __cplusplus //! Default constructor @@ -215,6 +302,8 @@ struct aiScene mNumMaterials = 0; mMaterials = NULL; mNumAnimations = 0; mAnimations = NULL; mNumTextures = 0; mTextures = NULL; + mNumCameras = 0; mCameras = NULL; + mNumLights = 0; mLights = NULL; mFlags = 0; } @@ -223,31 +312,74 @@ struct aiScene { // delete all subobjects recursively delete mRootNode; - if (mNumMeshes) // fix to make the d'tor work for invalid scenes, too + + // To make sure we won't crash if the data is invalid it's + // mich better to check whether both mNumXXX and mXXX are + // valid instead of relying on just one of them. + if (mNumMeshes && mMeshes) { for( unsigned int a = 0; a < mNumMeshes; a++) delete mMeshes[a]; delete [] mMeshes; } - if (mNumMaterials) // fix to make the d'tor work for invalid scenes, too + if (mNumMaterials && mMaterials) { for( unsigned int a = 0; a < mNumMaterials; a++) delete mMaterials[a]; delete [] mMaterials; } - if (mNumAnimations) // fix to make the d'tor work for invalid scenes, too + if (mNumAnimations && mAnimations) { for( unsigned int a = 0; a < mNumAnimations; a++) delete mAnimations[a]; delete [] mAnimations; } - if (mNumTextures) // fix to make the d'tor work for invalid scenes, too + if (mNumTextures && mTextures) { for( unsigned int a = 0; a < mNumTextures; a++) delete mTextures[a]; delete [] mTextures; } + if (mNumLights && mLights) + { + for( unsigned int a = 0; a < mNumLights; a++) + delete mLights[a]; + delete [] mLights; + } + if (mNumCameras && mCameras) + { + for( unsigned int a = 0; a < mNumCameras; a++) + delete mCameras[a]; + delete [] mCameras; + } } + + //! Check whether the scene contains meshes + //! Unless no special scene flags are set this will always be true. + inline bool HasMeshes() const + { return mMeshes != NULL; } + + //! Check whether the scene contains materials + //! Unless no special scene flags are set this will always be true. + inline bool HasMaterials() const + { return mMaterials != NULL; } + + //! Check whether the scene contains lights + inline bool HasLights() const + { return mLights != NULL; } + + //! Check whether the scene contains textures + inline bool HasTextures() const + { return mTextures != NULL; } + + //! Check whether the scene contains cameras + inline bool HasCameras() const + { return mCameras != NULL; } + + //! Check whether the scene contains animations + inline bool HasAnimations() const + { return mAnimations != NULL; } + #endif // __cplusplus }; @@ -255,4 +387,4 @@ struct aiScene } #endif -#endif // AI_SCENE_H_INC +#endif // __AI_SCENE_H_INC__ diff --git a/include/aiTexture.h b/include/aiTexture.h index b63b607aa..2df57f57f 100644 --- a/include/aiTexture.h +++ b/include/aiTexture.h @@ -69,15 +69,8 @@ extern "C" { # define AI_MAKE_EMBEDDED_TEXNAME(_n_) "*" # _n_ #endif -// ugly compiler dependent packing stuff -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) -# pragma pack(push,1) -# define PACK_STRUCT -#elif defined( __GNUC__ ) -# define PACK_STRUCT __attribute__((packed)) -#else -# error Compiler not supported. Never do this again. -#endif + +#include "./Compiler/pushpack1.h" // --------------------------------------------------------------------------- /** Helper structure to represent a texel in ARGB8888 format @@ -87,11 +80,9 @@ extern "C" { // --------------------------------------------------------------------------- struct aiTexel { - unsigned char b; - unsigned char g; - unsigned char r; - unsigned char a; + uint8_t b,g,r,a; +#ifdef __cplusplus //! Comparison operator bool operator== (const aiTexel& other) const { @@ -105,14 +96,11 @@ struct aiTexel return b != other.b || r != other.r || g != other.g || a != other.a; } +#endif // __cplusplus } PACK_STRUCT; -// reset packing to the original value -#if defined(_MSC_VER) || defined(__BORLANDC__) || defined (__BCPLUSPLUS__) -# pragma pack( pop ) -#endif -#undef PACK_STRUCT +#include "./Compiler/poppack1.h" // --------------------------------------------------------------------------- /** Helper structure to describe an embedded texture @@ -164,12 +152,14 @@ struct aiTexture // Construction aiTexture () - : mWidth(0), mHeight(0), pcData(NULL) + : mWidth (0) + , mHeight (0) + , pcData (NULL) { - achFormatHint[0] = '\0'; - achFormatHint[1] = '\0'; - achFormatHint[2] = '\0'; - achFormatHint[3] = '\0'; + achFormatHint[0] = 0; + achFormatHint[1] = 0; + achFormatHint[2] = 0; + achFormatHint[3] = 0; } // Destruction diff --git a/include/aiTypes.h b/include/aiTypes.h index 96dd907d0..7fdd73868 100644 --- a/include/aiTypes.h +++ b/include/aiTypes.h @@ -114,19 +114,15 @@ struct aiColor3D aiColor3D operator*(const aiColor3D& c) const {return aiColor3D(r*c.r,g*c.g,b*c.b);} - aiColor3D operator*(float f) + aiColor3D operator*(float f) const {return aiColor3D(r*f,g*f,b*f);} - // ugly subscript operator ... should better use an union, but - // hopefully the compiler will optimize the switch away. - inline float& operator[] (unsigned int sub) + inline float operator[](unsigned int i) const {return *(&r + i);} + inline float& operator[](unsigned int i) {return *(&r + i);} + + inline bool IsBlack() const { - switch (sub) - { - case 0: return (float&)r; - case 1: return (float&)g; - default: return (float&)b; - }; + return !r && !g && !b; } #endif // !__cplusplus @@ -156,17 +152,13 @@ struct aiColor4D bool operator != (const aiColor4D& other) const {return r != other.r || g != other.g || b != other.b || a != other.a;} - // ugly subscript operator ... should better use an union, but - // hopefully the compiler will optimize the switch away. - inline float& operator[] (unsigned int sub) + inline float operator[](unsigned int i) const {return *(&r + i);} + inline float& operator[](unsigned int i) {return *(&r + i);} + + inline bool IsBlack() const { - switch (sub) - { - case 0: return (float&)r; - case 1: return (float&)g; - case 2: return (float&)b; - default: return (float&)a; - }; + // the alpha component doesn't care here. black is black. + return !r && !g && !b; } #endif // !__cplusplus diff --git a/tools/assimp_view/assimp_view.cpp b/tools/assimp_view/assimp_view.cpp index 0431e73c9..d8545c49c 100644 --- a/tools/assimp_view/assimp_view.cpp +++ b/tools/assimp_view/assimp_view.cpp @@ -433,8 +433,7 @@ int CreateAssetData() g_pcAsset->apcMeshes[i],g_pcAsset->pcScene->mMeshes[i]); } - if (g_pcAsset->pcScene->mMeshes[i]->mPrimitiveTypes == aiPrimitiveType_LINE || - g_pcAsset->pcScene->mMeshes[i]->mPrimitiveTypes == aiPrimitiveType_POINT) + if (g_pcAsset->pcScene->mMeshes[i]->mPrimitiveTypes != aiPrimitiveType_TRIANGLE) { continue; } diff --git a/workspaces/vc8/UnitTest.vcproj b/workspaces/vc8/UnitTest.vcproj index 0cdb59663..547e77392 100644 --- a/workspaces/vc8/UnitTest.vcproj +++ b/workspaces/vc8/UnitTest.vcproj @@ -675,6 +675,14 @@ RelativePath="..\..\test\unit\Main.cpp" > + + + + diff --git a/workspaces/vc8/assimp.vcproj b/workspaces/vc8/assimp.vcproj index c529723c2..22e92bc04 100644 --- a/workspaces/vc8/assimp.vcproj +++ b/workspaces/vc8/assimp.vcproj @@ -726,6 +726,10 @@ RelativePath="..\..\include\BoostWorkaround\boost\format.hpp" > + + @@ -867,6 +871,10 @@ RelativePath="..\..\code\StandardShapes.h" > + +