diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index dffb04c3d..9e68c9332 100644 --- a/code/ColladaParser.cpp +++ b/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

-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

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& poChannels) // ------------------------------------------------------------------------------------------------ // Reads a

primitive index list and assembles the mesh data into the given mesh -void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPerIndexChannels, +size_t ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPerIndexChannels, size_t pNumPrimitives, const std::vector& pVCount, PrimitiveType pPrimType) { // determine number of indices coming per vertex @@ -2093,70 +2102,98 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPer acc->mData = &ResolveLibraryReference( mDataLibrary, acc->mSource); } - - // now assemble vertex data according to those indices - std::vector::const_iterator idx = indices.begin(); - // For continued primitives, the given count does not come all in one

, but only one primitive per

size_t numPrimitives = pNumPrimitives; if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) numPrimitives = 1; + // For continued primitives, the given count is actually the number of

'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::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::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& pPerIndexChannels, size_t currentPrimitive, const std::vector& 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::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::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& pPerIndexChannels, size_t currentPrimitive, const std::vector& 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); + } } // ------------------------------------------------------------------------------------------------ diff --git a/code/ColladaParser.h b/code/ColladaParser.h index c19a5e100..561ff05b3 100644 --- a/code/ColladaParser.h +++ b/code/ColladaParser.h @@ -177,9 +177,18 @@ protected: void ReadInputChannel( std::vector& poChannels); /** Reads a

primitive index list and assembles the mesh data into the given mesh */ - void ReadPrimitives( Collada::Mesh* pMesh, std::vector& pPerIndexChannels, + size_t ReadPrimitives( Collada::Mesh* pMesh, std::vector& pPerIndexChannels, size_t pNumPrimitives, const std::vector& 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& pPerIndexChannels, + size_t currentPrimitive, const std::vector& indices); + + /** Reads one triangle of a tristrip into the mesh */ + void ReadPrimTriStrips(size_t numOffsets, size_t perVertexOffset, Collada::Mesh* pMesh, + std::vector& pPerIndexChannels, size_t currentPrimitive, const std::vector& 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); diff --git a/test/models/Collada/cube_tristrips.dae b/test/models/Collada/cube_tristrips.dae new file mode 100644 index 000000000..83909045a --- /dev/null +++ b/test/models/Collada/cube_tristrips.dae @@ -0,0 +1,167 @@ + + + + + 2014-12-01T18:05:27Z + 2014-12-01T18:05:27Z + + Z_UP + + + + + + 1 0 0 90 + + 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 + + + + 1 0 0 -500 0 1 0 1000 0 0 1 400 0 0 0 1 + + + + 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 + + + + + + + + + + 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 + + + + 1 0 0 3 0 1 0 4 0 0 1 10 0 0 0 1 + + + + + + + + + + + + 37.8493 + 1 + 1 + 1000 + + + + + + + + + 37.8501 + 1 + 1 + 1000 + + + + + + + + + + + 1 1 1 + 1 + 0 + 0 + + + + + + + + 1 1 1 + 1 + 0 + 0 + + + + + + + + + -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 + + + + + + + + + + 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 + + + + + + + + + + + + + + +

6 6 9 9 0 0 3 3

+

12 12 1 1 15 15 4 4

+

14 14 17 17 20 20 23 23

+

5 5 11 11 16 16 22 22

+

13 13 19 19 2 2 8 8

+

21 21 10 10 18 18 7 7

+ + + + + + + + + + + + + + + + 0 0 0 1 + + + 0 0 0 1 + + + 0.137255 0.403922 0.870588 1 + + + 0.5 0.5 0.5 1 + + + 16 + + + + + + + + + +