From be864ce4a0bf187701bb12ea9c57c4e617731d58 Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Fri, 28 Nov 2008 23:02:27 +0000 Subject: [PATCH] Added support for cones and cylinders to NFF (the code to generate these primitives is hosted in StandardShapes.cpp). Fixed some minor material bugs in NFF. Added some more test NFF files, refactored and commented the old ones. Added FromToMatrix function to aiMatrix4x4 Temporarily disabled animation support in SMD and fixed some minor bugs. Static models should load correctly in every case. git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@254 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/AssimpPCH.h | 1 + code/ComputeUVMappingProcess.cpp | 4 +- code/IRRLoader.cpp | 2 +- code/LWOMaterial.cpp | 2 + code/MD2FileData.h | 4 +- code/MD2Loader.cpp | 4 +- code/NFFLoader.cpp | 180 +++++++++++++----------- code/NFFLoader.h | 3 +- code/SMDLoader.cpp | 178 +++++++++++------------ code/StandardShapes.cpp | 194 ++++++++++++++++++-------- code/StandardShapes.h | 30 ++-- code/StringComparison.h | 25 +++- code/ValidateDataStructure.cpp | 2 +- include/IOSystem.h | 2 +- include/aiMatrix4x4.h | 16 ++- include/aiMatrix4x4.inl | 165 ++++++++++++++++++---- test/IRR/multipleAnimators.irr | Bin 6652 -> 6654 bytes test/NFF/NFF/ManyEarthsNotJustOne.nff | 25 ++++ test/NFF/NFF/cone.nff | 26 ++++ test/NFF/NFF/cylinder.nff | 10 ++ test/NFF/NFF/positionTest.nff | 25 ++-- test/NFF/NFF/spheres.nff | 8 +- 22 files changed, 601 insertions(+), 305 deletions(-) create mode 100644 test/NFF/NFF/ManyEarthsNotJustOne.nff create mode 100644 test/NFF/NFF/cone.nff create mode 100644 test/NFF/NFF/cylinder.nff diff --git a/code/AssimpPCH.h b/code/AssimpPCH.h index a22cf340e..cf5dddc38 100644 --- a/code/AssimpPCH.h +++ b/code/AssimpPCH.h @@ -120,5 +120,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #endif + #endif // !! ASSIMP_PCH_INCLUDED diff --git a/code/ComputeUVMappingProcess.cpp b/code/ComputeUVMappingProcess.cpp index 4ad0a7add..328a01293 100644 --- a/code/ComputeUVMappingProcess.cpp +++ b/code/ComputeUVMappingProcess.cpp @@ -139,8 +139,8 @@ void RemoveUVSeams (aiMesh* mesh, aiVector3D* out) const static float LOWER_LIMIT = 0.1f; const static float UPPER_LIMIT = 0.9f; - const static float LOWER_EPSILON = 1e-3f; - const static float UPPER_EPSILON = 1.f-1e-3f; + const static float LOWER_EPSILON = 10e-3f; + const static float UPPER_EPSILON = 1.f-10e-3f; for (unsigned int fidx = 0; fidx < mesh->mNumFaces;++fidx) { diff --git a/code/IRRLoader.cpp b/code/IRRLoader.cpp index d312d5da0..a3c79a3ba 100644 --- a/code/IRRLoader.cpp +++ b/code/IRRLoader.cpp @@ -917,7 +917,7 @@ void IRRImporter::GenerateGraph(Node* root,aiNode* rootOut ,aiScene* scene, // Now compute the final local transformation matrix of the // node from the given translation, rotation and scaling values. // (the rotation is given in Euler angles, XYZ order) - rootOut->mTransformation.FromEulerAngles(AI_DEG_TO_RAD(root->rotation) ); + rootOut->mTransformation.FromEulerAnglesXYZ(AI_DEG_TO_RAD(root->rotation) ); // apply scaling aiMatrix4x4& mat = rootOut->mTransformation; diff --git a/code/LWOMaterial.cpp b/code/LWOMaterial.cpp index d1124613d..1aa84ce03 100644 --- a/code/LWOMaterial.cpp +++ b/code/LWOMaterial.cpp @@ -147,6 +147,8 @@ bool LWOImporter::HandleTextures(MaterialHelper* pcMat, const TextureList& in, a aiUVTransform trafo; trafo.mScaling.x = (*it).wrapAmountW; trafo.mScaling.y = (*it).wrapAmountH; + + pcMat->AddProperty((float*)&trafo,sizeof(aiUVTransform),AI_MATKEY_UVTRANSFORM(type,cur)); } DefaultLogger::get()->debug("LWO2: Setting up non-UV mapping"); } diff --git a/code/MD2FileData.h b/code/MD2FileData.h index 7a0ac94c6..45981becb 100644 --- a/code/MD2FileData.h +++ b/code/MD2FileData.h @@ -144,8 +144,8 @@ struct Frame */ struct TexCoord { - uint16_t s; - uint16_t t; + int16_t s; + int16_t t; } PACK_STRUCT; // --------------------------------------------------------------------------- diff --git a/code/MD2Loader.cpp b/code/MD2Loader.cpp index 5203856e0..ddfc1d88e 100644 --- a/code/MD2Loader.cpp +++ b/code/MD2Loader.cpp @@ -413,8 +413,8 @@ void MD2Importer::InternReadFile( const std::string& pFile, // the texture coordinates are absolute values but we // need relative values between 0 and 1 - pcOut.x = pcTexCoords[iIndex].s / fDivisorU; - pcOut.y = 1.f - pcTexCoords[iIndex].t / fDivisorV; + pcOut.y = pcTexCoords[iIndex].s / fDivisorU; + pcOut.x = pcTexCoords[iIndex].t / fDivisorV; } } // FIX: flip the face order for use with OpenGL diff --git a/code/NFFLoader.cpp b/code/NFFLoader.cpp index ff99015d5..53c3ba98a 100644 --- a/code/NFFLoader.cpp +++ b/code/NFFLoader.cpp @@ -665,32 +665,66 @@ void NFFImporter::InternReadFile( const std::string& pFile, // 'tpp' - texture polygon patch primitive if ('t' == line[0]) { - if (meshesWithUVCoords.empty()) + currentMeshWithUVCoords = NULL; + for (std::vector::iterator it = meshesWithUVCoords.begin(), end = meshesWithUVCoords.end(); + it != end;++it) + { + if ((*it).shader == s) + { + currentMeshWithUVCoords = &(*it); + break; + } + } + + if (!currentMeshWithUVCoords) { meshesWithUVCoords.push_back(MeshInfo(PatchType_UVAndNormals)); currentMeshWithUVCoords = &meshesWithUVCoords.back(); + currentMeshWithUVCoords->shader = s; } - out = currentMeshWithUVCoords; } // 'pp' - polygon patch primitive else if ('p' == line[1]) { - if (meshesWithNormals.empty()) + currentMeshWithNormals = NULL; + for (std::vector::iterator it = meshesWithNormals.begin(), end = meshesWithNormals.end(); + it != end;++it) + { + if ((*it).shader == s) + { + currentMeshWithNormals = &(*it); + break; + } + } + + if (!currentMeshWithNormals) { meshesWithNormals.push_back(MeshInfo(PatchType_Normals)); currentMeshWithNormals = &meshesWithNormals.back(); + currentMeshWithNormals->shader = s; } - sz = &line[2];out = currentMeshWithNormals; } // 'p' - polygon primitive else { - if (meshes.empty()) + currentMesh = NULL; + for (std::vector::iterator it = meshes.begin(), end = meshes.end(); + it != end;++it) + { + if ((*it).shader == s) + { + currentMesh = &(*it); + break; + } + } + + if (!currentMesh) { meshes.push_back(MeshInfo(PatchType_Simple)); currentMesh = &meshes.back(); + currentMesh->shader = s; } sz = &line[1];out = currentMesh; } @@ -711,7 +745,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, { if(!GetNextLine(buffer,line)) { - DefaultLogger::get()->error("NFF: Unexpected EOF was encountered"); + DefaultLogger::get()->error("NFF: Unexpected EOF was encountered. Patch definition incomplete"); continue; } @@ -764,7 +798,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, // NFF2 uses full colors here so we need to use them too // although NFF uses simple scaling factors - s.diffuse.g = s.diffuse.b = s.diffuse.r; + s.diffuse.g = s.diffuse.b = s.diffuse.r; s.specular.g = s.specular.b = s.specular.r; // if the next one is NOT a number we assume it is a texture file name @@ -787,58 +821,14 @@ void NFFImporter::InternReadFile( const std::string& pFile, { 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; - } } // 'shader' - other way to specify a texture else if (TokenMatch(sz,"shader",6)) { - // todo + SkipSpaces(&sz); + const char* old = sz; + while (!IsSpaceOrNewLine(*sz))++sz; + s.texFile = std::string(old, (uintptr_t)sz - (uintptr_t)old); } // 'l' - light source else if (TokenMatch(sz,"l",1)) @@ -856,7 +846,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); MeshInfo& currentMesh = meshesLocked.back(); currentMesh.shader = s; - s.mapping = aiTextureMapping_SPHERE; + currentMesh.shader.mapping = aiTextureMapping_SPHERE; AI_NFF_PARSE_SHAPE_INFORMATION(); @@ -873,7 +863,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); MeshInfo& currentMesh = meshesLocked.back(); currentMesh.shader = s; - s.mapping = aiTextureMapping_SPHERE; + currentMesh.shader.mapping = aiTextureMapping_SPHERE; AI_NFF_PARSE_SHAPE_INFORMATION(); @@ -891,7 +881,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); MeshInfo& currentMesh = meshesLocked.back(); currentMesh.shader = s; - s.mapping = aiTextureMapping_SPHERE; + currentMesh.shader.mapping = aiTextureMapping_SPHERE; AI_NFF_PARSE_SHAPE_INFORMATION(); @@ -909,7 +899,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); MeshInfo& currentMesh = meshesLocked.back(); currentMesh.shader = s; - s.mapping = aiTextureMapping_SPHERE; + currentMesh.shader.mapping = aiTextureMapping_SPHERE; AI_NFF_PARSE_SHAPE_INFORMATION(); @@ -927,7 +917,7 @@ void NFFImporter::InternReadFile( const std::string& pFile, meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); MeshInfo& currentMesh = meshesLocked.back(); currentMesh.shader = s; - s.mapping = aiTextureMapping_SPHERE; + currentMesh.shader.mapping = aiTextureMapping_BOX; AI_NFF_PARSE_SHAPE_INFORMATION(); @@ -942,26 +932,54 @@ void NFFImporter::InternReadFile( const std::string& pFile, else if (TokenMatch(sz,"c",1)) { meshesLocked.push_back(MeshInfo(PatchType_Simple,true)); - MeshInfo& currentMesh = meshes.back(); + MeshInfo& currentMesh = meshesLocked.back(); currentMesh.shader = s; - s.mapping = aiTextureMapping_CYLINDER; + currentMesh.shader.mapping = aiTextureMapping_CYLINDER; + if(!GetNextLine(buffer,line)) + { + DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)"); + break; + } + sz = line; + + // read the two center points and the respective radii aiVector3D center1, center2; float radius1, radius2; AI_NFF_PARSE_TRIPLE(center1); AI_NFF_PARSE_FLOAT(radius1); + + if(!GetNextLine(buffer,line)) + { + DefaultLogger::get()->error("NFF: Unexpected end of file (cone definition not complete)"); + break; + } + sz = line; + 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; + // compute the center point of the cone/cylinder - + // it is its local transformation origin + currentMesh.dir = center2-center1; + currentMesh.center = center1+currentMesh.dir/2.f; + + float f; + if (( f = currentMesh.dir.Length()) < 10e-3f ) + { + DefaultLogger::get()->error("NFF: Cone height is close to zero"); + continue; + } + currentMesh.dir /= f; // normalize // generate the cone - it consists of simple triangles - StandardShapes::MakeCone(center1, radius1, center2, radius2, iTesselation, currentMesh.vertices); + StandardShapes::MakeCone(f, radius1, radius2, + integer_pow(4, iTesselation), currentMesh.vertices); + + // MakeCone() returns tris currentMesh.faces.resize(currentMesh.vertices.size()/3,3); - // generate a name for the mesh + // generate a name for the mesh. 'cone' if it a cone, + // 'cylinder' if it is a cylinder. Funny, isn't it? if (radius1 != radius2) ::sprintf(currentMesh.name,"cone_%i",cone++); else ::sprintf(currentMesh.name,"cylinder_%i",cylinder++); @@ -1125,13 +1143,16 @@ void NFFImporter::InternReadFile( const std::string& pFile, node->mName.Set(src.name); // setup the transformation matrix of the node - node->mTransformation.a4 = src.center.x; - node->mTransformation.b4 = src.center.y; - node->mTransformation.c4 = src.center.z; + aiMatrix4x4::FromToMatrix(aiVector3D(0.f,1.f,0.f), + src.dir,node->mTransformation); - node->mTransformation.a1 = src.radius.x; - node->mTransformation.b2 = src.radius.y; - node->mTransformation.c3 = src.radius.z; + aiMatrix4x4& mat = node->mTransformation; + mat.a1 *= src.radius.x; mat.b1 *= src.radius.x; mat.c1 *= src.radius.x; + mat.a2 *= src.radius.y; mat.b2 *= src.radius.y; mat.c2 *= src.radius.y; + mat.a3 *= src.radius.z; mat.b3 *= src.radius.z; mat.c3 *= src.radius.z; + mat.a4 = src.center.x; + mat.b4 = src.center.y; + mat.c4 = src.center.z; ++ppcChildren; } @@ -1194,7 +1215,8 @@ void NFFImporter::InternReadFile( const std::string& pFile, s.Set(AI_DEFAULT_MATERIAL_NAME); pcMat->AddProperty(&s, AI_MATKEY_NAME); - aiColor3D c = src.shader.color * src.shader.diffuse; + // FIX: Ignore diffuse == 0 + aiColor3D c = src.shader.color * (src.shader.diffuse.r ? src.shader.diffuse : aiColor3D(1.f,1.f,1.f)); pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_DIFFUSE); c = src.shader.color * src.shader.specular; pcMat->AddProperty(&c,1,AI_MATKEY_COLOR_SPECULAR); @@ -1209,6 +1231,9 @@ void NFFImporter::InternReadFile( const std::string& pFile, { s.Set(src.shader.texFile); pcMat->AddProperty(&s,AI_MATKEY_TEXTURE_DIFFUSE(0)); + + if (aiTextureMapping_UV != src.shader.mapping) + pcMat->AddProperty((int*)&src.shader.mapping, 1,AI_MATKEY_MAPPING_DIFFUSE(0)); } // setup the name of the material @@ -1218,11 +1243,6 @@ void NFFImporter::InternReadFile( const std::string& pFile, pcMat->AddProperty(&s,AI_MATKEY_NAME); } - if (aiTextureMapping_UV != src.shader.mapping) - { - pcMat->AddProperty((int*)&src.shader.mapping, 1,AI_MATKEY_MAPPING_DIFFUSE(0)); - } - // setup some more material properties that are specific to NFF2 int i; if (src.shader.twoSided) diff --git a/code/NFFLoader.h b/code/NFFLoader.h index 762dda1dc..93a5845c5 100644 --- a/code/NFFLoader.h +++ b/code/NFFLoader.h @@ -174,6 +174,7 @@ private: , bLocked (bL) , matIndex (0) , radius (1.f,1.f,1.f) + , dir (0.f,1.f,0.f) { name[0] = '\0'; // by default meshes are unnamed } @@ -183,7 +184,7 @@ private: bool bLocked; // for spheres, cones and cylinders: center point of the object - aiVector3D center, radius; + aiVector3D center, radius, dir; char name[128]; diff --git a/code/SMDLoader.cpp b/code/SMDLoader.cpp index 9e9cccc5f..4321aac2d 100644 --- a/code/SMDLoader.cpp +++ b/code/SMDLoader.cpp @@ -46,7 +46,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // internal headers #include "SMDLoader.h" #include "fast_atof.h" - +#include "SkeletonMeshBuilder.h" using namespace Assimp; @@ -93,6 +93,7 @@ bool SMDImporter::CanRead( const std::string& pFile, IOSystem* pIOHandler) const } return true; } + // ------------------------------------------------------------------------------------------------ // Setup configuration properties void SMDImporter::SetupProperties(const Importer* pImp) @@ -105,6 +106,7 @@ void SMDImporter::SetupProperties(const Importer* pImp) configFrameID = pImp->GetPropertyInteger(AI_CONFIG_IMPORT_GLOBAL_KEYFRAME,0); } } + // ------------------------------------------------------------------------------------------------ // Imports the given file into the given scene structure. void SMDImporter::InternReadFile( @@ -120,7 +122,7 @@ void SMDImporter::InternReadFile( iFileSize = (unsigned int)file->FileSize(); - // allocate storage and copy the contents of the file to a memory buffer + // Allocate storage and copy the contents of the file to a memory buffer this->pScene = pScene; std::vector buff(iFileSize+1); @@ -132,20 +134,20 @@ void SMDImporter::InternReadFile( bHasUVs = true; iLineNumber = 1; - // reserve enough space for ... hm ... 10 textures + // Reserve enough space for ... hm ... 10 textures aszTextures.reserve(10); - // reserve enough space for ... hm ... 1000 triangles + // Reserve enough space for ... hm ... 1000 triangles asTriangles.reserve(1000); - // reserve enough space for ... hm ... 20 bones + // Reserve enough space for ... hm ... 20 bones asBones.reserve(20); // parse the file ... ParseFile(); - // if there are no triangles it seems to be an animation SMD, + // If there are no triangles it seems to be an animation SMD, // containing only the animation skeleton. if (asTriangles.empty()) { @@ -154,14 +156,15 @@ void SMDImporter::InternReadFile( throw new ImportErrorException("SMD: No triangles and no bones have " "been found in the file. This file seems to be invalid."); } - // set the flag in the scene structure which indicates + + // Set the flag in the scene structure which indicates // that there is nothing than an animation skeleton pScene->mFlags |= AI_SCENE_FLAGS_INCOMPLETE; } if (!asBones.empty()) { - // check whether all bones have been initialized + // Check whether all bones have been initialized for (std::vector::const_iterator i = asBones.begin(); i != asBones.end();++i) @@ -177,8 +180,9 @@ void SMDImporter::InternReadFile( FixTimeValues(); // compute absolute bone transformation matrices - ComputeAbsoluteBoneTransformations(); + // ComputeAbsoluteBoneTransformations(); } + if (!(pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE)) { // create output meshes @@ -193,36 +197,30 @@ void SMDImporter::InternReadFile( // build output nodes (bones are added as empty dummy nodes) CreateOutputNodes(); + + if (pScene->mFlags & AI_SCENE_FLAGS_INCOMPLETE) + { + SkeletonMeshBuilder skeleton(pScene); + } } // ------------------------------------------------------------------------------------------------ // Write an error message with line number to the log file void SMDImporter::LogErrorNoThrow(const char* msg) { char szTemp[1024]; - -#if _MSC_VER >= 1400 - sprintf_s(szTemp,"Line %i: %s",iLineNumber,msg); -#else - ai_assert(strlen(msg) < 1000); sprintf(szTemp,"Line %i: %s",iLineNumber,msg); -#endif - DefaultLogger::get()->error(szTemp); } + // ------------------------------------------------------------------------------------------------ // Write a warning with line number to the log file void SMDImporter::LogWarning(const char* msg) { char szTemp[1024]; - -#if _MSC_VER >= 1400 - sprintf_s(szTemp,"Line %i: %s",iLineNumber,msg); -#else ai_assert(strlen(msg) < 1000); - sprintf(szTemp,"Line %i: %s",iLineNumber,msg); -#endif DefaultLogger::get()->warn(szTemp); } + // ------------------------------------------------------------------------------------------------ // Fix invalid time values in the file void SMDImporter::FixTimeValues() @@ -243,10 +241,14 @@ void SMDImporter::FixTimeValues() } dLengthOfAnim = dMax; } + // ------------------------------------------------------------------------------------------------ // create output meshes void SMDImporter::CreateOutputMeshes() { + if (aszTextures.empty()) + aszTextures.push_back(std::string()); + // we need to sort all faces by their material index // in opposition to other loaders we can be sure that each // material is at least used once. @@ -260,9 +262,8 @@ void SMDImporter::CreateOutputMeshes() unsigned int iNum = (unsigned int)asTriangles.size() / pScene->mNumMeshes; iNum += iNum >> 1; for (unsigned int i = 0; i < pScene->mNumMeshes;++i) - { aaiFaces[i].reserve(iNum); - } + // collect all faces iNum = 0; @@ -402,10 +403,9 @@ void SMDImporter::CreateOutputMeshes() // now build all bones of the mesh iNum = 0; for (unsigned int iBone = 0; iBone < asBones.size();++iBone) - { if (!aaiBones[iBone].empty())++iNum; - } - if (iNum) + + if (false && iNum) { pcMesh->mNumBones = iNum; pcMesh->mBones = new aiBone*[pcMesh->mNumBones]; @@ -430,11 +430,11 @@ void SMDImporter::CreateOutputMeshes() ++iNum; } } - delete[] aaiBones; } delete[] aaiFaces; } + // ------------------------------------------------------------------------------------------------ // add bone child nodes void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) @@ -469,6 +469,7 @@ void SMDImporter::AddBoneChildren(aiNode* pcNode, uint32_t iParent) AddBoneChildren(pc,i); } } + // ------------------------------------------------------------------------------------------------ // create output nodes void SMDImporter::CreateOutputNodes() @@ -484,7 +485,7 @@ void SMDImporter::CreateOutputNodes() } // now add all bones as dummy sub nodes to the graph - AddBoneChildren(pScene->mRootNode,(uint32_t)-1); + // 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_INCOMPLETE && @@ -503,6 +504,7 @@ void SMDImporter::CreateOutputNodes() pScene->mRootNode->mName.length = 10; } } + // ------------------------------------------------------------------------------------------------ // create output animations void SMDImporter::CreateOutputAnimations() @@ -569,10 +571,11 @@ void SMDImporter::CreateOutputAnimations() // there are no scaling keys ... } } + // ------------------------------------------------------------------------------------------------ void SMDImporter::ComputeAbsoluteBoneTransformations() { - // for each bone: determine the key with the lowest time value + // For each bone: determine the key with the lowest time value // theoretically the SMD format should have all keyframes // in order. However, I've seen a file where this wasn't true. for (unsigned int i = 0; i < asBones.size();++i) @@ -609,27 +612,27 @@ void SMDImporter::ComputeAbsoluteBoneTransformations() const aiMatrix4x4& mat = bone.sAnim.asKeys[iIndex].matrix; aiMatrix4x4& matOut = bone.sAnim.asKeys[iIndex].matrixAbsolute; - // the same for the parent bone ... + // The same for the parent bone ... iIndex = parentBone.sAnim.iFirstTimeKey; - const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrix; + const aiMatrix4x4& mat2 = parentBone.sAnim.asKeys[iIndex].matrixAbsolute; - // compute the absolute transformation matrix + // Compute the absolute transformation matrix matOut = mat * mat2; } } ++iParent; } - // store the inverse of the absolute transformation matrix + // Store the inverse of the absolute transformation matrix // of the first key as bone offset matrix for (iParent = 0; iParent < asBones.size();++iParent) { SMD::Bone& bone = asBones[iParent]; - aiMatrix4x4& mat = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute; - bone.mOffsetMatrix = mat; + bone.mOffsetMatrix = bone.sAnim.asKeys[bone.sAnim.iFirstTimeKey].matrixAbsolute; bone.mOffsetMatrix.Inverse(); } } + // ------------------------------------------------------------------------------------------------ // create output materials void SMDImporter::CreateOutputMaterials() @@ -646,9 +649,12 @@ void SMDImporter::CreateOutputMaterials() szName.length = (size_t)::sprintf(szName.data,"Texture_%i",iMat); pcMat->AddProperty(&szName,AI_MATKEY_NAME); - ::strcpy(szName.data, aszTextures[iMat].c_str() ); - szName.length = aszTextures[iMat].length(); - pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0)); + if (aszTextures[iMat].length()) + { + ::strcpy(szName.data, aszTextures[iMat].c_str() ); + szName.length = aszTextures[iMat].length(); + pcMat->AddProperty(&szName,AI_MATKEY_TEXTURE_DIFFUSE(0)); + } } // create a default material if necessary @@ -675,6 +681,7 @@ void SMDImporter::CreateOutputMaterials() pcHelper->AddProperty(&szName,AI_MATKEY_NAME); } } + // ------------------------------------------------------------------------------------------------ // Parse the file void SMDImporter::ParseFile() @@ -687,10 +694,8 @@ void SMDImporter::ParseFile() if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; // "version \n", should be 1 for hl and hl² SMD files - if (0 == ASSIMP_strincmp(szCurrent,"version",7) && - IsSpaceOrNewLine(*(szCurrent+7))) + if (TokenMatch(szCurrent,"version",7)) { - szCurrent += 8; if(!SkipSpaces(szCurrent,&szCurrent)) break; if (1 != strtol10(szCurrent,&szCurrent)) { @@ -700,35 +705,27 @@ void SMDImporter::ParseFile() continue; } // "nodes\n" - Starts the node section - if (0 == ASSIMP_strincmp(szCurrent,"nodes",5) && - IsSpaceOrNewLine(*(szCurrent+5))) + if (TokenMatch(szCurrent,"nodes",5)) { - szCurrent += 6; ParseNodesSection(szCurrent,&szCurrent); continue; } // "triangles\n" - Starts the triangle section - if (0 == ASSIMP_strincmp(szCurrent,"triangles",9) && - IsSpaceOrNewLine(*(szCurrent+9))) + if (TokenMatch(szCurrent,"triangles",9)) { - szCurrent += 10; ParseTrianglesSection(szCurrent,&szCurrent); continue; } // "vertexanimation\n" - Starts the vertex animation section - if (0 == ASSIMP_strincmp(szCurrent,"vertexanimation",15) && - IsSpaceOrNewLine(*(szCurrent+15))) + if (TokenMatch(szCurrent,"vertexanimation",15)) { bHasUVs = false; - szCurrent += 16; ParseVASection(szCurrent,&szCurrent); continue; } // "skeleton\n" - Starts the skeleton section - if (0 == ASSIMP_strincmp(szCurrent,"skeleton",8) && - IsSpaceOrNewLine(*(szCurrent+8))) + if (TokenMatch(szCurrent,"skeleton",8)) { - szCurrent += 9; ParseSkeletonSection(szCurrent,&szCurrent); continue; } @@ -736,6 +733,7 @@ void SMDImporter::ParseFile() } return; } + // ------------------------------------------------------------------------------------------------ unsigned int SMDImporter::GetTextureIndex(const std::string& filename) { @@ -744,13 +742,14 @@ unsigned int SMDImporter::GetTextureIndex(const std::string& filename) i = aszTextures.begin(); i != aszTextures.end();++i,++iIndex) { - // case-insensitive ... just for safety + // case-insensitive ... it's a path if (0 == ASSIMP_stricmp ( filename.c_str(),(*i).c_str()))return iIndex; } iIndex = (unsigned int)aszTextures.size(); aszTextures.push_back(filename); return iIndex; } + // ------------------------------------------------------------------------------------------------ // Parse the nodes section of the file void SMDImporter::ParseNodesSection(const char* szCurrent, @@ -770,24 +769,21 @@ void SMDImporter::ParseNodesSection(const char* szCurrent, SkipSpacesAndLineEnd(szCurrent,&szCurrent); *szCurrentOut = szCurrent; } + // ------------------------------------------------------------------------------------------------ // Parse the triangles section of the file void SMDImporter::ParseTrianglesSection(const char* szCurrent, const char** szCurrentOut) { - // parse a triangle, parse another triangle, parse the next triangle ... + // Parse a triangle, parse another triangle, parse the next triangle ... // and so on until we reach a token that looks quite similar to "end" while (true) { if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; // "end\n" - Ends the triangles section - if (0 == ASSIMP_strincmp(szCurrent,"end",3) && - IsSpaceOrNewLine(*(szCurrent+3))) - { - szCurrent += 4; + if (TokenMatch(szCurrent,"end",3)) break; - } ParseTriangle(szCurrent,&szCurrent); } SkipSpacesAndLineEnd(szCurrent,&szCurrent); @@ -804,39 +800,33 @@ void SMDImporter::ParseVASection(const char* szCurrent, if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; // "end\n" - Ends the "vertexanimation" section - if (0 == ASSIMP_strincmp(szCurrent,"end",3) && - IsSpaceOrNewLine(*(szCurrent+3))) - { - szCurrent += 4; - //SkipLine(szCurrent,&szCurrent); + if (TokenMatch(szCurrent,"end",3)) break; - } + // "time \n" - if (0 == ASSIMP_strincmp(szCurrent,"time",4) && - IsSpaceOrNewLine(*(szCurrent+4))) + if (TokenMatch(szCurrent,"time",4)) { - szCurrent += 5; // NOTE: The doc says that time values COULD be negative ... - // note2: this is the shape key -> valve docs + // NOTE2: this is the shape key -> valve docs int iTime = 0; if(!ParseSignedInt(szCurrent,&szCurrent,iTime) || configFrameID != (unsigned int)iTime)break; SkipLine(szCurrent,&szCurrent); } else { - ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true); - if(3 == ++iCurIndex) + if(0 == iCurIndex) { asTriangles.push_back(SMD::Face()); - iCurIndex = 0; } + if (++iCurIndex == 3)iCurIndex = 0; + ParseVertex(szCurrent,&szCurrent,asTriangles.back().avVertices[iCurIndex],true); } } - if (iCurIndex) + if (iCurIndex != 2 && !asTriangles.empty()) { - // no degenerates, so let this triangle - aszTextures.pop_back(); + // we want to no degenerates, so throw this triangle away + asTriangles.pop_back(); } SkipSpacesAndLineEnd(szCurrent,&szCurrent); @@ -853,18 +843,12 @@ void SMDImporter::ParseSkeletonSection(const char* szCurrent, if(!SkipSpacesAndLineEnd(szCurrent,&szCurrent)) break; // "end\n" - Ends the skeleton section - if (0 == ASSIMP_strincmp(szCurrent,"end",3) && - IsSpaceOrNewLine(*(szCurrent+3))) - { - szCurrent += 4; - //SkipLine(szCurrent,&szCurrent); + if (TokenMatch(szCurrent,"end",3)) break; - } + // "time \n" - Specifies the current animation frame - else if (0 == ASSIMP_strincmp(szCurrent,"time",4) && - IsSpaceOrNewLine(*(szCurrent+4))) + else if (TokenMatch(szCurrent,"time",4)) { - szCurrent += 5; // NOTE: The doc says that time values COULD be negative ... if(!ParseSignedInt(szCurrent,&szCurrent,iTime))break; @@ -876,12 +860,12 @@ void SMDImporter::ParseSkeletonSection(const char* szCurrent, *szCurrentOut = szCurrent; } +// ------------------------------------------------------------------------------------------------ #define SMDI_PARSE_RETURN { \ SkipLine(szCurrent,&szCurrent); \ *szCurrentOut = szCurrent; \ return; \ } - // ------------------------------------------------------------------------------------------------ // Parse a node line void SMDImporter::ParseNodeInfo(const char* szCurrent, @@ -941,6 +925,7 @@ void SMDImporter::ParseNodeInfo(const char* szCurrent, // go to the beginning of the next line SMDI_PARSE_RETURN; } + // ------------------------------------------------------------------------------------------------ // Parse a skeleton element void SMDImporter::ParseSkeletonElement(const char* szCurrent, @@ -997,7 +982,7 @@ void SMDImporter::ParseSkeletonElement(const char* szCurrent, SMDI_PARSE_RETURN; } // build the transformation matrix of the key - key.matrix.FromEulerAngles(vRot.x,vRot.y,vRot.z); + key.matrix.FromEulerAnglesXYZ(vRot.x,vRot.y,vRot.z); { aiMatrix4x4 mTemp; mTemp.a4 = vPos.x; @@ -1009,6 +994,7 @@ void SMDImporter::ParseSkeletonElement(const char* szCurrent, // go to the beginning of the next line SMDI_PARSE_RETURN; } + // ------------------------------------------------------------------------------------------------ // Parse a triangle void SMDImporter::ParseTriangle(const char* szCurrent, @@ -1027,10 +1013,10 @@ void SMDImporter::ParseTriangle(const char* szCurrent, const char* szLast = szCurrent; while (!IsSpaceOrNewLine(*szCurrent++)); - face.iTexture = GetTextureIndex(std::string(szLast, - (uintptr_t)szCurrent-(uintptr_t)szLast)); + // ... and get the index that belongs to this file name + face.iTexture = GetTextureIndex(std::string(szLast,(uintptr_t)szCurrent-(uintptr_t)szLast)); - SkipLine(szCurrent,&szCurrent); + SkipSpacesAndLineEnd(szCurrent,&szCurrent); // load three vertices for (unsigned int iVert = 0; iVert < 3;++iVert) @@ -1040,6 +1026,7 @@ void SMDImporter::ParseTriangle(const char* szCurrent, } *szCurrentOut = szCurrent; } + // ------------------------------------------------------------------------------------------------ // Parse a float bool SMDImporter::ParseFloat(const char* szCurrent, @@ -1051,6 +1038,7 @@ bool SMDImporter::ParseFloat(const char* szCurrent, *szCurrentOut = fast_atof_move(szCurrent,out); return true; } + // ------------------------------------------------------------------------------------------------ // Parse an unsigned int bool SMDImporter::ParseUnsignedInt(const char* szCurrent, @@ -1062,6 +1050,7 @@ bool SMDImporter::ParseUnsignedInt(const char* szCurrent, out = strtol10(szCurrent,szCurrentOut); return true; } + // ------------------------------------------------------------------------------------------------ // Parse a signed int bool SMDImporter::ParseSignedInt(const char* szCurrent, @@ -1073,6 +1062,7 @@ bool SMDImporter::ParseSignedInt(const char* szCurrent, out = strtol10s(szCurrent,szCurrentOut); return true; } + // ------------------------------------------------------------------------------------------------ // Parse a vertex void SMDImporter::ParseVertex(const char* szCurrent, @@ -1143,8 +1133,10 @@ void SMDImporter::ParseVertex(const char* szCurrent, i = vertex.aiBoneLinks.begin(); i != vertex.aiBoneLinks.end();++i) { - if(!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first))SMDI_PARSE_RETURN; - if(!ParseFloat(szCurrent,&szCurrent,(*i).second))SMDI_PARSE_RETURN; + if(!ParseUnsignedInt(szCurrent,&szCurrent,(*i).first)) + SMDI_PARSE_RETURN; + if(!ParseFloat(szCurrent,&szCurrent,(*i).second)) + SMDI_PARSE_RETURN; } // go to the beginning of the next line diff --git a/code/StandardShapes.cpp b/code/StandardShapes.cpp index 58979af1c..c914e73af 100644 --- a/code/StandardShapes.cpp +++ b/code/StandardShapes.cpp @@ -114,6 +114,7 @@ aiMesh* StandardShapes::MakeMesh(const std::vector& positions, { if (positions.size() & numIndices || positions.empty() || !numIndices)return NULL; + // Determine which kinds of primitives the mesh will consist of aiMesh* out = new aiMesh(); switch (numIndices) { @@ -182,18 +183,18 @@ unsigned int StandardShapes::MakeIcosahedron(std::vector& positions) const float t = (1.f + 2.236067977f)/2.f; const float s = sqrt(1.f + t*t); - aiVector3D v0 = aiVector3D(t,1.f, 0.f)/s; - aiVector3D v1 = aiVector3D(-t,1.f, 0.f)/s; - aiVector3D v2 = aiVector3D(t,-1.f, 0.f)/s; - aiVector3D v3 = aiVector3D(-t,-1.f, 0.f)/s; - aiVector3D v4 = aiVector3D(1.f, 0.f, t)/s; - aiVector3D v5 = aiVector3D(1.f, 0.f,-t)/s; - aiVector3D v6 = aiVector3D(-1.f, 0.f,t)/s; - aiVector3D v7 = aiVector3D(-1.f, 0.f,-t)/s; - aiVector3D v8 = aiVector3D(0.f, t, 1.f)/s; - aiVector3D v9 = aiVector3D(0.f,-t, 1.f)/s; - aiVector3D v10 = aiVector3D(0.f, t,-1.f)/s; - aiVector3D v11 = aiVector3D(0.f,-t,-1.f)/s; + const aiVector3D v0 = aiVector3D(t,1.f, 0.f)/s; + const aiVector3D v1 = aiVector3D(-t,1.f, 0.f)/s; + const aiVector3D v2 = aiVector3D(t,-1.f, 0.f)/s; + const aiVector3D v3 = aiVector3D(-t,-1.f, 0.f)/s; + const aiVector3D v4 = aiVector3D(1.f, 0.f, t)/s; + const aiVector3D v5 = aiVector3D(1.f, 0.f,-t)/s; + const aiVector3D v6 = aiVector3D(-1.f, 0.f,t)/s; + const aiVector3D v7 = aiVector3D(-1.f, 0.f,-t)/s; + const aiVector3D v8 = aiVector3D(0.f, t, 1.f)/s; + const aiVector3D v9 = aiVector3D(0.f,-t, 1.f)/s; + const aiVector3D v10 = aiVector3D(0.f, t,-1.f)/s; + const aiVector3D v11 = aiVector3D(0.f,-t,-1.f)/s; ADD_TRIANGLE(v0,v8,v4); ADD_TRIANGLE(v0,v5,v10); @@ -232,26 +233,26 @@ unsigned int StandardShapes::MakeDodecahedron(std::vector& positions const float b = sqrt((3.f-2.23606797f)/6.f); const float c = sqrt((3.f+2.23606797f)/6.f); - aiVector3D v0 = aiVector3D(a,a,a); - aiVector3D v1 = aiVector3D(a,a,-a); - aiVector3D v2 = aiVector3D(a,-a,a); - aiVector3D v3 = aiVector3D(a,-a,-a); - aiVector3D v4 = aiVector3D(-a,a,a); - aiVector3D v5 = aiVector3D(-a,a,-a); - aiVector3D v6 = aiVector3D(-a,-a,a); - aiVector3D v7 = aiVector3D(-a,-a,-a); - aiVector3D v8 = aiVector3D(b,c,0.f); - aiVector3D v9 = aiVector3D(-b,c,0.f); - aiVector3D v10 = aiVector3D(b,-c,0.f); - aiVector3D v11 = aiVector3D(-b,-c,0.f); - aiVector3D v12 = aiVector3D(c, 0.f, b); - aiVector3D v13 = aiVector3D(c, 0.f, -b); - aiVector3D v14 = aiVector3D(-c, 0.f, b); - aiVector3D v15 = aiVector3D(-c, 0.f, -b); - aiVector3D v16 = aiVector3D(0.f, b, c); - aiVector3D v17 = aiVector3D(0.f, -b, c); - aiVector3D v18 = aiVector3D(0.f, b, -c); - aiVector3D v19 = aiVector3D(0.f, -b, -c); + const aiVector3D v0 = aiVector3D(a,a,a); + const aiVector3D v1 = aiVector3D(a,a,-a); + const aiVector3D v2 = aiVector3D(a,-a,a); + const aiVector3D v3 = aiVector3D(a,-a,-a); + const aiVector3D v4 = aiVector3D(-a,a,a); + const aiVector3D v5 = aiVector3D(-a,a,-a); + const aiVector3D v6 = aiVector3D(-a,-a,a); + const aiVector3D v7 = aiVector3D(-a,-a,-a); + const aiVector3D v8 = aiVector3D(b,c,0.f); + const aiVector3D v9 = aiVector3D(-b,c,0.f); + const aiVector3D v10 = aiVector3D(b,-c,0.f); + const aiVector3D v11 = aiVector3D(-b,-c,0.f); + const aiVector3D v12 = aiVector3D(c, 0.f, b); + const aiVector3D v13 = aiVector3D(c, 0.f, -b); + const aiVector3D v14 = aiVector3D(-c, 0.f, b); + const aiVector3D v15 = aiVector3D(-c, 0.f, -b); + const aiVector3D v16 = aiVector3D(0.f, b, c); + const aiVector3D v17 = aiVector3D(0.f, -b, c); + const aiVector3D v18 = aiVector3D(0.f, b, -c); + const aiVector3D v19 = aiVector3D(0.f, -b, -c); ADD_PENTAGON(v0, v8, v9, v4, v16); ADD_PENTAGON(v0, v12, v13, v1, v8); @@ -274,12 +275,12 @@ unsigned int StandardShapes::MakeOctahedron(std::vector& positions) { positions.reserve(positions.size()+24); - aiVector3D v0 = aiVector3D(1.0f, 0.f, 0.f) ; - aiVector3D v1 = aiVector3D(-1.0f, 0.f, 0.f); - aiVector3D v2 = aiVector3D(0.f, 1.0f, 0.f); - aiVector3D v3 = aiVector3D(0.f, -1.0f, 0.f); - aiVector3D v4 = aiVector3D(0.f, 0.f, 1.0f); - aiVector3D v5 = aiVector3D(0.f, 0.f, -1.0f); + const aiVector3D v0 = aiVector3D(1.0f, 0.f, 0.f) ; + const aiVector3D v1 = aiVector3D(-1.0f, 0.f, 0.f); + const aiVector3D v2 = aiVector3D(0.f, 1.0f, 0.f); + const aiVector3D v3 = aiVector3D(0.f, -1.0f, 0.f); + const aiVector3D v4 = aiVector3D(0.f, 0.f, 1.0f); + const aiVector3D v5 = aiVector3D(0.f, 0.f, -1.0f); ADD_TRIANGLE(v4,v0,v2); ADD_TRIANGLE(v4,v2,v1); @@ -301,10 +302,10 @@ unsigned int StandardShapes::MakeTetrahedron(std::vector& positions) const float a = 1.41421f/3.f; const float b = 2.4494f/3.f; - aiVector3D v0 = aiVector3D(0.f,0.f,1.f); - aiVector3D v1 = aiVector3D(2*a,0,-1.f/3.f); - aiVector3D v2 = aiVector3D(-a,b,-1.f/3.f); - aiVector3D v3 = aiVector3D(-a,-b,-1.f/3.f); + const aiVector3D v0 = aiVector3D(0.f,0.f,1.f); + const aiVector3D v1 = aiVector3D(2*a,0,-1.f/3.f); + const aiVector3D v2 = aiVector3D(-a,b,-1.f/3.f); + const aiVector3D v3 = aiVector3D(-a,-b,-1.f/3.f); ADD_TRIANGLE(v0,v1,v2); ADD_TRIANGLE(v0,v2,v3); @@ -318,16 +319,16 @@ unsigned int StandardShapes::MakeHexahedron(std::vector& positions, bool polygons /*= false*/) { positions.reserve(positions.size()+36); - float length = 1.f/1.73205080f; + const float length = 1.f/1.73205080f; - aiVector3D v0 = aiVector3D(-1.f,-1.f,-1.f)*length; - aiVector3D v1 = aiVector3D(1.f,-1.f,-1.f)*length; - aiVector3D v2 = aiVector3D(1.f,1.f,-1.f)*length; - aiVector3D v3 = aiVector3D(-1.f,1.f,-1.f)*length; - aiVector3D v4 = aiVector3D(-1.f,-1.f,1.f)*length; - aiVector3D v5 = aiVector3D(1.f,-1.f,1.f)*length; - aiVector3D v6 = aiVector3D(1.f,1.f,1.f)*length; - aiVector3D v7 = aiVector3D(-1.f,1.f,1.f)*length; + const aiVector3D v0 = aiVector3D(-1.f,-1.f,-1.f)*length; + const aiVector3D v1 = aiVector3D(1.f,-1.f,-1.f)*length; + const aiVector3D v2 = aiVector3D(1.f,1.f,-1.f)*length; + const aiVector3D v3 = aiVector3D(-1.f,1.f,-1.f)*length; + const aiVector3D v4 = aiVector3D(-1.f,-1.f,1.f)*length; + const aiVector3D v5 = aiVector3D(1.f,-1.f,1.f)*length; + const aiVector3D v6 = aiVector3D(1.f,1.f,1.f)*length; + const aiVector3D v7 = aiVector3D(-1.f,1.f,1.f)*length; ADD_QUAD(v0,v3,v2,v1); ADD_QUAD(v0,v1,v5,v4); @@ -347,22 +348,95 @@ unsigned int StandardShapes::MakeHexahedron(std::vector& positions, void StandardShapes::MakeSphere(unsigned int tess, std::vector& positions) { + // Reserve enough storage. Every subdivision + // splits each triangle in 4, the icosahedron consists of 60 verts + positions.reserve(positions.size()+60 * integer_pow(4, tess)); + + // Construct an icosahedron to start with MakeIcosahedron(positions); + // ... and subdivide it until the requested output + // tesselation is reached for (unsigned int i = 0; i& positions, - bool bOpened /*= false*/) +void StandardShapes::MakeCone(float height,float radius1, + float radius2,unsigned int tess, + std::vector& positions,bool bOpen /*= false */) { + // Sorry, a cone with less than 3 segments makes + // ABSOLUTELY NO SENSE + if (tess < 3 || !height) + return; + + // No negative radii + radius1 = fabs(radius1); + radius2 = fabs(radius2); + + float halfHeight = height / 2; + + // radius1 is always the smaller one + if (radius2 > radius1) + { + std::swap(radius2,radius1); + halfHeight = -halfHeight; + } + + // Use a large epsilon to check whether the cone is pointy + if (radius1 < (radius2-radius1)*10e-3f)radius1 = 0.f; + + // We will need 3*2 verts per segment + 3*2 verts per segment + // if the cone is closed + const unsigned int mem = tess*6 + (!bOpen ? tess*3 * (radius1 ? 2 : 1) : 0); + positions.reserve(mem); + + // Now construct all segments + const float angle_delta = (float)AI_MATH_TWO_PI / tess; + const float angle_max = (float)AI_MATH_TWO_PI; + + float s = 1.f; // cos(angle == 0); + float t = 0.f; // sin(angle == 0); + + for (float angle = 0.f; angle < angle_max; ) + { + const aiVector3D v1 = aiVector3D (s * radius1, -halfHeight, t * radius1 ); + const aiVector3D v2 = aiVector3D (s * radius2, halfHeight, t * radius2 ); + + const float next = angle + angle_delta; + float s2 = cos(next); + float t2 = sin(next); + + const aiVector3D v3 = aiVector3D (s2 * radius2, halfHeight, t2 * radius2 ); + const aiVector3D v4 = aiVector3D (s2 * radius1, -halfHeight, t2 * radius1 ); + + positions.push_back(v1); + positions.push_back(v3); + positions.push_back(v2); + positions.push_back(v4); + positions.push_back(v3); + positions.push_back(v1); + + if (!bOpen) + { + // generate the end 'cap' + positions.push_back(aiVector3D(s * radius2, halfHeight, t * radius2 )); + positions.push_back(aiVector3D(0.f, halfHeight, 0.f)); + positions.push_back(aiVector3D(s2 * radius2, halfHeight, t2 * radius2 )); + + if (radius1) + { + // generate the other end 'cap' + positions.push_back(aiVector3D(s * radius1, -halfHeight, t * radius1 )); + positions.push_back(aiVector3D(0.f, -halfHeight, 0.f)); + positions.push_back(aiVector3D(s2 * radius1, -halfHeight, t2 * radius1 )); + } + } + s = s2; + t = t2; + angle = next; + } } // ------------------------------------------------------------------------------------------------ @@ -373,7 +447,7 @@ void StandardShapes::MakeCircle( unsigned int tess, std::vector& positions) { - //aiVector3D current = aiVector3D ( normal.x, + // todo } } // ! Assimp diff --git a/code/StandardShapes.h b/code/StandardShapes.h index c1c3cf94a..bb397f603 100644 --- a/code/StandardShapes.h +++ b/code/StandardShapes.h @@ -153,28 +153,28 @@ public: * * |-----| <- radius 1 * - * __x__ <- center 1 - * / \ - * / \ - * / \ - * / \ - * /______x______\ <- center 2 + * __x__ <- ] + * / \ | height + * / \ | + * / \ | + * / \ | + * /______x______\ <- ] <- end cap * * |-------------| <- radius 2 * * @endcode * - * @param center1 First center point + * @param height Height of the cone * @param radius1 First radius - * @param center2 Second center point * @param radius2 Second radius - * @param tess Number of subdivisions - * @param bOpened true for an open cone/cylinder. - * @param positions Receives output triangles. + * @param tess Number of triangles. + * @param bOpened true for an open cone/cylinder. An open shape has + * no 'end caps' + * @param positions Receives output triangles */ - static void MakeCone(aiVector3D& center1,float radius1, - aiVector3D& center2,float radius2,unsigned int tess, - std::vector& positions,bool bOpened = false); + static void MakeCone(float height,float radius1, + float radius2,unsigned int tess, + std::vector& positions,bool bOpen= false); // ---------------------------------------------------------------- @@ -185,7 +185,7 @@ public: * @param normal Normal vector of the circle. * This is also the normal vector of all triangles generated by * this function. - * @param tess Number of triangles + * @param tess Number of segments. * @param positions Receives output triangles. */ static void MakeCircle(const aiVector3D& center, const aiVector3D& normal, diff --git a/code/StringComparison.h b/code/StringComparison.h index e8db1c432..236026f43 100644 --- a/code/StringComparison.h +++ b/code/StringComparison.h @@ -54,7 +54,6 @@ namespace Assimp // @param max Maximum number of characters to be written, including '\0' // @param number Number to be written // @return Number of bytes written. Including '\0'. -// --------------------------------------------------------------------------- inline unsigned int itoa10( char* out, unsigned int max, int32_t number) { ai_assert(NULL != out); @@ -96,7 +95,6 @@ inline unsigned int itoa10( char* out, unsigned int max, int32_t number) // Secure template overload // The compiler should choose this function if he is able to determine the // size of the array automatically. -// --------------------------------------------------------------------------- template inline unsigned int itoa10( char(& out)[length], int32_t number) { @@ -113,13 +111,16 @@ inline unsigned int itoa10( char(& out)[length], int32_t number) * \param s1 First input string * \param s2 Second input string */ -// --------------------------------------------------------------------------- inline int ASSIMP_stricmp(const char *s1, const char *s2) { #if (defined _MSC_VER) return ::_stricmp(s1,s2); +#elif defined( __GNUC__ ) + + return ::strcasecmp(s1,s2); + #else register char c1, c2; do @@ -136,7 +137,6 @@ inline int ASSIMP_stricmp(const char *s1, const char *s2) // --------------------------------------------------------------------------- /** \brief Case independent comparison of two std::strings */ -// --------------------------------------------------------------------------- inline int ASSIMP_stricmp(const std::string& a, const std::string& b) { register int i = (int)b.length()-(int)a.length(); @@ -154,13 +154,16 @@ inline int ASSIMP_stricmp(const std::string& a, const std::string& b) * \param s2 Second input string * \param n Macimum number of characters to compare */ -// --------------------------------------------------------------------------- inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) { #if (defined _MSC_VER) return ::_strnicmp(s1,s2,n); +#elif defined( __GNUC__ ) + + return ::strncasecmp(s1,s2, n); + #else register char c1, c2; unsigned int p = 0; @@ -175,6 +178,18 @@ inline int ASSIMP_strincmp(const char *s1, const char *s2, unsigned int n) return c1 - c2; #endif } + + +// --------------------------------------------------------------------------- +// Evaluates an integer power. +inline unsigned int integer_pow (unsigned int base, unsigned int power) +{ + unsigned int res = 1; + for (unsigned int i = 0; i < power;++i) + res *= base; + + return res; } +} // end of namespace #endif // ! AI_STRINGCOMPARISON_H_INC diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index 4b7900b67..986dbd01c 100644 --- a/code/ValidateDataStructure.cpp +++ b/code/ValidateDataStructure.cpp @@ -511,7 +511,7 @@ void ValidateDSProcess::Validate( const aiMesh* pMesh) // check whether all bone weights for a vertex sum to 1.0 ... for (unsigned int i = 0; i < pMesh->mNumVertices;++i) { - if (afSum[i] && (afSum[i] <= 0.995 || afSum[i] >= 1.005)) + if (afSum[i] && (afSum[i] <= 0.94 || afSum[i] >= 1.05)) { ReportWarning("aiMesh::mVertices[%i]: bone weight sum != 1.0 (sum is %f)",i,afSum[i]); } diff --git a/include/IOSystem.h b/include/IOSystem.h index 15c6050cd..b0621e163 100644 --- a/include/IOSystem.h +++ b/include/IOSystem.h @@ -78,7 +78,7 @@ public: /** Compares two paths and check whether the point to identical files. * * The dummy implementation of this virtual performs a - * case-insensitive comparison of the path strings. + * case-insensitive comparison of the absolute path strings. * @param one First file * @param second Second file * @return true if the paths point to the same file. The file needn't diff --git a/include/aiMatrix4x4.h b/include/aiMatrix4x4.h index df762d307..e3caa4ab6 100644 --- a/include/aiMatrix4x4.h +++ b/include/aiMatrix4x4.h @@ -118,8 +118,8 @@ struct aiMatrix4x4 * \param y Rotation angle for the y-axis, in radians * \param z Rotation angle for the z-axis, in radians */ - inline void FromEulerAngles(float x, float y, float z); - inline void FromEulerAngles(const aiVector3D& blubb); + inline void FromEulerAnglesXYZ(float x, float y, float z); + inline void FromEulerAnglesXYZ(const aiVector3D& blubb); /** \brief Returns a rotation matrix for a rotation around the x axis @@ -158,6 +158,18 @@ struct aiMatrix4x4 */ static aiMatrix4x4& Translation( const aiVector3D& v, aiMatrix4x4& out); + + /** A function for creating a rotation matrix that rotates a vector called + * "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in colum-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ + static aiMatrix4x4& FromToMatrix(const aiVector3D& from, + const aiVector3D& to, aiMatrix4x4& out); + #endif // __cplusplus float a1, a2, a3, a4; diff --git a/include/aiMatrix4x4.inl b/include/aiMatrix4x4.inl index c77d1707a..d1dfce9c2 100644 --- a/include/aiMatrix4x4.inl +++ b/include/aiMatrix4x4.inl @@ -87,12 +87,16 @@ inline aiMatrix4x4& aiMatrix4x4::Inverse() float det = Determinant(); if(det == 0.0f) { + // Matrix not invertible. Setting all elements to nan is not really + // correct in a mathematical sense but it is easy to debug for the + // programmer. const float nan = std::numeric_limits::quiet_NaN(); *this = aiMatrix4x4( nan,nan,nan,nan, nan,nan,nan,nan, nan,nan,nan,nan, nan,nan,nan,nan); + return *this; } @@ -204,43 +208,62 @@ inline void aiMatrix4x4::DecomposeNoScaling (aiQuaternion& rotation, rotation = aiQuaternion((aiMatrix3x3)_this); } // --------------------------------------------------------------------------- -inline void aiMatrix4x4::FromEulerAngles(const aiVector3D& blubb) +inline void aiMatrix4x4::FromEulerAnglesXYZ(const aiVector3D& blubb) { - FromEulerAngles(blubb.x,blubb.y,blubb.z); + FromEulerAnglesXYZ(blubb.x,blubb.y,blubb.z); } // --------------------------------------------------------------------------- -inline void aiMatrix4x4::FromEulerAngles(float x, float y, float z) +inline void aiMatrix4x4::FromEulerAnglesXYZ(float x, float y, float z) { aiMatrix4x4& _this = *this; - const float A = ::cos(x); - const float B = ::sin(x); - const float C = ::cos(y); - const float D = ::sin(y); - const float E = ::cos(z); - const float F = ::sin(z); - const float AD = A * D; - const float BD = B * D; - _this.a1 = C * E; - _this.a2 = -C * F; - _this.a3 = D; - _this.b1 = BD * E + A * F; - _this.b2 = -BD * F + A * E; - _this.b3 = -B * C; - _this.c1 = -AD * E + B * F; - _this.c2 = AD * F + B * E; - _this.c3 = A * C; - _this.a4 = _this.b4 = _this.c4 = _this.d1 = _this.d2 = _this.d3 = 0.0f; - _this.d4 = 1.0f; + float cr = cos( x ); + float sr = sin( x ); + float cp = cos( y ); + float sp = sin( y ); + float cy = cos( z ); + float sy = sin( z ); + + _this.a1 = cp*cy ; + _this.a2 = cp*sy; + _this.a3 = -sp ; + + float srsp = sr*sp; + float crsp = cr*sp; + + _this.b1 = srsp*cy-cr*sy ; + _this.b2 = srsp*sy+cr*cy ; + _this.b3 = sr*cp ; + + _this.c1 = crsp*cy+sr*sy ; + _this.c2 = crsp*sy-sr*cy ; + _this.c3 = cr*cp ; + } // --------------------------------------------------------------------------- inline bool aiMatrix4x4::IsIdentity() const { - return !(a1 != 1.0f || a2 || a3 || a4 || - b1 || b2 != 1.0f || b3 || b4 || - c1 || c2 || c3 != 1.0f || a4 || - d1 || d2 || d3 || d4 != 1.0f); + // Use a small epsilon to solve floating-point inaccuracies + const static float epsilon = 10e-3f; + + return (a2 <= epsilon && a2 >= -epsilon && + a3 <= epsilon && a3 >= -epsilon && + a4 <= epsilon && a4 >= -epsilon && + b1 <= epsilon && b1 >= -epsilon && + b3 <= epsilon && b3 >= -epsilon && + b4 <= epsilon && b4 >= -epsilon && + c1 <= epsilon && c1 >= -epsilon && + c2 <= epsilon && c2 >= -epsilon && + c3 <= epsilon && c3 >= -epsilon && + d1 <= epsilon && d1 >= -epsilon && + d2 <= epsilon && d2 >= -epsilon && + d3 <= epsilon && d3 >= -epsilon && + a1 <= 1.f+epsilon && a1 >= 1.f-epsilon && + b2 <= 1.f+epsilon && b2 >= 1.f-epsilon && + c3 <= 1.f+epsilon && c3 >= 1.f-epsilon && + d4 <= 1.f+epsilon && d4 >= 1.f-epsilon); } + // --------------------------------------------------------------------------- inline aiMatrix4x4& aiMatrix4x4::RotationX(float a, aiMatrix4x4& out) { @@ -312,5 +335,95 @@ inline aiMatrix4x4& aiMatrix4x4::Translation( const aiVector3D& v, aiMatrix4x4& return out; } +// --------------------------------------------------------------------------- +/** A function for creating a rotation matrix that rotates a vector called + * "from" into another vector called "to". + * Input : from[3], to[3] which both must be *normalized* non-zero vectors + * Output: mtx[3][3] -- a 3x3 matrix in colum-major form + * Authors: Tomas Möller, John Hughes + * "Efficiently Building a Matrix to Rotate One Vector to Another" + * Journal of Graphics Tools, 4(4):1-4, 1999 + */ +// --------------------------------------------------------------------------- +inline aiMatrix4x4& aiMatrix4x4::FromToMatrix(const aiVector3D& from, + const aiVector3D& to, aiMatrix4x4& mtx) +{ + const aiVector3D v = from ^ to; + const float e = from * to; + const float f = (e < 0)? -e:e; + + if (f > 1.0 - 0.00001f) /* "from" and "to"-vector almost parallel */ + { + aiVector3D u,v; /* temporary storage vectors */ + aiVector3D x; /* vector most nearly orthogonal to "from" */ + + x.x = (from.x > 0.0)? from.x : -from.x; + x.y = (from.y > 0.0)? from.y : -from.y; + x.z = (from.z > 0.0)? from.z : -from.z; + + if (x.x < x.y) + { + if (x.x < x.z) + { + x.x = 1.0; x.y = x.z = 0.0; + } + else + { + x.z = 1.0; x.y = x.z = 0.0; + } + } + else + { + if (x.y < x.z) + { + x.y = 1.0; x.x = x.z = 0.0; + } + else + { + x.z = 1.0; x.x = x.y = 0.0; + } + } + + u.x = x.x - from.x; u.y = x.y - from.y; u.z = x.z - from.z; + v.x = x.x - to.x; v.y = x.y - to.y; v.z = x.z - to.z; + + const float c1 = 2.0f / (u * u); + const float c2 = 2.0f / (v * v); + const float c3 = c1 * c2 * (u * v); + + for (unsigned int i = 0; i < 3; i++) + { + for (unsigned int j = 0; j < 3; j++) + { + mtx[i][j] = - c1 * u[i] * u[j] - c2 * v[i] * v[j] + + c3 * v[i] * u[j]; + } + mtx[i][i] += 1.0; + } + } + else /* the most common case, unless "from"="to", or "from"=-"to" */ + { + /* ... use this hand optimized version (9 mults less) */ + const float h = 1.0f/(1.0f + e); /* optimization by Gottfried Chen */ + const float hvx = h * v.x; + const float hvz = h * v.z; + const float hvxy = hvx * v.y; + const float hvxz = hvx * v.z; + const float hvyz = hvz * v.y; + mtx[0][0] = e + hvx * v.x; + mtx[0][1] = hvxy - v.z; + mtx[0][2] = hvxz + v.y; + + mtx[1][0] = hvxy + v.z; + mtx[1][1] = e + h * v.y * v.y; + mtx[1][2] = hvyz - v.x; + + mtx[2][0] = hvxz - v.y; + mtx[2][1] = hvyz + v.x; + mtx[2][2] = e + hvz * v.z; + } + return mtx; +} + #endif // __cplusplus #endif // AI_MATRIX4x4_INL_INC diff --git a/test/IRR/multipleAnimators.irr b/test/IRR/multipleAnimators.irr index 998868d4c73b5d2c93fc6c438583aa50d848e99c..fb468aa11da107c0b4a02ac1419a5bed6555f329 100644 GIT binary patch delta 20 ccmexk{Lgs92F}T=xWp#saA|FR$H^xE0A!vBA^-pY delta 20 ccmexo{Kt612F}SX>>`u%xU@EZ;N%kk0Ajrd`v3p{ diff --git a/test/NFF/NFF/ManyEarthsNotJustOne.nff b/test/NFF/NFF/ManyEarthsNotJustOne.nff new file mode 100644 index 000000000..c1261a1d0 --- /dev/null +++ b/test/NFF/NFF/ManyEarthsNotJustOne.nff @@ -0,0 +1,25 @@ +b 0.078 0.361 0.753 +v +from 2.1 1.3 1.7 +at 0 0 0 +up 0 0 1 +angle 45 +hither 0.01 +resolution 512 512 +l 4 3 2 +l 1 -4 4 +l -3 1 5 + +f 1 0.9 0.7 0.5 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthSpherical.jpg +s 0 0 0 0.5 + +f 1 0.9 0.7 0.5 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthSpherical.jpg +s 0.272166 0.272166 0.544331 0.16665 +s 0.643951 0.172546 1.11022e-16 0.16665 +s 0.172546 0.643951 1.11022e-16 0.16665 +s -0.371785 0.0996195 0.544331 0.16665 +s -0.471405 0.471405 1.11022e-16 0.16665 +s -0.643951 -0.172546 1.11022e-16 0.16665 +s 0.0996195 -0.371785 0.544331 0.16665 +s -0.172546 -0.643951 1.11022e-16 0.16665 +s 0.471405 -0.471405 1.11022e-16 0.16665 diff --git a/test/NFF/NFF/cone.nff b/test/NFF/NFF/cone.nff new file mode 100644 index 000000000..744c2d1c9 --- /dev/null +++ b/test/NFF/NFF/cone.nff @@ -0,0 +1,26 @@ + +#red +f 1.0 0.0 0.0 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthCylindric.jpg + + +tess 4 + + +# a simple cone - should go directly through the center +c +10 10 10 3 +-10 -10 -10 6 + + +f 1.0 1.0 1.0 0.5 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthSpherical.jpg +s -10 -10 -10 2 +s 10 10 10 3 + + +#white +f 1.0 1.0 1.0 0.5 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthCylindric.jpg + +# another cone, closed this time +c +20 20 20 6 +11 11 11 0 \ No newline at end of file diff --git a/test/NFF/NFF/cylinder.nff b/test/NFF/NFF/cylinder.nff new file mode 100644 index 000000000..4f0c78f87 --- /dev/null +++ b/test/NFF/NFF/cylinder.nff @@ -0,0 +1,10 @@ + +#red +f 1 0.9 0.7 0.5 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthCylindric.jpg + +tess 4 + +# a simple cylinder - should go directly through the center +c +10 10 10 6 +-10 -10 -10 6 \ No newline at end of file diff --git a/test/NFF/NFF/positionTest.nff b/test/NFF/NFF/positionTest.nff index 304aa9c66..375b51db7 100644 --- a/test/NFF/NFF/positionTest.nff +++ b/test/NFF/NFF/positionTest.nff @@ -1,27 +1,36 @@ +# Enable high-quality subdivision tess 4 + +# This represents a C4H10 molecule (Butan) + + f 0.3 0.3 0.3 1 1 - -# a centered sphere - - s 0 0 0 5 - s 20 0 0 5 - s 40 0 0 5 - s 60 0 0 5 +# Switch to white f 1 1 1 1 1 s -5 0 0 3 - s 0 5 0 3 s 0 -5 0 3 +s 20 5 0 3 +s 20 -5 0 3 +s 40 5 0 3 +s 40 -5 0 3 +s 60 5 0 3 +s 60 -5 0 3 +s 65 0 0 3 +# Now a tube to connect them +c +0 0 0 0.8 +60 0 0 0.8 diff --git a/test/NFF/NFF/spheres.nff b/test/NFF/NFF/spheres.nff index c03c52d49..8a313c921 100644 --- a/test/NFF/NFF/spheres.nff +++ b/test/NFF/NFF/spheres.nff @@ -5,13 +5,13 @@ s 5.0 5.0 5.0 3.0 #blue -f 0.0 0.0 1.0 0 1 1 +f 0.0 0.0 1.0 0 1 1 1 # Another simple sphere s 5.0 4.0 8.0 3.0 #green -f 0.0 1.0 0.0 0 1 1 +f 1.0 1.0 1.0 0.5 0.5 45.2776 0 1 ./../../LWOFiles/LWo2/MappingModes/EarthCylindric.jpg # And another one s 1.0 -4.0 2.0 4.0 2 2 @@ -19,10 +19,6 @@ s 1.0 -4.0 2.0 4.0 2 2 #red f 1.0 0.0 0.0 0 1 1 -# a simple cone -c 10 10 5 3 -c 14 14 3 6 - # An icosahedron tess 0