Bläddra i källkod

Merge pull request #414 from l337r007/collada-tristrips

Collada: tristrip support: helps a lot :-). Thanks for that!
Kim Kulling 10 år sedan
förälder
incheckning
bc0fd00e96
3 ändrade filer med 251 tillägg och 38 borttagningar
  1. 74 37
      code/ColladaParser.cpp
  2. 10 1
      code/ColladaParser.h
  3. 167 0
      test/models/Collada/cube_tristrips.dae

+ 74 - 37
code/ColladaParser.cpp

@@ -1867,14 +1867,15 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 	// read primitive count from the attribute
 	int attrCount = GetAttribute( "count");
 	size_t numPrimitives = (size_t) mReader->getAttributeValueAsInt( attrCount);
+	// some mesh types (e.g. tristrips) don't specify primitive count upfront,
+	// so we need to sum up the actual number of primitives while we read the <p>-tags
+	size_t actualPrimitives = 0;
 
-	// material subgroup 
+	// material subgroup
 	int attrMaterial = TestAttribute( "material");
 	SubMesh subgroup;
 	if( attrMaterial > -1)
 		subgroup.mMaterial = mReader->getAttributeValue( attrMaterial);
-	subgroup.mNumFaces = numPrimitives;
-	pMesh->mSubMeshes.push_back( subgroup);
 
 	// distinguish between polys and triangles
 	std::string elementName = mReader->getNodeName();
@@ -1933,7 +1934,7 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 				if( !mReader->isEmptyElement())
 				{
 					// now here the actual fun starts - these are the indices to construct the mesh data from
-					ReadPrimitives( pMesh, perIndexData, numPrimitives, vcount, primType);
+					actualPrimitives += ReadPrimitives(pMesh, perIndexData, numPrimitives, vcount, primType);
 				}
 			} else
 			{
@@ -1948,6 +1949,14 @@ void ColladaParser::ReadIndexData( Mesh* pMesh)
 			break;
 		}
 	}
+
+	// small sanity check
+	if (primType != Prim_TriFans && primType != Prim_TriStrips)
+		ai_assert(actualPrimitives == numPrimitives);
+
+	// only when we're done reading all <p> tags (and thus know the final vertex count) can we commit the submesh
+	subgroup.mNumFaces = actualPrimitives;
+	pMesh->mSubMeshes.push_back(subgroup);
 }
 
 // ------------------------------------------------------------------------------------------------
@@ -1995,7 +2004,7 @@ void ColladaParser::ReadInputChannel( std::vector<InputChannel>& poChannels)
 
 // ------------------------------------------------------------------------------------------------
 // Reads a <p> primitive index list and assembles the mesh data into the given mesh
-void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, 
+size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels,
 	size_t pNumPrimitives, const std::vector<size_t>& pVCount, PrimitiveType pPrimType)
 {
 	// determine number of indices coming per vertex 
@@ -2093,70 +2102,98 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector<InputChannel>& pPer
 			acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource);
 	}
 
-
-	// now assemble vertex data according to those indices
-	std::vector<size_t>::const_iterator idx = indices.begin();
-
 	// For continued primitives, the given count does not come all in one <p>, but only one primitive per <p>
 	size_t numPrimitives = pNumPrimitives;
 	if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon)
 		numPrimitives = 1;
+	// For continued primitives, the given count is actually the number of <p>'s inside the parent tag
+	if ( pPrimType == Prim_TriStrips){
+		size_t numberOfVertices = indices.size() / numOffsets;
+		numPrimitives = numberOfVertices - 2;
+	}
 
 	pMesh->mFaceSize.reserve( numPrimitives);
 	pMesh->mFacePosIndices.reserve( indices.size() / numOffsets);
 
-	for( size_t a = 0; a < numPrimitives; a++)
+	size_t polylistStartVertex = 0;
+	for (size_t currentPrimitive = 0; currentPrimitive < numPrimitives; currentPrimitive++)
 	{
 		// determine number of points for this primitive
 		size_t numPoints = 0;
 		switch( pPrimType)
 		{
 			case Prim_Lines:
-				numPoints = 2; 
+				numPoints = 2;
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
 				break;
-			case Prim_Triangles: 
-				numPoints = 3; 
+			case Prim_Triangles:
+				numPoints = 3;
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+				break;
+			case Prim_TriStrips:
+				numPoints = 3;
+				ReadPrimTriStrips(numOffsets, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
 				break;
 			case Prim_Polylist: 
-				numPoints = pVCount[a];
+				numPoints = pVCount[currentPrimitive];
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(polylistStartVertex + currentVertex, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, 0, indices);
+				polylistStartVertex += numPoints;
 				break;
 			case Prim_TriFans: 
 			case Prim_Polygon:
-				numPoints = indices.size() / numOffsets; 
+				numPoints = indices.size() / numOffsets;
+				for (size_t currentVertex = 0; currentVertex < numPoints; currentVertex++)
+					CopyVertex(currentVertex, numOffsets, numPoints, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
 				break;
 			default:
-				// LineStrip and TriStrip not supported due to expected index unmangling
+				// LineStrip is not supported due to expected index unmangling
 				ThrowException( "Unsupported primitive type.");
 				break;
 		}
 
 		// store the face size to later reconstruct the face from
 		pMesh->mFaceSize.push_back( numPoints);
-
-		// gather that number of vertices
-		for( size_t b = 0; b < numPoints; b++)
-		{
-			// read all indices for this vertex. Yes, in a hacky local array
-			ai_assert( numOffsets < 20 && perVertexOffset < 20);
-			size_t vindex[20];
-			for( size_t offsets = 0; offsets < numOffsets; ++offsets)
-				vindex[offsets] = *idx++;
-
-			// extract per-vertex channels using the global per-vertex offset
-      for( std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
-        ExtractDataObjectFromChannel( *it, vindex[perVertexOffset], pMesh);
-			// and extract per-index channels using there specified offset
-      for( std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
-				ExtractDataObjectFromChannel( *it, vindex[it->mOffset], pMesh);
-
-			// store the vertex-data index for later assignment of bone vertex weights
-			pMesh->mFacePosIndices.push_back( vindex[perVertexOffset]);
-		}
 	}
 
-
 	// if I ever get my hands on that guy who invented this steaming pile of indirection...
 	TestClosing( "p");
+	return numPrimitives;
+}
+
+void ColladaParser::CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
+	// calculate the base offset of the vertex whose attributes we ant to copy
+	size_t baseOffset = currentPrimitive * numOffsets * numPoints + currentVertex * numOffsets;
+
+	// don't overrun the boundaries of the index list
+	size_t maxIndexRequested = baseOffset + numOffsets - 1;
+	ai_assert(maxIndexRequested < indices.size());
+
+	// extract per-vertex channels using the global per-vertex offset
+	for (std::vector<InputChannel>::iterator it = pMesh->mPerVertexData.begin(); it != pMesh->mPerVertexData.end(); ++it)
+		ExtractDataObjectFromChannel(*it, indices[baseOffset + perVertexOffset], pMesh);
+	// and extract per-index channels using there specified offset
+	for (std::vector<InputChannel>::iterator it = pPerIndexChannels.begin(); it != pPerIndexChannels.end(); ++it)
+		ExtractDataObjectFromChannel(*it, indices[baseOffset + it->mOffset], pMesh);
+
+	// store the vertex-data index for later assignment of bone vertex weights
+	pMesh->mFacePosIndices.push_back(indices[baseOffset + perVertexOffset]);
+}
+
+void ColladaParser::ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Mesh* pMesh, std::vector<InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices){
+	if (currentPrimitive % 2 != 0){
+		//odd tristrip triangles need their indices mangled, to preserve winding direction
+		CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+	}
+	else {//for non tristrips or even tristrip triangles
+		CopyVertex(0, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(1, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+		CopyVertex(2, numOffsets, 1, perVertexOffset, pMesh, pPerIndexChannels, currentPrimitive, indices);
+	}
 }
 
 // ------------------------------------------------------------------------------------------------

+ 10 - 1
code/ColladaParser.h

@@ -177,9 +177,18 @@ protected:
 	void ReadInputChannel( std::vector<Collada::InputChannel>& poChannels);
 
 	/** Reads a <p> primitive index list and assembles the mesh data into the given mesh */
-	void ReadPrimitives( Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels, 
+	size_t ReadPrimitives( Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
 		size_t pNumPrimitives, const std::vector<size_t>& pVCount, Collada::PrimitiveType pPrimType);
 
+	/** Copies the data for a single primitive into the mesh, based on the InputChannels */
+	void CopyVertex(size_t currentVertex, size_t numOffsets, size_t numPoints, size_t perVertexOffset,
+		Collada::Mesh* pMesh, std::vector<Collada::InputChannel>& pPerIndexChannels,
+		size_t currentPrimitive, const std::vector<size_t>& indices);
+
+	/** Reads one triangle of a tristrip into the mesh */
+	void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh* pMesh,
+		std::vector<Collada::InputChannel>& pPerIndexChannels, size_t currentPrimitive, const std::vector<size_t>& indices);
+
 	/** Extracts a single object from an input channel and stores it in the appropriate mesh data array */
 	void ExtractDataObjectFromChannel( const Collada::InputChannel& pInput, size_t pLocalIndex, Collada::Mesh* pMesh);
 

+ 167 - 0
test/models/Collada/cube_tristrips.dae

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">
+	<asset>
+		<contributor/>
+		<created>2014-12-01T18:05:27Z</created>
+		<modified>2014-12-01T18:05:27Z</modified>
+		<unit/>
+		<up_axis>Z_UP</up_axis>
+	</asset>
+	<library_visual_scenes>
+		<visual_scene id="defaultScene">
+			<node id="sceneRoot">
+				<node id="Collada visual scene group">
+					<rotate sid="rotate">1 0 0 90</rotate>
+					<node id="Camera">
+						<matrix sid="Camera_matrix">0.838671 0.205746 -0.504282 -427.749 0 0.925901 0.377766 333.855 0.544639 -0.316822 0.776526 655.017 0 0 0 1</matrix>
+						<instance_camera url="#PerspCamera"/>
+					</node>
+					<node id="Light">
+						<matrix sid="Light_matrix">1 0 0 -500 0 1 0 1000 0 0 1 400 0 0 0 1</matrix>
+						<instance_light url="#light"/>
+					</node>
+					<node id="Box">
+						<matrix sid="Box_matrix">1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1</matrix>
+						<instance_geometry url="#BlueSG">
+							<bind_material>
+								<technique_common>
+									<instance_material symbol="BlueSG_material" target="#material"/>
+								</technique_common>
+							</bind_material>
+						</instance_geometry>
+					</node>
+					<node id="testCamera">
+						<matrix sid="testCamera_matrix">0.838671 0.205746 -0.504282 -427.749 0 0.925901 0.377766 333.855 0.544639 -0.316822 0.776526 655.017 0 0 0 1</matrix>
+						<instance_camera url="#testCameraShape"/>
+					</node>
+					<node id="pointLight1">
+						<matrix sid="pointLight1_matrix">1 0 0 3 0 1 0 4 0 0 1 10 0 0 0 1</matrix>
+						<instance_light url="#pointLightShape1"/>
+					</node>
+				</node>
+			</node>
+		</visual_scene>
+	</library_visual_scenes>
+	<library_cameras>
+		<camera id="PerspCamera">
+			<optics>
+				<technique_common>
+					<perspective>
+						<yfov>37.8493</yfov>
+						<aspect_ratio>1</aspect_ratio>
+						<znear>1</znear>
+						<zfar>1000</zfar>
+					</perspective>
+				</technique_common>
+			</optics>
+		</camera>
+		<camera id="testCameraShape">
+			<optics>
+				<technique_common>
+					<perspective>
+						<yfov>37.8501</yfov>
+						<aspect_ratio>1</aspect_ratio>
+						<znear>1</znear>
+						<zfar>1000</zfar>
+					</perspective>
+				</technique_common>
+			</optics>
+		</camera>
+	</library_cameras>
+	<library_lights>
+		<light id="light"/>
+		<light id="light">
+			<technique_common>
+				<point>
+					<color>1 1 1</color>
+					<constant_attenuation>1</constant_attenuation>
+					<linear_attenuation>0</linear_attenuation>
+					<quadratic_attenuation>0</quadratic_attenuation>
+				</point>
+			</technique_common>
+		</light>
+		<light id="pointLightShape1"/>
+		<light id="pointLightShape1">
+			<technique_common>
+				<point>
+					<color>1 1 1</color>
+					<constant_attenuation>1</constant_attenuation>
+					<linear_attenuation>0</linear_attenuation>
+					<quadratic_attenuation>0</quadratic_attenuation>
+				</point>
+			</technique_common>
+		</light>
+	</library_lights>
+	<library_geometries>
+		<geometry id="BlueSG">
+			<mesh>
+				<source id="BlueSG-positions">
+					<float_array id="BlueSG-positions-array" count="72">-50 50 50 -50 50 50 -50 50 50 50 50 50 50 50 50 50 50 50 -50 -50 50 -50 -50 50 -50 -50 50 50 -50 50 50 -50 50 50 -50 50 -50 50 -50 -50 50 -50 -50 50 -50 50 50 -50 50 50 -50 50 50 -50 -50 -50 -50 -50 -50 -50 -50 -50 -50 50 -50 -50 50 -50 -50 50 -50 -50</float_array>
+					<technique_common>
+						<accessor count="24" source="#BlueSG-positions-array" stride="3">
+							<param name="X" type="float"/>
+							<param name="Y" type="float"/>
+							<param name="Z" type="float"/>
+						</accessor>
+					</technique_common>
+				</source>
+				<source id="BlueSG-normals">
+					<float_array id="BlueSG-normals-array" count="72">0 0 1 0 1 0 -1 0 0 0 0 1 0 1 0 1 0 0 0 0 1 0 -1 0 -1 0 0 0 0 1 0 -1 0 1 0 0 0 1 0 -1 0 0 0 0 -1 0 1 0 1 0 0 0 0 -1 0 -1 0 -1 0 0 0 0 -1 0 -1 0 1 0 0 0 0 -1</float_array>
+					<technique_common>
+						<accessor count="24" source="#BlueSG-normals-array" stride="3">
+							<param name="X" type="float"/>
+							<param name="Y" type="float"/>
+							<param name="Z" type="float"/>
+						</accessor>
+					</technique_common>
+				</source>
+				<vertices id="BlueSG-vertices">
+					<input semantic="POSITION" source="#BlueSG-positions"/>
+				</vertices>
+				<tristrips count="6" material="BlueSG_material">
+					<input offset="0" semantic="VERTEX" source="#BlueSG-vertices" set="0"/>
+					<input offset="1" semantic="NORMAL" source="#BlueSG-normals" set="0"/>
+					<p>6 6 9 9 0 0 3 3</p>
+					<p>12 12 1 1 15 15 4 4</p>
+					<p>14 14 17 17 20 20 23 23</p>
+					<p>5 5 11 11 16 16 22 22</p>
+					<p>13 13 19 19 2 2 8 8</p>
+					<p>21 21 10 10 18 18 7 7</p>
+				</tristrips>
+			</mesh>
+		</geometry>
+	</library_geometries>
+	<library_materials>
+		<material id="material">
+			<instance_effect url="#material_effect"/>
+		</material>
+	</library_materials>
+	<library_effects>
+		<effect id="material_effect">
+			<profile_COMMON>
+				<technique sid="t0">
+					<phong>
+						<emission>
+							<color>0 0 0 1</color>
+						</emission>
+						<ambient>
+							<color>0 0 0 1</color>
+						</ambient>
+						<diffuse>
+							<color>0.137255 0.403922 0.870588 1</color>
+						</diffuse>
+						<specular>
+							<color>0.5 0.5 0.5 1</color>
+						</specular>
+						<shininess>
+							<float>16</float>
+						</shininess>
+					</phong>
+				</technique>
+			</profile_COMMON>
+		</effect>
+	</library_effects>
+	<scene>
+		<instance_visual_scene url="#defaultScene"/>
+	</scene>
+</COLLADA>