Răsfoiți Sursa

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

aramis_acg 16 ani în urmă
părinte
comite
de50bb915b

+ 35 - 13
code/JoinVerticesProcess.cpp

@@ -145,19 +145,17 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 	float posEpsilonSqr;
 	float posEpsilonSqr;
 	SpatialSort*  vertexFinder = NULL;
 	SpatialSort*  vertexFinder = NULL;
 	SpatialSort  _vertexFinder;
 	SpatialSort  _vertexFinder;
-	if (shared)
-	{
+	if (shared)	{
 		std::vector<std::pair<SpatialSort,float> >* avf;
 		std::vector<std::pair<SpatialSort,float> >* avf;
 		shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
 		shared->GetProperty(AI_SPP_SPATIAL_SORT,avf);
 		if (avf)	{
 		if (avf)	{
 			std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
 			std::pair<SpatialSort,float>& blubb = avf->operator [] (meshIndex);
-			vertexFinder  = &blubb.first;
-			posEpsilonSqr = blubb.second;
+			vertexFinder  = &blubb.first;posEpsilonSqr = blubb.second;
 		}
 		}
 	}
 	}
 	if (!vertexFinder)	{
 	if (!vertexFinder)	{
 		_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
 		_vertexFinder.Fill(pMesh->mVertices, pMesh->mNumVertices, sizeof( aiVector3D));
-		vertexFinder = &_vertexFinder;
+		vertexFinder = &_vertexFinder; 
 		posEpsilonSqr = ComputePositionEpsilon(pMesh);
 		posEpsilonSqr = ComputePositionEpsilon(pMesh);
 	}
 	}
 
 
@@ -360,7 +358,7 @@ int JoinVerticesProcess::ProcessMesh( aiMesh* pMesh, unsigned int meshIndex)
 	}
 	}
 
 
 	// adjust bone vertex weights.
 	// 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];
 		aiBone* bone = pMesh->mBones[a];
 		std::vector<aiVertexWeight> newWeights;
 		std::vector<aiVertexWeight> 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;
 	return pMesh->mNumVertices;
 }
 }

+ 3 - 6
code/LimitBoneWeightsProcess.cpp

@@ -181,12 +181,9 @@ void LimitBoneWeightsProcess::ProcessMesh( aiMesh* pMesh)
 			::memcpy( bone->mWeights, &bw[0], bw.size() * sizeof( aiVertexWeight));
 			::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<bool>::const_iterator iter  = abNoNeed.begin();iter != abNoNeed.end()  ;++iter)	{
 			for (std::vector<bool>::const_iterator iter  = abNoNeed.begin();iter != abNoNeed.end()  ;++iter)	{
 				if (*iter)	{
 				if (*iter)	{

+ 32 - 33
code/MD5Loader.cpp

@@ -198,12 +198,12 @@ void MD5Importer::MakeDataUnique (MD5::MeshDesc& meshSrc)
 	const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); 
 	const unsigned int guess = (unsigned int)(fWeightsPerVert*iNewNum); 
 	meshSrc.mWeights.reserve(guess + (guess >> 3)); // + 12.5% as buffer
 	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;
 		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]])	{
 			if (abHad[face.mIndices[i]])	{
 				// generate a new vertex
 				// generate a new vertex
 				meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
 				meshSrc.mVertices[iNewIndex] = meshSrc.mVertices[face.mIndices[i]];
@@ -445,6 +445,9 @@ void MD5Importer::LoadMD5MeshFile ()
 				// process bone weights
 				// process bone weights
 				for (unsigned int jub = (*iter).mFirstWeight, w = jub; w < jub + (*iter).mNumWeights;++w)
 				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];
 					MD5::WeightDesc& desc = meshSrc.mWeights[w];
 					if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON)
 					if ( desc.mWeight < AI_MD5_WEIGHT_EPSILON && desc.mWeight >= -AI_MD5_WEIGHT_EPSILON)
 						continue;
 						continue;
@@ -532,7 +535,6 @@ void MD5Importer::LoadMD5AnimFile ()
 		DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
 		DefaultLogger::get()->warn("Failed to read MD5ANIM file: " + pFile);
 		return;
 		return;
 	}
 	}
-	bHadMD5Anim = true;
 	LoadFileIntoMemory(file.get());
 	LoadFileIntoMemory(file.get());
 
 
 	// parse the basic file structure
 	// parse the basic file structure
@@ -542,7 +544,14 @@ void MD5Importer::LoadMD5AnimFile ()
 	MD5::MD5AnimParser animParser(parser.mSections);
 	MD5::MD5AnimParser animParser(parser.mSections);
 
 
 	// generate and fill the output animation
 	// 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];
 		pScene->mAnimations = new aiAnimation*[pScene->mNumAnimations = 1];
 		aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
 		aiAnimation* anim = pScene->mAnimations[0] = new aiAnimation();
 		anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
 		anim->mNumChannels = (unsigned int)animParser.mAnimatedBones.size();
@@ -552,9 +561,8 @@ void MD5Importer::LoadMD5AnimFile ()
 			node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
 			node->mNodeName = aiString( animParser.mAnimatedBones[i].mName );
 
 
 			// allocate storage for the keyframes
 			// 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
 		// 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){
 		for (FrameList::const_iterator iter = animParser.mFrames.begin(), iterEnd = animParser.mFrames.end();iter != iterEnd;++iter){
 			double dTime = (double)(*iter).iIndex;
 			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
 				// now process all values in there ... read all joints
-				aiNodeAnim** pcAnimNode = anim->mChannels;
 				MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
 				MD5::BaseFrameDesc* pcBaseFrame = &animParser.mBaseFrames[0];
 				for (AnimBoneList::const_iterator iter2	= animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
 				for (AnimBoneList::const_iterator iter2	= animParser.mAnimatedBones.begin(); iter2 != animParser.mAnimatedBones.end();++iter2,
 					++pcAnimNode,++pcBaseFrame)
 					++pcAnimNode,++pcBaseFrame)
 				{
 				{
+					const float* fpCur;
 					if((*iter2).iFirstKeyIndex >= (*iter).mValues.size()) {
 					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;
 					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;
 					aiVector3D vTemp;
 
 
 					// translational component
 					// translational component
@@ -587,7 +599,7 @@ void MD5Importer::LoadMD5AnimFile ()
 							vKey->mValue[i] =  *fpCur++;
 							vKey->mValue[i] =  *fpCur++;
 						else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
 						else vKey->mValue[i] = pcBaseFrame->vPositionXYZ[i];
 					}
 					}
-					
+
 					// orientation component
 					// orientation component
 					for (unsigned int i = 0; i < 3; ++i) {
 					for (unsigned int i = 0; i < 3; ++i) {
 						if ((*iter2).iFlags & (8u << i))
 						if ((*iter2).iFlags & (8u << i))
@@ -596,26 +608,14 @@ void MD5Importer::LoadMD5AnimFile ()
 					}
 					}
 
 
 					MD5::ConvertQuaternion(vTemp, qKey->mValue);
 					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;
 					qKey->mTime = vKey->mTime = dTime;
 				}
 				}
 			}
 			}
+
 			// compute the duration of the animation
 			// compute the duration of the animation
 			anim->mDuration = std::max(dTime,anim->mDuration);
 			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),
 		// If we didn't build the hierarchy yet (== we didn't load a MD5MESH),
 		// construct it now from the data given in the MD5ANIM.
 		// construct it now from the data given in the MD5ANIM.
 		if (!pScene->mRootNode) {
 		if (!pScene->mRootNode) {
@@ -629,7 +629,6 @@ void MD5Importer::LoadMD5AnimFile ()
 			}
 			}
 		}
 		}
 	}
 	}
-
 	// delete the file again
 	// delete the file again
 	UnloadFileFromMemory();
 	UnloadFileFromMemory();
 }
 }

+ 134 - 142
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 "AssimpPCH.h"
 #include "Hash.h"
 #include "Hash.h"
 
 
@@ -55,12 +59,17 @@ aiReturn aiGetMaterialProperty(const aiMaterial* pMat,
 	ai_assert (pKey != NULL);
 	ai_assert (pKey != NULL);
 	ai_assert (pPropOut != 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];
 		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];
 			*pPropOut = pMat->mProperties[i];
 			return AI_SUCCESS;
 			return AI_SUCCESS;
@@ -79,50 +88,40 @@ aiReturn aiGetMaterialFloatArray(const aiMaterial* pMat,
 	float* pOut,
 	float* pOut,
 	unsigned int* pMax)
 	unsigned int* pMax)
 {
 {
-	ai_assert (pMat != NULL);
-	ai_assert (pKey != NULL);
 	ai_assert (pOut != 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;
+	}
+	// 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;
 	}
 	}
-	return AI_FAILURE;
+	// 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,
 	int* pOut,
 	unsigned int* pMax)
 	unsigned int* pMax)
 {
 {
-	ai_assert (pMat != NULL);
-	ai_assert (pKey != NULL);
 	ai_assert (pOut != 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);
-
-				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;
+		unsigned int iWrite = prop->mDataLength / sizeof(int);
+
+		if (pMax)iWrite = *pMax < iWrite ? *pMax : iWrite;
+		::memcpy (pOut, prop->mData, iWrite * sizeof (int));
+		if (pMax)*pMax = iWrite;
+	}
+	// 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;
 	}
 	}
-	return AI_FAILURE;
+	// 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, 
 aiReturn aiGetMaterialColor(const aiMaterial* pMat, 
 	const char* pKey,
 	const char* pKey,
 	unsigned int type,
 	unsigned int type,
-    unsigned int index,
+	unsigned int index,
 	aiColor4D* pOut)
 	aiColor4D* pOut)
 {
 {
 	unsigned int iMax = 4;
 	unsigned int iMax = 4;
 	aiReturn eRet = aiGetMaterialFloatArray(pMat,pKey,type,index,(float*)pOut,&iMax);
 	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;
 	return eRet;
 }
 }
 
 
@@ -200,31 +190,28 @@ aiReturn aiGetMaterialColor(const aiMaterial* pMat,
 aiReturn aiGetMaterialString(const aiMaterial* pMat, 
 aiReturn aiGetMaterialString(const aiMaterial* pMat, 
 	const char* pKey,
 	const char* pKey,
 	unsigned int type,
 	unsigned int type,
-    unsigned int index,
+	unsigned int index,
 	aiString* pOut)
 	aiString* pOut)
 {
 {
-	ai_assert (pMat != NULL);
-	ai_assert (pKey != NULL);
 	ai_assert (pOut != 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()
 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();
 	((MaterialHelper*)this)->_InternDestruct();
 }
 }
 
 
@@ -305,12 +294,12 @@ uint32_t MaterialHelper::ComputeHash(bool includeMatName /*= false*/)
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type,
 aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type,
-    unsigned int index)
+    unsigned int index
+	)
 {
 {
 	ai_assert(NULL != pKey);
 	ai_assert(NULL != pKey);
 
 
-	for (unsigned int i = 0; i < mNumProperties;++i)
-	{
+	for (unsigned int i = 0; i < mNumProperties;++i) {
 		aiMaterialProperty* prop = mProperties[i];
 		aiMaterialProperty* prop = mProperties[i];
 
 
 		if (prop && !::strcmp( prop->mKey.data, pKey ) &&
 		if (prop && !::strcmp( prop->mKey.data, pKey ) &&
@@ -321,8 +310,7 @@ aiReturn MaterialHelper::RemoveProperty (const char* pKey,unsigned int type,
 
 
 			// collapse the array behind --.
 			// collapse the array behind --.
 			--mNumProperties;
 			--mNumProperties;
-			for (unsigned int a = i; a < mNumProperties;++a)
-			{
+			for (unsigned int a = i; a < mNumProperties;++a)	{
 				mProperties[a] = mProperties[a+1];
 				mProperties[a] = mProperties[a+1];
 			}
 			}
 			return AI_SUCCESS;
 			return AI_SUCCESS;
@@ -338,24 +326,25 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
 	const char* pKey,
 	const char* pKey,
 	unsigned int type,
 	unsigned int type,
     unsigned int index,
     unsigned int index,
-	aiPropertyTypeInfo pType)
+	aiPropertyTypeInfo pType
+	)
 {
 {
 	ai_assert (pInput != NULL);
 	ai_assert (pInput != NULL);
 	ai_assert (pKey != NULL);
 	ai_assert (pKey != NULL);
 	ai_assert (0 != pSizeInBytes);
 	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];
 		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 entry
-			delete this->mProperties[i];
+			delete mProperties[i];
 			iOutIndex = i;
 			iOutIndex = i;
 		}
 		}
 	}
 	}
@@ -376,21 +365,24 @@ aiReturn MaterialHelper::AddBinaryProperty (const void* pInput,
 	ai_assert ( MAXLEN > pcNew->mKey.length);
 	ai_assert ( MAXLEN > pcNew->mKey.length);
 	::strcpy( pcNew->mKey.data, pKey );
 	::strcpy( pcNew->mKey.data, pKey );
 
 
-	if (0xFFFFFFFF != iOutIndex)
-	{
+	if (0xffffffff != iOutIndex)	{
 		mProperties[iOutIndex] = pcNew;
 		mProperties[iOutIndex] = pcNew;
 		return AI_SUCCESS;
 		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;
 		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*));
 		::memcpy (ppTemp,mProperties,iOld * sizeof(void*));
 
 
 		delete[] mProperties;
 		delete[] mProperties;
@@ -407,15 +399,20 @@ aiReturn MaterialHelper::AddProperty (const aiString* pInput,
 	unsigned int type,
 	unsigned int type,
     unsigned int index)
     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)),
 		(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, 
 void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest, 
-	const MaterialHelper* pcSrc)
+	const MaterialHelper* pcSrc
+	)
 {
 {
 	ai_assert(NULL != pcDest);
 	ai_assert(NULL != pcDest);
 	ai_assert(NULL != pcSrc);
 	ai_assert(NULL != pcSrc);
@@ -427,32 +424,28 @@ void MaterialHelper::CopyPropertyList(MaterialHelper* pcDest,
 	aiMaterialProperty** pcOld = pcDest->mProperties;
 	aiMaterialProperty** pcOld = pcDest->mProperties;
 	pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated];
 	pcDest->mProperties = new aiMaterialProperty*[pcDest->mNumAllocated];
 
 
-	if (iOldNum && pcOld)
-	{
+	if (iOldNum && pcOld)	{
 		for (unsigned int i = 0; i < iOldNum;++i)
 		for (unsigned int i = 0; i < iOldNum;++i)
 			pcDest->mProperties[i] = pcOld[i];
 			pcDest->mProperties[i] = pcOld[i];
 
 
 		delete[] pcOld;
 		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];
 		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;
 		aiMaterialProperty* prop;
-		for (unsigned int q = 0; q < iOldNum;++q)
-		{
+		for (unsigned int q = 0; q < iOldNum;++q) {
 			prop = pcDest->mProperties[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;
 				delete prop;
 
 
 				// collapse the whole array ...
 				// collapse the whole array ...
 				::memmove(&pcDest->mProperties[q],&pcDest->mProperties[q+1],i-q);
 				::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){
 	if (flags){
 		aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags);
 		aiGetMaterialInteger(mat,AI_MATKEY_TEXFLAGS(type,index),(int*)flags);
 	}
 	}
-
 	return AI_SUCCESS;
 	return AI_SUCCESS;
 }
 }
 
 

+ 23 - 4
code/SkeletonMeshBuilder.cpp

@@ -65,9 +65,9 @@ SkeletonMeshBuilder::SkeletonMeshBuilder( aiScene* pScene, aiNode* root)
 	pScene->mMeshes = new aiMesh*[1];
 	pScene->mMeshes = new aiMesh*[1];
 	pScene->mMeshes[0] = CreateMesh();
 	pScene->mMeshes[0] = CreateMesh();
 	// and install it at the root node
 	// 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
 	// create a dummy material for the mesh
 	pScene->mNumMaterials = 1;
 	pScene->mNumMaterials = 1;
@@ -207,6 +207,8 @@ aiMesh* SkeletonMeshBuilder::CreateMesh()
 	mesh->mVertices = new aiVector3D[mesh->mNumVertices];
 	mesh->mVertices = new aiVector3D[mesh->mNumVertices];
 	std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
 	std::copy( mVertices.begin(), mVertices.end(), mesh->mVertices);
 
 
+	mesh->mNormals = new aiVector3D[mesh->mNumVertices];
+
 	// add faces
 	// add faces
 	mesh->mNumFaces = mFaces.size();
 	mesh->mNumFaces = mFaces.size();
 	mesh->mFaces = new aiFace[mesh->mNumFaces];
 	mesh->mFaces = new aiFace[mesh->mNumFaces];
@@ -219,6 +221,19 @@ aiMesh* SkeletonMeshBuilder::CreateMesh()
 		outface.mIndices[0] = inface.mIndices[0];
 		outface.mIndices[0] = inface.mIndices[0];
 		outface.mIndices[1] = inface.mIndices[1];
 		outface.mIndices[1] = inface.mIndices[1];
 		outface.mIndices[2] = inface.mIndices[2];
 		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
 	// add the bones
@@ -239,8 +254,12 @@ aiMaterial* SkeletonMeshBuilder::CreateMaterial()
 	Assimp::MaterialHelper* matHelper = new Assimp::MaterialHelper;
 	Assimp::MaterialHelper* matHelper = new Assimp::MaterialHelper;
 
 
 	// Name
 	// Name
-	aiString matName( std::string( "Material"));
+	aiString matName( std::string( "SkeletonMaterial"));
 	matHelper->AddProperty( &matName, AI_MATKEY_NAME);
 	matHelper->AddProperty( &matName, AI_MATKEY_NAME);
 
 
+	// Prevent backface culling
+	const int no_cull = 1;
+	matHelper->AddProperty(&no_cull,1,AI_MATKEY_TWOSIDED);
+
 	return matHelper;
 	return matHelper;
 }
 }

+ 7 - 46
code/ValidateDataStructure.cpp

@@ -50,6 +50,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "ValidateDataStructure.h"
 #include "ValidateDataStructure.h"
 #include "BaseImporter.h"
 #include "BaseImporter.h"
 #include "fast_atof.h"
 #include "fast_atof.h"
+#include "ProcessHelper.h"
 
 
 // CRT headers
 // CRT headers
 #include <stdarg.h>
 #include <stdarg.h>
@@ -59,16 +60,12 @@ using namespace Assimp;
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Constructor to be privately used by Importer
 // Constructor to be privately used by Importer
 ValidateDSProcess::ValidateDSProcess()
 ValidateDSProcess::ValidateDSProcess()
-{
-	// nothing to do here
-}
+{}
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Destructor, private as well
 // Destructor, private as well
 ValidateDSProcess::~ValidateDSProcess()
 ValidateDSProcess::~ValidateDSProcess()
-{
-	// nothing to do here
-}
+{}
 
 
 // ------------------------------------------------------------------------------------------------
 // ------------------------------------------------------------------------------------------------
 // Returns whether the processing step is present in the given flag field.
 // 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,
 void ValidateDSProcess::SearchForInvalidTextures(const aiMaterial* pMaterial,
 	aiTextureType type)
 	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 ...
 	// 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)
 		for (unsigned int i = 0; i < pNodeAnim->mNumPositionKeys;++i)
 		{
 		{
 			// ScenePreprocessor will compute the duration if still teh default value
 			// 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 "
 				ReportError("aiNodeAnim::mPositionKeys[%i].mTime (%.5f) is larger "
 					"than aiAnimation::mDuration (which is %.5f)",i,
 					"than aiAnimation::mDuration (which is %.5f)",i,
@@ -863,7 +824,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 		double dLast = -10e10;
 		double dLast = -10e10;
 		for (unsigned int i = 0; i < pNodeAnim->mNumRotationKeys;++i)
 		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 "
 				ReportError("aiNodeAnim::mRotationKeys[%i].mTime (%.5f) is larger "
 					"than aiAnimation::mDuration (which is %.5f)",i,
 					"than aiAnimation::mDuration (which is %.5f)",i,
@@ -890,7 +851,7 @@ void ValidateDSProcess::Validate( const aiAnimation* pAnimation,
 		double dLast = -10e10;
 		double dLast = -10e10;
 		for (unsigned int i = 0; i < pNodeAnim->mNumScalingKeys;++i)
 		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 "
 				ReportError("aiNodeAnim::mScalingKeys[%i].mTime (%.5f) is larger "
 					"than aiAnimation::mDuration (which is %.5f)",i,
 					"than aiAnimation::mDuration (which is %.5f)",i,