From de50bb915b59eece1ae5417888f1aa4d492423ff Mon Sep 17 00:00:00 2001 From: aramis_acg Date: Wed, 11 Mar 2009 22:56:16 +0000 Subject: [PATCH] MD5 - added some extra validations to prevent unwanted crashes - fixed stand-alone loading of MD5ANIM files - MD5CAMERA working very well now ... JoinIdenticalVertices - an exception case where multiple vertices with the same position are assigned to different bones is handled now -> no crash anymore SkeletonMeshBuilder - proper material naming - two-sided flag is set for material - enforcing 'rahd' per-face normal vectors for better visual appearance ValidateDataStructure - code cleanup MaterialSystem - code cleanup LimitBoneWeights - code cleanup git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@364 67173fc5-114c-0410-ac8e-9d2fd5bffc1f --- code/JoinVerticesProcess.cpp | 48 ++++-- code/LimitBoneWeightsProcess.cpp | 9 +- code/MD5Loader.cpp | 65 ++++---- code/MaterialSystem.cpp | 278 +++++++++++++++---------------- code/SkeletonMeshBuilder.cpp | 27 ++- code/ValidateDataStructure.cpp | 53 +----- 6 files changed, 235 insertions(+), 245 deletions(-) diff --git a/code/JoinVerticesProcess.cpp b/code/JoinVerticesProcess.cpp index ead42446d..aaee1aaf6 100644 --- a/code/JoinVerticesProcess.cpp +++ b/code/JoinVerticesProcess.cpp @@ -145,19 +145,17 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) float posEpsilonSqr; SpatialSort* vertexFinder = NULL; SpatialSort _vertexFinder; - if (shared) - { + if (shared) { std::vector >* avf; shared->GetProperty(AI_SPP_SPATIAL_SORT,avf); if (avf) { std::pair& blubb = avf->operator [] (meshIndex); - vertexFinder = &blubb.first; - posEpsilonSqr = blubb.second; + vertexFinder = &blubb.first;posEpsilonSqr = blubb.second; } } if (!vertexFinder) { _vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D)); - vertexFinder = &_vertexFinder; + vertexFinder = &_vertexFinder; posEpsilonSqr = ComputePositionEpsilon(pMesh); } @@ -360,7 +358,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) } // adjust bone vertex weights. - for( unsigned int a = 0; a < pMesh->mNumBones; a++) + for( int a = 0; a < (int)pMesh->mNumBones; a++) { aiBone* bone = pMesh->mBones[a]; std::vector newWeights; @@ -379,14 +377,38 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex) } } - // there should be some. At least I think there should be some - ai_assert( newWeights.size() > 0); + if (newWeights.size() > 0) { + // kill the old and replace them with the translated weights + delete [] bone->mWeights; + bone->mNumWeights = (unsigned int)newWeights.size(); - // kill the old and replace them with the translated weights - delete [] bone->mWeights; - bone->mNumWeights = (unsigned int)newWeights.size(); - bone->mWeights = new aiVertexWeight[bone->mNumWeights]; - memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + memcpy( bone->mWeights, &newWeights[0], bone->mNumWeights * sizeof( aiVertexWeight)); + } + else { + + /* NOTE: + * + * In the algorithm above we're assuming that there are no vertices + * with a different bone weight setup at the same position. That wouldn't + * make sense, but it is not absolutely impossible. SkeletonMeshBuilder + * for example generates such input data if two skeleton points + * share the same position. Again this doesn't make sense but is + * reality for some model formats (MD5 for example uses these special + * nodes as attachment tags for its weapons). + * + * Then it is possible that a bone has no weights anymore .... as a quick + * workaround, we're just removing these bones. If they're animated, + * model geometry might be modified but at least there's no risk of a crash. + */ + delete bone; + --pMesh->mNumBones; + for (unsigned int n = a; n < pMesh->mNumBones; ++n) + pMesh->mBones[n] = pMesh->mBones[n+1]; + + --a; + DefaultLogger::get()->warn("Removing bone -> no weights remaining"); + } } return pMesh->mNumVertices; } diff --git a/code/LimitBoneWeightsProcess.cpp b/code/LimitBoneWeightsProcess.cpp index 6bee3943c..888fdb138 100644 --- a/code/LimitBoneWeightsProcess.cpp +++ b/code/LimitBoneWeightsProcess.cpp @@ -181,12 +181,9 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh) ::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight)); } - if (bChanged) - { - // the number of new bones is smaller than before, so we can - // reuse the old array, too. - aiBone** ppcCur = pMesh->mBones; - aiBone** ppcSrc = ppcCur; + if (bChanged) { + // the number of new bones is smaller than before, so we can reuse the old array + aiBone** ppcCur = pMesh->mBones;aiBone** ppcSrc = ppcCur; for (std::vector::const_iterator iter = abNoNeed.begin();iter != abNoNeed.end() ;++iter) { if (*iter) { diff --git a/code/MD5Loader.cpp b/code/MD5Loader.cpp index bd0255c69..9ca1f8b45 100644 --- a/code/MD5Loader.cpp +++ b/code/MD5Loader.cpp @@ -198,12 +198,12 @@ void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc) const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer - for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end(); - iter != iterEnd;++iter) - { + for (FaceList::const_iterator iter = meshSrc.mFaces.begin(),iterEnd = meshSrc.mFaces.end();iter != iterEnd;++iter){ const aiFace& face = *iter; - for (unsigned int i = 0; i < 3;++i) - { + for (unsigned int i = 0; i < 3;++i) { + if (face.mIndices[0] >= meshSrc.mVertices.size()) + throw new ImportErrorException("MD5MESH: Invalid vertex index"); + if (abHad[face.mIndices[i]]) { // generate a new vertex meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]]; @@ -445,6 +445,9 @@ void MD5Importer::LoadMD5MeshFile () // process bone weights for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w) { + if (w >= meshSrc.mWeights.size()) + throw new ImportErrorException("MD5MESH: Invalid weight index"); + MD5::WeightDesc& desc = meshSrc.mWeights[w]; if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON) continue; @@ -532,7 +535,6 @@ void MD5Importer::LoadMD5AnimFile () DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile); return; } - bHadMD5Anim = true; LoadFileIntoMemory(file.get()); // parse the basic file structure @@ -542,7 +544,14 @@ void MD5Importer::LoadMD5AnimFile () MD5::MD5AnimParser animParser(parser.mSections); // generate and fill the output animation - if (!animParser.mAnimatedBones.empty()) { + if (animParser.mAnimatedBones.empty() || animParser.mFrames.empty() || + animParser.mBaseFrames.size() != animParser.mAnimatedBones.size()) { + + DefaultLogger::get()->error("MD5ANIM: No frames or animated bones loaded"); + } + else { + bHadMD5Anim = true; + pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1]; aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation(); anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size(); @@ -552,9 +561,8 @@ void MD5Importer::LoadMD5AnimFile () node->mNodeName = aiString( animParser.mAnimatedBones[i].mName ); // allocate storage for the keyframes - node->mNumPositionKeys = node->mNumRotationKeys = (unsigned int)animParser.mFrames.size(); - node->mPositionKeys = new aiVectorKey[node->mNumPositionKeys]; - node->mRotationKeys = new aiQuatKey[node->mNumPositionKeys]; + node->mPositionKeys = new aiVectorKey[animParser.mFrames.size()]; + node->mRotationKeys = new aiQuatKey[animParser.mFrames.size()]; } // 1 tick == 1 frame @@ -562,23 +570,27 @@ void MD5Importer::LoadMD5AnimFile () for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){ double dTime = (double)(*iter).iIndex; - if (!(*iter).mValues.empty()) + aiNodeAnim** pcAnimNode = anim->mChannels; + if (!(*iter).mValues.empty() || iter == animParser.mFrames.begin()) /* be sure we have at least one frame */ { // now process all values in there ... read all joints - aiNodeAnim** pcAnimNode = anim->mChannels; MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0]; for (AnimBoneList::const_iterator iter2 = animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2, ++pcAnimNode,++pcBaseFrame) { + const float* fpCur; if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) { - DefaultLogger::get()->error("MD5: Keyframe index is out of range"); - continue; - } - const float* fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex]; + // Allow for empty frames + if ((*iter2).iFlags != 0) { + throw new ImportErrorException("MD5: Keyframe index is out of range"); + } + } + else fpCur = &(*iter).mValues[(*iter2).iFirstKeyIndex]; aiNodeAnim* pcCurAnimBone = *pcAnimNode; - aiVectorKey* vKey = pcCurAnimBone->mPositionKeys++; - aiQuatKey* qKey = pcCurAnimBone->mRotationKeys++; + + aiVectorKey* vKey = &pcCurAnimBone->mPositionKeys[pcCurAnimBone->mNumPositionKeys++]; + aiQuatKey* qKey = &pcCurAnimBone->mRotationKeys [pcCurAnimBone->mNumRotationKeys++]; aiVector3D vTemp; // translational component @@ -587,7 +599,7 @@ void MD5Importer::LoadMD5AnimFile () vKey->mValue[i] = *fpCur++; else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i]; } - + // orientation component for (unsigned int i = 0; i < 3; ++i) { if ((*iter2).iFlags & (8u << i)) @@ -596,26 +608,14 @@ void MD5Importer::LoadMD5AnimFile () } MD5::ConvertQuaternion(vTemp, qKey->mValue); - - aiMatrix4x4 m; - aiMatrix4x4::Translation(vKey->mValue,m); - m = m*aiMatrix4x4( qKey->mValue.GetMatrix() ); - m.DecomposeNoScaling(qKey->mValue,vKey->mValue); - qKey->mTime = vKey->mTime = dTime; } } + // compute the duration of the animation anim->mDuration = std::max(dTime,anim->mDuration); } - // undo our offset computations - for (unsigned int i = 0; i < anim->mNumChannels;++i) { - aiNodeAnim* node = anim->mChannels[i]; - node->mPositionKeys -= node->mNumPositionKeys; - node->mRotationKeys -= node->mNumPositionKeys; - } - // If we didn't build the hierarchy yet (== we didn't load a MD5MESH), // construct it now from the data given in the MD5ANIM. if (!pScene->mRootNode) { @@ -629,7 +629,6 @@ void MD5Importer::LoadMD5AnimFile () } } } - // delete the file again UnloadFileFromMemory(); } diff --git a/code/MaterialSystem.cpp b/code/MaterialSystem.cpp index 9c0c920f8..644484687 100644 --- a/code/MaterialSystem.cpp +++ b/code/MaterialSystem.cpp @@ -38,6 +38,10 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ---------------------------------------------------------------------- */ +/** @file MaterialSystem.cpp + * @brief Implementation of the material system of the library + */ + #include "AssimpPCH.h" #include "Hash.h" @@ -55,12 +59,17 @@ aiReturn aiGetMaterialProperty(const aiMaterial* pMat, ai_assert (pKey != NULL); ai_assert (pPropOut != NULL); - for (unsigned int i = 0; i < pMat->mNumProperties;++i) - { + /* Just search for a property with exactly this name .. + * could be improved by hashing, but it's possibly + * no worth the effort. + */ + for (unsigned int i = 0; i < pMat->mNumProperties;++i) { aiMaterialProperty* prop = pMat->mProperties[i]; - if (prop && !::strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index) + if (prop /* just for safety ... */ + && 0 == ::strcmp( prop->mKey.data, pKey ) + && (0xffffffff == type || prop->mSemantic == type) /* 0xffffffff is a wildcard */ + && (0xffffffff == index || prop->mIndex == index)) { *pPropOut = pMat->mProperties[i]; return AI_SUCCESS; @@ -79,50 +88,40 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat, float* pOut, unsigned int* pMax) { - ai_assert (pMat != NULL); - ai_assert (pKey != NULL); ai_assert (pOut != NULL); - for (unsigned int i = 0; i < pMat->mNumProperties;++i) - { - aiMaterialProperty* prop = pMat->mProperties[i]; + aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index, (const aiMaterialProperty**) &prop); + if (!prop) + return AI_FAILURE; - if (prop && !::strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index) - { - // data is given in floats, simply copy it - if( aiPTI_Float == pMat->mProperties[i]->mType || - aiPTI_Buffer == pMat->mProperties[i]->mType) - { - unsigned int iWrite = pMat->mProperties[i]->mDataLength / sizeof(float); + // data is given in floats, simply copy it + if( aiPTI_Float == prop->mType || aiPTI_Buffer == prop->mType) { + unsigned int iWrite = prop->mDataLength / sizeof(float); - if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; - ::memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (float)); + if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; + ::memcpy (pOut, prop->mData, iWrite * sizeof (float)); - if (pMax)*pMax = iWrite; - } - // data is given in ints, convert to float - else if( aiPTI_Integer == pMat->mProperties[i]->mType) - { - unsigned int iWrite = pMat->mProperties[i]->mDataLength / sizeof(int); - - if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; - for (unsigned int a = 0; a < iWrite;++a) - { - pOut[a] = (float) ((int*)pMat->mProperties[i]->mData)[a]; - } - if (pMax)*pMax = iWrite; - } - // it is a string ... no way to read something out of this - else - { - if (pMax)*pMax = 0; - return AI_FAILURE; - } - return AI_SUCCESS; - } + if (pMax)*pMax = iWrite; } - return AI_FAILURE; + // data is given in ints, convert to float + else if( aiPTI_Integer == prop->mType) { + unsigned int iWrite = prop->mDataLength / sizeof(int); + + if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = (float) ((int*)prop->mData)[a]; + } + if (pMax)*pMax = iWrite; + } + // it is a string ... no way to read something out of this + else { + DefaultLogger::get()->error("Material property" + std::string(pKey) + " was found, but is not an float array"); + if (pMax)*pMax = 0; + return AI_FAILURE; + } + return AI_SUCCESS; + } // ------------------------------------------------------------------------------------------------ @@ -134,49 +133,39 @@ aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, int* pOut, unsigned int* pMax) { - ai_assert (pMat != NULL); - ai_assert (pKey != NULL); ai_assert (pOut != NULL); - for (unsigned int i = 0; i < pMat->mNumProperties;++i) - { - aiMaterialProperty* prop = pMat->mProperties[i]; - if (prop && !::strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index) - { - // data is given in ints, simply copy it - if( aiPTI_Integer == pMat->mProperties[i]->mType || - aiPTI_Buffer == pMat->mProperties[i]->mType) - { - unsigned int iWrite = pMat->mProperties[i]->mDataLength / sizeof(int); + aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**) &prop); + if (!prop) + return AI_FAILURE; - if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; - ::memcpy (pOut, pMat->mProperties[i]->mData, iWrite * sizeof (int)); + // data is given in ints, simply copy it + if( aiPTI_Integer == prop->mType || aiPTI_Buffer == prop->mType) { - if (pMax)*pMax = iWrite; - } - // data is given in floats convert to int (lossy!) - else if( aiPTI_Float == pMat->mProperties[i]->mType) - { - unsigned int iWrite = pMat->mProperties[i]->mDataLength / sizeof(float); + unsigned int iWrite = prop->mDataLength / sizeof(int); - if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; - for (unsigned int a = 0; a < iWrite;++a) - { - pOut[a] = (int) ((float*)pMat->mProperties[i]->mData)[a]; - } - if (pMax)*pMax = iWrite; - } - // it is a string ... no way to read something out of this - else - { - if (pMax)*pMax = 0; - return AI_FAILURE; - } - return AI_SUCCESS; - } + if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; + ::memcpy (pOut, prop->mData, iWrite * sizeof (int)); + if (pMax)*pMax = iWrite; } - return AI_FAILURE; + // data is given in floats convert to int (lossy!) + else if( aiPTI_Float == prop->mType) { + unsigned int iWrite = prop->mDataLength / sizeof(float); + + if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite; + for (unsigned int a = 0; a < iWrite;++a) { + pOut[a] = (int) ((float*)prop->mData)[a]; + } + if (pMax)*pMax = iWrite; + } + // it is a string ... no way to read something out of this + else { + DefaultLogger::get()->error("Material property" + std::string(pKey) + " was found, but is not an integer array"); + if (pMax)*pMax = 0; + return AI_FAILURE; + } + return AI_SUCCESS; } // ------------------------------------------------------------------------------------------------ @@ -184,14 +173,15 @@ aiReturn aiGetMaterialIntegerArray(const aiMaterial* pMat, aiReturn aiGetMaterialColor(const aiMaterial* pMat, const char* pKey, unsigned int type, - unsigned int index, + unsigned int index, aiColor4D* pOut) { unsigned int iMax = 4; aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(float*)pOut,&iMax); - // if no alpha channel is provided set it to 1.0 by default - if (3 == iMax)pOut->a = 1.0f; + // if no alpha channel is defined: set it to 1.0 + if (3 == iMax) + pOut->a = 1.0f; return eRet; } @@ -200,31 +190,28 @@ aiReturn aiGetMaterialColor(const aiMaterial* pMat, aiReturn aiGetMaterialString(const aiMaterial* pMat, const char* pKey, unsigned int type, - unsigned int index, + unsigned int index, aiString* pOut) { - ai_assert (pMat != NULL); - ai_assert (pKey != NULL); ai_assert (pOut != NULL); - for (unsigned int i = 0; i < pMat->mNumProperties;++i) - { - aiMaterialProperty* prop = pMat->mProperties[i]; + aiMaterialProperty* prop; + aiGetMaterialProperty(pMat,pKey,type,index,(const aiMaterialProperty**)&prop); + if (!prop) + return AI_FAILURE; - if (prop && !::strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index) - { - if( aiPTI_String == pMat->mProperties[i]->mType) - { - const aiString* pcSrc = (const aiString*)pMat->mProperties[i]->mData; - ::memcpy (pOut->data, pcSrc->data, (pOut->length = pcSrc->length)+1); - } - // Wrong type - else return AI_FAILURE; - return AI_SUCCESS; - } + if( aiPTI_String == prop->mType) { + + // WARN: There's not the whole string stored .. + const aiString* pcSrc = (const aiString*)prop->mData; + ::memcpy (pOut->data, pcSrc->data, (pOut->length = pcSrc->length)+1); } - return AI_FAILURE; + // Wrong type + else { + DefaultLogger::get()->error("Material property" + std::string(pKey) + " was found, but is no string" ); + return AI_FAILURE; + } + return AI_SUCCESS; } // ------------------------------------------------------------------------------------------------ @@ -246,8 +233,10 @@ MaterialHelper::~MaterialHelper() // ------------------------------------------------------------------------------------------------ aiMaterial::~aiMaterial() { - // This is safe: aiMaterial has a private constructor, - // so instances must be created indirectly via MaterialHelper. + // HACK (Aramis): This is safe: aiMaterial has a private constructor, + // so instances must be created indirectly via MaterialHelper. We can't + // use a virtual d'tor because we need to preserve binary compatibility + // with good old C ... ((MaterialHelper*)this)->_InternDestruct(); } @@ -305,12 +294,12 @@ uint32_t MaterialHelper::ComputeHash(bool includeMatName /*= false*/) // ------------------------------------------------------------------------------------------------ aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type, - unsigned int index) + unsigned int index + ) { ai_assert(NULL != pKey); - for (unsigned int i = 0; i < mNumProperties;++i) - { + for (unsigned int i = 0; i < mNumProperties;++i) { aiMaterialProperty* prop = mProperties[i]; if (prop && !::strcmp( prop->mKey.data, pKey ) && @@ -321,8 +310,7 @@ aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type, // collapse the array behind --. --mNumProperties; - for (unsigned int a = i; a < mNumProperties;++a) - { + for (unsigned int a = i; a < mNumProperties;++a) { mProperties[a] = mProperties[a+1]; } return AI_SUCCESS; @@ -338,24 +326,25 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput, const char* pKey, unsigned int type, unsigned int index, - aiPropertyTypeInfo pType) + aiPropertyTypeInfo pType + ) { ai_assert (pInput != NULL); ai_assert (pKey != NULL); ai_assert (0 != pSizeInBytes); - // first search the list whether there is already an entry - // with this name. - unsigned int iOutIndex = 0xFFFFFFFF; - for (unsigned int i = 0; i < mNumProperties;++i) - { + // first search the list whether there is already an entry with this key + unsigned int iOutIndex = 0xffffffff; + for (unsigned int i = 0; i < mNumProperties;++i) { aiMaterialProperty* prop = mProperties[i]; - if (prop && !::strcmp( prop->mKey.data, pKey ) && - prop->mSemantic == type && prop->mIndex == index) + if (prop /* just for safety */ + && !::strcmp( prop->mKey.data, pKey ) + && prop->mSemantic == type + && prop->mIndex == index) { // delete this entry - delete this->mProperties[i]; + delete mProperties[i]; iOutIndex = i; } } @@ -376,21 +365,24 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput, ai_assert ( MAXLEN > pcNew->mKey.length); ::strcpy( pcNew->mKey.data, pKey ); - if (0xFFFFFFFF != iOutIndex) - { + if (0xffffffff != iOutIndex) { mProperties[iOutIndex] = pcNew; return AI_SUCCESS; } - // resize the array ... double the storage - if (mNumProperties == mNumAllocated) - { - unsigned int iOld = mNumAllocated; + // resize the array ... double the storage allocated + if (mNumProperties == mNumAllocated) { + const unsigned int iOld = mNumAllocated; mNumAllocated *= 2; - aiMaterialProperty** ppTemp = new aiMaterialProperty*[mNumAllocated]; - if (NULL == ppTemp)return AI_OUTOFMEMORY; + aiMaterialProperty** ppTemp; + try { + ppTemp = new aiMaterialProperty*[mNumAllocated]; + } catch (std::bad_alloc&) { + return AI_OUTOFMEMORY; + } + // just copy all items over; then replace the old array ::memcpy (ppTemp,mProperties,iOld * sizeof(void*)); delete[] mProperties; @@ -407,15 +399,20 @@ aiReturn MaterialHelper::AddProperty (const aiString* pInput, unsigned int type, unsigned int index) { - // Fix ... don't keep the whole string buffer - return this->AddBinaryProperty(pInput,(unsigned int)pInput->length+1+ + // We don't want to add the whole buffer .. + return AddBinaryProperty(pInput, + (unsigned int)pInput->length+1+ (unsigned int)(((uint8_t*)&pInput->data - (uint8_t*)&pInput->length)), - pKey,type,index, aiPTI_String); + pKey, + type, + index, + aiPTI_String); } // ------------------------------------------------------------------------------------------------ void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest, - const MaterialHelper* pcSrc) + const MaterialHelper* pcSrc + ) { ai_assert(NULL != pcDest); ai_assert(NULL != pcSrc); @@ -427,32 +424,28 @@ void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest, aiMaterialProperty** pcOld = pcDest->mProperties; pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated]; - if (iOldNum && pcOld) - { + if (iOldNum && pcOld) { for (unsigned int i = 0; i < iOldNum;++i) pcDest->mProperties[i] = pcOld[i]; delete[] pcOld; } - for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) - { + for (unsigned int i = iOldNum; i< pcDest->mNumProperties;++i) { aiMaterialProperty* propSrc = pcSrc->mProperties[i]; - // search whether we have already a property with this name - // (if yes we overwrite the old one) + // search whether we have already a property with this name -> if yes, overwrite it aiMaterialProperty* prop; - for (unsigned int q = 0; q < iOldNum;++q) - { + for (unsigned int q = 0; q < iOldNum;++q) { prop = pcDest->mProperties[q]; - if (prop && prop->mKey == propSrc->mKey && - prop->mSemantic == propSrc->mSemantic && prop->mIndex == propSrc->mIndex) - { + if (prop /* just for safety */ + && prop->mKey == propSrc->mKey + && prop->mSemantic == propSrc->mSemantic + && prop->mIndex == propSrc->mIndex) { delete prop; // collapse the whole array ... ::memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q); - i--; - pcDest->mNumProperties--; + i--;pcDest->mNumProperties--; } } @@ -516,7 +509,6 @@ aiReturn aiGetMaterialTexture(const C_STRUCT aiMaterial* mat, if (flags){ aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags); } - return AI_SUCCESS; } diff --git a/code/SkeletonMeshBuilder.cpp b/code/SkeletonMeshBuilder.cpp index 5605e2dd2..eb9c40ca0 100644 --- a/code/SkeletonMeshBuilder.cpp +++ b/code/SkeletonMeshBuilder.cpp @@ -65,9 +65,9 @@ SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root) pScene->mMeshes = new aiMesh*[1]; pScene->mMeshes[0] = CreateMesh(); // and install it at the root node - pScene->mRootNode->mNumMeshes = 1; - pScene->mRootNode->mMeshes = new unsigned int[1]; - pScene->mRootNode->mMeshes[0] = 0; + root->mNumMeshes = 1; + root->mMeshes = new unsigned int[1]; + root->mMeshes[0] = 0; // create a dummy material for the mesh pScene->mNumMaterials = 1; @@ -207,6 +207,8 @@ aiMesh* SkeletonMeshBuilder::CreateMesh() mesh->mVertices = new aiVector3D[mesh->mNumVertices]; std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices); + mesh->mNormals = new aiVector3D[mesh->mNumVertices]; + // add faces mesh->mNumFaces = mFaces.size(); mesh->mFaces = new aiFace[mesh->mNumFaces]; @@ -219,6 +221,19 @@ aiMesh* SkeletonMeshBuilder::CreateMesh() outface.mIndices[0] = inface.mIndices[0]; outface.mIndices[1] = inface.mIndices[1]; outface.mIndices[2] = inface.mIndices[2]; + + // Compute per-face normals ... we don't want the bones to be + // smoothed ... they're built to visualize the skeleton, + // so it's good if there's a visual difference to the rest + // of the geometry + aiVector3D nor = ((mVertices[inface.mIndices[2]] - mVertices[inface.mIndices[0]]) ^ + (mVertices[inface.mIndices[1]] - mVertices[inface.mIndices[0]])); + + if (nor.Length() < 1e-5f) /* ensure that FindInvalidData won't remove us ...*/ + nor = aiVector3D(1.f,0.f,0.f); + + for (unsigned int n = 0; n < 3; ++n) + mesh->mNormals[inface.mIndices[n]] = nor; } // add the bones @@ -239,8 +254,12 @@ aiMaterial* SkeletonMeshBuilder::CreateMaterial() Assimp::MaterialHelper* matHelper = new Assimp::MaterialHelper; // Name - aiString matName( std::string( "Material")); + aiString matName( std::string( "SkeletonMaterial")); matHelper->AddProperty( &matName, AI_MATKEY_NAME); + // Prevent backface culling + const int no_cull = 1; + matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED); + return matHelper; } diff --git a/code/ValidateDataStructure.cpp b/code/ValidateDataStructure.cpp index 2b2b01259..d7079c798 100644 --- a/code/ValidateDataStructure.cpp +++ b/code/ValidateDataStructure.cpp @@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "ValidateDataStructure.h" #include "BaseImporter.h" #include "fast_atof.h" +#include "ProcessHelper.h" // CRT headers #include @@ -59,16 +60,12 @@ using namespace Assimp; // ------------------------------------------------------------------------------------------------ // Constructor to be privately used by Importer ValidateDSProcess::ValidateDSProcess() -{ - // nothing to do here -} +{} // ------------------------------------------------------------------------------------------------ // Destructor, private as well ValidateDSProcess::~ValidateDSProcess() -{ - // nothing to do here -} +{} // ------------------------------------------------------------------------------------------------ // Returns whether the processing step is present in the given flag field. @@ -550,43 +547,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation) void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial, aiTextureType type) { - const char* szType = NULL; - switch (type) - { - case aiTextureType_DIFFUSE: - szType = "Diffuse"; - break; - - case aiTextureType_SPECULAR: - szType = "Specular"; - break; - - case aiTextureType_AMBIENT: - szType = "Ambient"; - break; - - case aiTextureType_EMISSIVE: - szType = "Emissive"; - break; - - case aiTextureType_OPACITY: - szType = "Opacity"; - break; - - case aiTextureType_SHININESS: - szType = "Shininess"; - break; - - case aiTextureType_NORMALS: - szType = "Normals"; - break; - - case aiTextureType_HEIGHT: - szType = "Height"; - break; - default: - break; - }; + const char* szType = TextureTypeToString(type); // **************************************************************************** // Search all keys of the material ... @@ -835,7 +796,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation, for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i) { // ScenePreprocessor will compute the duration if still teh default value - if (-1. != pAnimation->mDuration && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration) + if (pAnimation->mDuration > 0. && pNodeAnim->mPositionKeys[i].mTime > pAnimation->mDuration) { ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger " "than aiAnimation::mDuration (which is %.5f)",i, @@ -863,7 +824,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation, double dLast = -10e10; for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i) { - if (-1. != pAnimation->mDuration && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration) + if (pAnimation->mDuration > 0. && pNodeAnim->mRotationKeys[i].mTime > pAnimation->mDuration) { ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger " "than aiAnimation::mDuration (which is %.5f)",i, @@ -890,7 +851,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation, double dLast = -10e10; for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i) { - if (-1. != pAnimation->mDuration && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration) + if (pAnimation->mDuration > 0. && pNodeAnim->mScalingKeys[i].mTime > pAnimation->mDuration) { ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger " "than aiAnimation::mDuration (which is %.5f)",i,