浏览代码

- Collada loader now also loads skinned meshes, although still as static meshes. Preparation step, more to come.

git-svn-id: https://assimp.svn.sourceforge.net/svnroot/assimp/trunk@368 67173fc5-114c-0410-ac8e-9d2fd5bffc1f
ulfjorensen 16 年之前
父节点
当前提交
74204086ae
共有 4 个文件被更改,包括 477 次插入92 次删除
  1. 33 5
      code/ColladaHelper.h
  2. 40 17
      code/ColladaLoader.cpp
  3. 377 67
      code/ColladaParser.cpp
  4. 27 3
      code/ColladaParser.h

+ 33 - 5
code/ColladaHelper.h

@@ -186,13 +186,15 @@ struct SemanticMappingTable
 	}
 };
 
-/** A reference to a mesh inside a node, including materials assigned to the various subgroups */
+/** A reference to a mesh inside a node, including materials assigned to the various subgroups.
+ * The ID refers to either a mesh or a controller which specifies the mesh
+ */
 struct MeshInstance
 {
-	 ///< ID of the mesh
-	std::string mMesh;
+	///< ID of the mesh or controller to be instanced
+	std::string mMeshOrController;
 
-	 ///< Map of materials by the subgroup ID they're applied to
+	///< Map of materials by the subgroup ID they're applied to
 	std::map<std::string, SemanticMappingTable> mMaterials;
 };
 
@@ -255,10 +257,12 @@ struct Node
 	}
 };
 
-/** Data source array */
+/** Data source array: either floats or strings */
 struct Data
 {
+	bool mIsStringArray;
 	std::vector<float> mValues;
+	std::vector<std::string> mStrings;
 };
 
 /** Accessor to a data array */
@@ -347,6 +351,30 @@ enum PrimitiveType
 	Prim_Polygon
 };
 
+/** A skeleton controller to deform a mesh with the use of joints */
+struct Controller
+{
+	// the URL of the mesh deformed by the controller.
+	std::string mMeshId; 
+
+	// accessor URL of the joint names
+	std::string mJointNameSource;
+
+	// accessor URL of the joint inverse bind matrices
+	std::string mJointOffsetMatrixSource;
+
+	// input channel: joint names. 
+	InputChannel mWeightInputJoints;
+	// input channel: joint weights
+	InputChannel mWeightInputWeights;
+
+	// Number of weights per vertex.
+	std::vector<size_t> mWeightCounts;
+
+	// JointIndex-WeightIndex pairs for all vertices
+	std::vector< std::pair<size_t, size_t> > mWeights;
+};
+
 /** A collada material. Pretty much the only member is a reference to an effect. */
 struct Material
 {

+ 40 - 17
code/ColladaLoader.cpp

@@ -399,14 +399,33 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll
 	// add a mesh for each subgroup in each collada mesh
 	BOOST_FOREACH( const Collada::MeshInstance& mid, pNode->mMeshes)
 	{
+		const Collada::Mesh* srcMesh = NULL;
+		const Collada::Controller* srcController = NULL;
+
 		// find the referred mesh
-		ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMesh);
+		ColladaParser::MeshLibrary::const_iterator srcMeshIt = pParser.mMeshLibrary.find( mid.mMeshOrController);
 		if( srcMeshIt == pParser.mMeshLibrary.end())
 		{
-			DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMesh));
-			continue;
+			// if not found in the mesh-library, it might also be a controller referring to a mesh
+			ColladaParser::ControllerLibrary::const_iterator srcContrIt = pParser.mControllerLibrary.find( mid.mMeshOrController);
+			if( srcContrIt != pParser.mControllerLibrary.end())
+			{
+				srcController = &srcContrIt->second;
+				srcMeshIt = pParser.mMeshLibrary.find( srcController->mMeshId);
+				if( srcMeshIt != pParser.mMeshLibrary.end())
+					srcMesh = srcMeshIt->second;
+			}
+
+			if( !srcMesh)
+			{
+				DefaultLogger::get()->warn( boost::str( boost::format( "Collada: Unable to find geometry for ID \"%s\". Skipping.") % mid.mMeshOrController));
+				continue;
+			}
+		} else
+		{
+			// ID found in the mesh library -> direct reference to an unskinned mesh
+			srcMesh = srcMeshIt->second;
 		}
-		const Collada::Mesh* srcMesh = srcMeshIt->second;
 
 		// build a mesh for each of its subgroups
 		size_t vertexStart = 0, faceStart = 0;
@@ -424,7 +443,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll
 				table = &meshMatIt->second;
 			else {
 				table = NULL;
-				DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMesh));
+				DefaultLogger::get()->warn( boost::str( boost::format( "Collada: No material specified for subgroup \"%s\" in geometry \"%s\".") % submesh.mMaterial % mid.mMeshOrController));
 			}
 			const std::string& meshMaterial = table ? table->mMatName : "";
 
@@ -451,7 +470,7 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll
 			}
 
 			// built lookup index of the Mesh-Submesh-Material combination
-			ColladaMeshIndex index( mid.mMesh, sm, meshMaterial);
+			ColladaMeshIndex index( mid.mMeshOrController, sm, meshMaterial);
 
 			// if we already have the mesh at the library, just add its index to the node's array
 			std::map<ColladaMeshIndex, size_t>::const_iterator dstMeshIt = mMeshIndexByID.find( index);
@@ -843,30 +862,33 @@ const aiString& ColladaLoader::FindFilenameForEffectTexture( const ColladaParser
 
 	// find the image referred by this name in the image library of the scene
 	ColladaParser::ImageLibrary::const_iterator imIt = pParser.mImageLibrary.find( name);
-	if( imIt == pParser.mImageLibrary.end()) {
+	if( imIt == pParser.mImageLibrary.end()) 
+	{
 		throw new ImportErrorException( boost::str( boost::format( 
 			"Collada: Unable to resolve effect texture entry \"%s\", ended up at ID \"%s\".") % pName % name));
 	}
+
 	static aiString result;
 
 	// if this is an embedded texture image setup an aiTexture for it
-	if (imIt->second.mFileName.empty()) {
-		if (imIt->second.mImageData.empty()) {
+	if (imIt->second.mFileName.empty()) 
+	{
+		if (imIt->second.mImageData.empty()) 
 			throw new ImportErrorException("Collada: Invalid texture, no data or file reference given");
-		}
+
 		aiTexture* tex = new aiTexture();
 
 		// setup format hint
-		if (imIt->second.mEmbeddedFormat.length() > 3) {
+		if (imIt->second.mEmbeddedFormat.length() > 3)
 			DefaultLogger::get()->warn("Collada: texture format hint is too long, truncating to 3 characters");
-		}
-		::strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
+
+		strncpy(tex->achFormatHint,imIt->second.mEmbeddedFormat.c_str(),3);
 
 		// and copy texture data
 		tex->mHeight = 0;
 		tex->mWidth = imIt->second.mImageData.size();
 		tex->pcData = (aiTexel*)new char[tex->mWidth];
-		::memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
+		memcpy(tex->pcData,&imIt->second.mImageData[0],tex->mWidth);
 
 		// setup texture reference string
 		result.data[0] = '*';
@@ -875,7 +897,8 @@ const aiString& ColladaLoader::FindFilenameForEffectTexture( const ColladaParser
 		// and add this texture to the list
 		mTextures.push_back(tex);
 	}
-	else {
+	else 
+	{
 		result.Set( imIt->second.mFileName );
 		ConvertPath(result);
 	}
@@ -890,10 +913,10 @@ void ColladaLoader::ConvertPath (aiString& ss)
 	// For the moment we're just stripping the file:// away to make it work.
 	// Windoes doesn't seem to be able to find stuff like
 	// 'file://..\LWO\LWO2\MappingModes\earthSpherical.jpg'
-	if (0 == ::strncmp(ss.data,"file://",7)) 
+	if (0 == strncmp(ss.data,"file://",7)) 
 	{
 		ss.length -= 7;
-		::memmove(ss.data,ss.data+7,ss.length);
+		memmove(ss.data,ss.data+7,ss.length);
 		ss.data[ss.length] = '\0';
 	}
 }

+ 377 - 67
code/ColladaParser.cpp

@@ -160,6 +160,8 @@ void ColladaParser::ReadStructure()
 		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
 			if( IsElement( "asset"))
 				ReadAssetInfo();
+			else if( IsElement( "library_controllers"))
+				ReadControllerLibrary();
 			else if( IsElement( "library_images"))
 				ReadImageLibrary();
 			else if( IsElement( "library_materials"))
@@ -233,6 +235,244 @@ void ColladaParser::ReadAssetInfo()
 	}
 }
 
+// ------------------------------------------------------------------------------------------------
+// Reads the skeleton controller library
+void ColladaParser::ReadControllerLibrary()
+{
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
+			if( IsElement( "controller"))
+			{
+				// read ID. Ask the spec if it's neccessary or optional... you might be surprised.
+				int attrID = GetAttribute( "id");
+				std::string id = mReader->getAttributeValue( attrID);
+
+				// create an entry and store it in the library under its ID
+				mControllerLibrary[id] = Controller();
+
+				// read on from there
+				ReadController( mControllerLibrary[id]);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
+			if( strcmp( mReader->getNodeName(), "library_controllers") != 0)
+				ThrowException( "Expected end of \"library_controllers\" element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads a controller into the given mesh structure
+void ColladaParser::ReadController( Collada::Controller& pController)
+{
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
+			// two types of controllers: "skin" and "morph". Only the first one is relevant, we skip the other
+			if( IsElement( "morph"))
+			{
+				// should skip everything inside, so there's no danger of catching elements inbetween
+				SkipElement();
+			} 
+			else if( IsElement( "skin"))
+			{
+				// read the mesh it refers to. According to the spec this could also be another
+				// controller, but I refuse to implement every bullshit idea they've come up with
+				int sourceIndex = GetAttribute( "source");
+				pController.mMeshId = mReader->getAttributeValue( sourceIndex) + 1;
+			} 
+			else if( IsElement( "bind_shape_matrix"))
+			{
+				// content is 16 floats to define some sort of matrix... I'm going to ignore this
+				// as long as I don't have a clue how to interpret it
+				SkipElement();
+			} 
+			else if( IsElement( "source"))
+			{
+				// data array - we have specialists to handle this
+				ReadSource();
+			} 
+			else if( IsElement( "joints"))
+			{
+				ReadControllerJoints( pController);
+			}
+			else if( IsElement( "vertex_weights"))
+			{
+				ReadControllerWeights( pController);
+			}
+			else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
+			if( strcmp( mReader->getNodeName(), "controller") == 0)
+				break;
+			else if( strcmp( mReader->getNodeName(), "skin") != 0)
+				ThrowException( "Expected end of \"controller\" element.");
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint definitions for the given controller
+void ColladaParser::ReadControllerJoints( Collada::Controller& pController)
+{
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
+			// Input channels for joint data. Two possible semantics: "JOINT" and "INV_BIND_MATRIX"
+			if( IsElement( "input"))
+			{
+				int indexSemantic = GetAttribute( "semantic");
+				const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
+				int indexSource = GetAttribute( "source");
+				const char* attrSource = mReader->getAttributeValue( indexSource);
+
+				// local URLS always start with a '#'. We don't support global URLs
+				if( attrSource[0] != '#')
+					ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource));
+				attrSource++;
+
+				// parse source URL to corresponding source
+				if( strcmp( attrSemantic, "JOINT") == 0)
+					pController.mJointNameSource = attrSource;
+				else if( strcmp( attrSemantic, "INV_BIND_MATRIX") == 0)
+					pController.mJointOffsetMatrixSource = attrSource;
+				else
+					ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in joint data") % attrSemantic));
+
+				// skip inner data, if present
+				if( !mReader->isEmptyElement())
+					SkipElement();
+			}
+			else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
+			if( strcmp( mReader->getNodeName(), "joints") != 0)
+				ThrowException( "Expected end of \"joints\" element.");
+
+			break;
+		}
+	}
+}
+
+// ------------------------------------------------------------------------------------------------
+// Reads the joint weights for the given controller
+void ColladaParser::ReadControllerWeights( Collada::Controller& pController)
+{
+	// read vertex count from attributes and resize the array accordingly
+	int indexCount = GetAttribute( "count");
+	size_t vertexCount = (size_t) mReader->getAttributeValueAsInt( indexCount);
+	pController.mWeightCounts.resize( vertexCount);
+
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
+			// Input channels for weight data. Two possible semantics: "JOINT" and "WEIGHT"
+			if( IsElement( "input"))
+			{
+				InputChannel channel;
+
+				int indexSemantic = GetAttribute( "semantic");
+				const char* attrSemantic = mReader->getAttributeValue( indexSemantic);
+				int indexSource = GetAttribute( "source");
+				const char* attrSource = mReader->getAttributeValue( indexSource);
+				int indexOffset = TestAttribute( "offset");
+				if( indexOffset >= 0)
+					channel.mOffset = mReader->getAttributeValueAsInt( indexOffset);
+
+				// local URLS always start with a '#'. We don't support global URLs
+				if( attrSource[0] != '#')
+					ThrowException( boost::str( boost::format( "Unsupported URL format in \"%s\"") % attrSource));
+				channel.mAccessor = attrSource + 1;
+
+				// parse source URL to corresponding source
+				if( strcmp( attrSemantic, "JOINT") == 0)
+					pController.mWeightInputJoints = channel;
+				else if( strcmp( attrSemantic, "WEIGHT") == 0)
+					pController.mWeightInputWeights = channel;
+				else
+					ThrowException( boost::str( boost::format( "Unknown semantic \"%s\" in vertex_weight data") % attrSemantic));
+
+				// skip inner data, if present
+				if( !mReader->isEmptyElement())
+					SkipElement();
+			}
+			else if( IsElement( "vcount"))
+			{
+				// read weight count per vertex
+				const char* text = GetTextContent();
+				size_t numWeights = 0;
+				for( std::vector<size_t>::iterator it = pController.mWeightCounts.begin(); it != pController.mWeightCounts.end(); ++it)
+				{
+					if( *text == 0)
+						ThrowException( "Out of data while reading vcount");
+
+					*it = strtol10( text, &text);
+					numWeights += *it;
+					SkipSpacesAndLineEnd( &text);
+				}
+
+				TestClosing( "vcount");
+
+				// reserve weight count 
+				pController.mWeights.resize( numWeights);
+			}
+			else if( IsElement( "v"))
+			{
+				// read JointIndex - WeightIndex pairs
+				const char* text = GetTextContent();
+
+				for( std::vector< std::pair<size_t, size_t> >::iterator it = pController.mWeights.begin(); it != pController.mWeights.end(); ++it)
+				{
+					if( *text == 0)
+						ThrowException( "Out of data while reading vertex_weights");
+					it->first = strtol10( text, &text);
+					SkipSpacesAndLineEnd( &text);
+					if( *text == 0)
+						ThrowException( "Out of data while reading vertex_weights");
+					it->second = strtol10( text, &text);
+					SkipSpacesAndLineEnd( &text);
+				}
+
+				TestClosing( "v");
+			}
+			else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
+			if( strcmp( mReader->getNodeName(), "vertex_weights") != 0)
+				ThrowException( "Expected end of \"vertex_weights\" element.");
+
+			break;
+		}
+	}
+}
+
 // ------------------------------------------------------------------------------------------------
 // Reads the image library contents
 void ColladaParser::ReadImageLibrary()
@@ -276,14 +516,15 @@ void ColladaParser::ReadImage( Collada::Image& pImage)
 			// Need to run different code paths here, depending on the Collada XSD version
 			if(  IsElement( "init_from"))
 			{
-				if (mFormat == FV_1_4_n) {
+				if (mFormat == FV_1_4_n) 
+				{
 					// element content is filename - hopefully
 					const char* sz = TestTextContent();
 					if (sz)pImage.mFileName = sz;
 					TestClosing( "init_from");
 				}
-				else if (mFormat == FV_1_5_n) {
-
+				else if (mFormat == FV_1_5_n) 
+				{
 					// make sure we skip over mip and array initializations, which
 					// we don't support, but which could confuse the loader if 
 					// they're not skipped.
@@ -302,7 +543,8 @@ void ColladaParser::ReadImage( Collada::Image& pImage)
 					// TODO: correctly jump over cube and volume maps?
 				}
 			}
-			else if (mFormat == FV_1_5_n) {
+			else if (mFormat == FV_1_5_n) 
+			{
 				if( IsElement( "ref"))
 				{
 					// element content is filename - hopefully
@@ -327,13 +569,14 @@ void ColladaParser::ReadImage( Collada::Image& pImage)
 
 					const unsigned int size = (unsigned int)(cur-data) * 2;
 					pImage.mImageData.resize(size);
-					for (unsigned int i = 0; i < size;++i) {
+					for (unsigned int i = 0; i < size;++i) 
 						pImage.mImageData[i] = HexOctetToDecimal(data+(i<<1));
-					}
+
 					TestClosing( "hex");
 				} 
 			}
-			else	{
+			else	
+			{
 				// ignore the rest
 				SkipElement();
 			}
@@ -351,7 +594,8 @@ void ColladaParser::ReadMaterialLibrary()
 {
 	while( mReader->read())
 	{
-		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
 			if( IsElement( "material"))
 			{
 				// read ID. By now you propably know my opinion about this "specification"
@@ -366,7 +610,8 @@ void ColladaParser::ReadMaterialLibrary()
 				SkipElement();
 			}
 		}
-		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) {
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END) 
+		{
 			if( strcmp( mReader->getNodeName(), "library_materials") != 0)
 				ThrowException( "Expected end of \"library_materials\" element.");
 
@@ -999,7 +1244,7 @@ void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
 		{
 			if( IsElement( "mesh"))
 			{
-        // read on from there
+				// read on from there
 				ReadMesh( pMesh);
 			} else
 			{
@@ -1021,33 +1266,15 @@ void ColladaParser::ReadGeometry( Collada::Mesh* pMesh)
 // Reads a mesh from the geometry library
 void ColladaParser::ReadMesh( Mesh* pMesh)
 {
-	// I'm doing a dirty state parsing here because I don't want to open another submethod for it.
-	// There's a <source> tag defining the name for the accessor inside, and possible a <float_array>
-	// with it's own ID. This string contains the current source's ID if parsing is inside a <source> element.
-	std::string presentSourceID;
-
 	while( mReader->read())
 	{
 		if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
 		{
 			if( IsElement( "source"))
 			{
-				// beginning of a source element - store ID for the inner elements
-				int attrID = GetAttribute( "id");
-				presentSourceID = mReader->getAttributeValue( attrID);
-			}
-			else if( IsElement( "float_array"))
-			{
-				ReadFloatArray();
-			}
-			else if( IsElement( "technique_common"))
-			{
-				// I don't fucking care for your profiles bullshit
+				// we have professionals dealing with this
+				ReadSource();
 			}
-			else if( IsElement( "accessor"))
-			{
-				ReadAccessor( presentSourceID);
-			} 
 			else if( IsElement( "vertices"))
 			{
 				// read per-vertex mesh data
@@ -1066,12 +1293,7 @@ void ColladaParser::ReadMesh( Mesh* pMesh)
 		}
 		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
 		{
-			if( strcmp( mReader->getNodeName(), "source") == 0)
-			{
-				// end of <source> - reset present source ID
-				presentSourceID.clear();
-			}
-			else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
+			if( strcmp( mReader->getNodeName(), "technique_common") == 0)
 			{
 				// end of another meaningless element - read over it
 			} 
@@ -1088,10 +1310,60 @@ void ColladaParser::ReadMesh( Mesh* pMesh)
 	}
 }
 
+// ------------------------------------------------------------------------------------------------
+// Reads a source element 
+void ColladaParser::ReadSource()
+{
+	int indexID = GetAttribute( "id");
+	std::string sourceID = mReader->getAttributeValue( indexID);
+
+	while( mReader->read())
+	{
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT)
+		{
+			if( IsElement( "float_array") || IsElement( "IDREF_array"))
+			{
+				ReadDataArray();
+			}
+			else if( IsElement( "technique_common"))
+			{
+				// I don't fucking care for your profiles bullshit
+			}
+			else if( IsElement( "accessor"))
+			{
+				ReadAccessor( sourceID);
+			} else
+			{
+				// ignore the rest
+				SkipElement();
+			}
+		}
+		else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)
+		{
+			if( strcmp( mReader->getNodeName(), "source") == 0)
+			{
+				// end of <source> - we're done
+				break;
+			}
+			else if( strcmp( mReader->getNodeName(), "technique_common") == 0)
+			{
+				// end of another meaningless element - read over it
+			} else
+			{
+				// everything else should be punished
+				ThrowException( "Expected end of \"source\" element.");
+			}
+		}
+	}
+}
+
 // ------------------------------------------------------------------------------------------------
 // Reads a data array holding a number of floats, and stores it in the global library
-void ColladaParser::ReadFloatArray()
+void ColladaParser::ReadDataArray()
 {
+	std::string elmName = mReader->getNodeName();
+	bool isStringArray = (elmName == "IDREF_array");
+
 	// read attributes
 	int indexID = GetAttribute( "id");
 	std::string id = mReader->getAttributeValue( indexID);
@@ -1102,22 +1374,45 @@ void ColladaParser::ReadFloatArray()
 	// read values and store inside an array in the data library
 	mDataLibrary[id] = Data();
 	Data& data = mDataLibrary[id];
-	data.mValues.reserve( count);
-	for( unsigned int a = 0; a < count; a++)
+	data.mIsStringArray = isStringArray;
+
+	if( isStringArray)
 	{
-		if( *content == 0)
-			ThrowException( "Expected more values while reading float_array contents.");
+		data.mStrings.reserve( count);
+		std::string s;
 
-		float value;
-		// read a number
-		content = fast_atof_move( content, value);
-		data.mValues.push_back( value);
-		// skip whitespace after it
-		SkipSpacesAndLineEnd( &content);
+		for( unsigned int a = 0; a < count; a++)
+		{
+			if( *content == 0)
+				ThrowException( "Expected more values while reading IDREF_array contents.");
+
+			s.clear();
+			while( !IsSpaceOrNewLine( *content))
+				s += *content++;
+			data.mStrings.push_back( s);
+
+			SkipSpacesAndLineEnd( &content);
+		}
+	} else
+	{
+		data.mValues.reserve( count);
+
+		for( unsigned int a = 0; a < count; a++)
+		{
+			if( *content == 0)
+				ThrowException( "Expected more values while reading float_array contents.");
+
+			float value;
+			// read a number
+			content = fast_atof_move( content, value);
+			data.mValues.push_back( value);
+			// skip whitespace after it
+			SkipSpacesAndLineEnd( &content);
+		}
 	}
 
 	// test for closing tag
-	TestClosing( "float_array");
+	TestClosing( elmName.c_str());
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1636,7 +1931,8 @@ void ColladaParser::ReadSceneNode( Node* pNode)
 
 	while( mReader->read())
 	{
-		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) {
+		if( mReader->getNodeType() == irr::io::EXN_ELEMENT) 
+		{
 			if( IsElement( "node"))
 			{
 				Node* child = new Node;
@@ -1651,12 +1947,13 @@ void ColladaParser::ReadSceneNode( Node* pNode)
 				// TODO: (thom) support SIDs
 				// assert( TestAttribute( "sid") == -1);
 
-				if (pNode) {
-
+				if (pNode) 
+				{
 					pNode->mChildren.push_back( child);
 					child->mParent = pNode;
 				}
-				else {
+				else 
+				{
 					// no parent node given, probably called from <library_nodes> element.
 					// create new node in node library
 					mNodeLibrary[child->mID] = pNode = child;
@@ -1689,36 +1986,44 @@ void ColladaParser::ReadSceneNode( Node* pNode)
 				// render a Collada scene. The only thing that is interesting for
 				// us is the primary camera.
 				int attrId = TestAttribute("camera_node");
-				if (-1 != attrId) {
+				if (-1 != attrId) 
+				{
 					const char* s = mReader->getAttributeValue(attrId);
 					if (s[0] != '#')
 						DefaultLogger::get()->error("Collada: Unresolved reference format of camera");
-					else pNode->mPrimaryCamera = s+1;
+					else 
+						pNode->mPrimaryCamera = s+1;
 				}
 			}
-			else if( IsElement( "instance_node")) {
+			else if( IsElement( "instance_node")) 
+			{
 				// find the node in the library
 				int attrID = TestAttribute( "url");
-				if( attrID != -1) {
+				if( attrID != -1) 
+				{
 					const char* s = mReader->getAttributeValue(attrID);
 					if (s[0] != '#')
 						DefaultLogger::get()->error("Collada: Unresolved reference format of node");
-					else {
+					else 
+					{
 						pNode->mNodeInstances.push_back(NodeInstance());
 						pNode->mNodeInstances.back().mNode = s+1;
 					}
 				}
 			} 
-			else if( IsElement( "instance_geometry")) {
-				// Reference to a mesh, with possible material associations
+			else if( IsElement( "instance_geometry") || IsElement( "instance_controller"))
+			{
+				// Reference to a mesh or controller, with possible material associations
 				ReadNodeGeometry( pNode);
 			}
-			else if( IsElement( "instance_light")) {
+			else if( IsElement( "instance_light")) 
+			{
 				// Reference to a light, name given in 'url' attribute
 				int attrID = TestAttribute("url");
 				if (-1 == attrID)
 					DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_light> element");
-				else {
+				else 
+				{
 					const char* url = mReader->getAttributeValue( attrID);
 					if( url[0] != '#')
 						ThrowException( "Unknown reference format in <instance_light> element");
@@ -1727,12 +2032,14 @@ void ColladaParser::ReadSceneNode( Node* pNode)
 					pNode->mLights.back().mLight = url+1;
 				}
 			}
-			else if( IsElement( "instance_camera")) {
+			else if( IsElement( "instance_camera")) 
+			{
 				// Reference to a camera, name given in 'url' attribute
 				int attrID = TestAttribute("url");
 				if (-1 == attrID)
 					DefaultLogger::get()->warn("Collada: Expected url attribute in <instance_camera> element");
-				else {
+				else 
+				{
 					const char* url = mReader->getAttributeValue( attrID);
 					if( url[0] != '#')
 						ThrowException( "Unknown reference format in <instance_camera> element");
@@ -1829,14 +2136,15 @@ void ColladaParser::ReadNodeGeometry( Node* pNode)
 		ThrowException( "Unknown reference format");
 	
 	Collada::MeshInstance instance;
-	instance.mMesh = url+1; // skipping the leading #
+	instance.mMeshOrController = url+1; // skipping the leading #
 
 	if( !mReader->isEmptyElement())
 	{
 		// read material associations. Ignore additional elements inbetween
 		while( mReader->read())
 		{
-			if( mReader->getNodeType() == irr::io::EXN_ELEMENT)	{
+			if( mReader->getNodeType() == irr::io::EXN_ELEMENT)	
+			{
 				if( IsElement( "instance_material"))
 				{
 					// read ID of the geometry subgroup and the target material
@@ -1858,8 +2166,10 @@ void ColladaParser::ReadNodeGeometry( Node* pNode)
 					instance.mMaterials[group] = s;
 				} 
 			} 
-			else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)	{
-				if( strcmp( mReader->getNodeName(), "instance_geometry") == 0)
+			else if( mReader->getNodeType() == irr::io::EXN_ELEMENT_END)	
+			{
+				if( strcmp( mReader->getNodeName(), "instance_geometry") == 0 
+					|| strcmp( mReader->getNodeName(), "instance_controller") == 0)
 					break;
 			} 
 		}

+ 27 - 3
code/ColladaParser.h

@@ -48,7 +48,8 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include "irrXMLWrapper.h"
 #include "ColladaHelper.h"
 
-namespace Assimp	{
+namespace Assimp
+{
 
 // ------------------------------------------------------------------------------------------
 /** Parser helper class for the Collada loader. 
@@ -76,6 +77,18 @@ protected:
 	/** Reads asset informations such as coordinate system informations and legal blah */
 	void ReadAssetInfo();
 
+	/** Reads the skeleton controller library */
+	void ReadControllerLibrary();
+
+	/** Reads a controller into the given mesh structure */
+	void ReadController( Collada::Controller& pController);
+
+	/** Reads the joint definitions for the given controller */
+	void ReadControllerJoints( Collada::Controller& pController);
+
+	/** Reads the joint weights for the given controller */
+	void ReadControllerWeights( Collada::Controller& pController);
+
 	/** Reads the image library contents */
 	void ReadImageLibrary();
 
@@ -127,8 +140,15 @@ protected:
 	/** Reads a mesh from the geometry library */
 	void ReadMesh( Collada::Mesh* pMesh);
 
-	/** Reads a data array holding a number of floats, and stores it in the global library */
-	void ReadFloatArray();
+	/** Reads a source element - a combination of raw data and an accessor defining 
+	 * things that should not be redefinable. Yes, that's another rant.
+	 */
+	void ReadSource();
+
+	/** Reads a data array holding a number of elements, and stores it in the global library.
+	 * Currently supported are array of floats and arrays of strings.
+	 */
+	void ReadDataArray();
 
 	/** Reads an accessor and stores it in the global library under the given ID - 
 	 * accessors use the ID of the parent <source> element
@@ -264,6 +284,10 @@ protected:
 	typedef std::map<std::string, Collada::Camera> CameraLibrary;
 	CameraLibrary mCameraLibrary;
 
+	/** Controller library: joint controllers by ID */
+	typedef std::map<std::string, Collada::Controller> ControllerLibrary;
+	ControllerLibrary mControllerLibrary;
+
 	/** Pointer to the root node. Don't delete, it just points to one of 
 	    the nodes in the node library. */
 	Collada::Node* mRootNode;