diff --git a/code/ColladaHelper.h b/code/ColladaHelper.h index 65f0491cb..2e78de938 100644 --- a/code/ColladaHelper.h +++ b/code/ColladaHelper.h @@ -333,6 +333,8 @@ struct Mesh // Faces. Stored are only the number of vertices for each face. 1 == point, 2 == line, 3 == triangle, 4+ == poly std::vector mFaceSize; + // Position indices for all faces in the sequence given in mFaceSize - necessary for bone weight assignment + std::vector mFacePosIndices; // Submeshes in this mesh, each with a given material std::vector mSubMeshes; diff --git a/code/ColladaLoader.cpp b/code/ColladaLoader.cpp index 392921a94..b3399e0e5 100644 --- a/code/ColladaLoader.cpp +++ b/code/ColladaLoader.cpp @@ -480,86 +480,13 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll } else { // else we have to add the mesh to the collection and store its newly assigned index at the node - aiMesh* dstMesh = new aiMesh; - - // count the vertices addressed by its faces - const size_t numVertices = std::accumulate( srcMesh->mFaceSize.begin() + faceStart, - srcMesh->mFaceSize.begin() + faceStart + submesh.mNumFaces, 0); - - // copy positions - dstMesh->mNumVertices = numVertices; - dstMesh->mVertices = new aiVector3D[numVertices]; - std::copy( srcMesh->mPositions.begin() + vertexStart, srcMesh->mPositions.begin() + - vertexStart + numVertices, dstMesh->mVertices); - - // normals, if given. HACK: (thom) Due to the fucking Collada spec we never - // know if we have the same number of normals as there are positions. So we - // also ignore any vertex attribute if it has a different count - if( srcMesh->mNormals.size() == srcMesh->mPositions.size()) - { - dstMesh->mNormals = new aiVector3D[numVertices]; - std::copy( srcMesh->mNormals.begin() + vertexStart, srcMesh->mNormals.begin() + - vertexStart + numVertices, dstMesh->mNormals); - } - - // tangents, if given. - if( srcMesh->mTangents.size() == srcMesh->mPositions.size()) - { - dstMesh->mTangents = new aiVector3D[numVertices]; - std::copy( srcMesh->mTangents.begin() + vertexStart, srcMesh->mTangents.begin() + - vertexStart + numVertices, dstMesh->mTangents); - } - - // bitangents, if given. - if( srcMesh->mBitangents.size() == srcMesh->mPositions.size()) - { - dstMesh->mBitangents = new aiVector3D[numVertices]; - std::copy( srcMesh->mBitangents.begin() + vertexStart, srcMesh->mBitangents.begin() + - vertexStart + numVertices, dstMesh->mBitangents); - } - - // same for texturecoords, as many as we have - for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) - { - if( srcMesh->mTexCoords[a].size() == srcMesh->mPositions.size()) - { - dstMesh->mTextureCoords[a] = new aiVector3D[numVertices]; - for( size_t b = 0; b < numVertices; ++b) - dstMesh->mTextureCoords[a][b] = srcMesh->mTexCoords[a][vertexStart+b]; - - dstMesh->mNumUVComponents[a] = srcMesh->mNumUVComponents[a]; - } - } - - // same for vertex colors, as many as we have - for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) - { - if( srcMesh->mColors[a].size() == srcMesh->mPositions.size()) - { - dstMesh->mColors[a] = new aiColor4D[numVertices]; - std::copy( srcMesh->mColors[a].begin() + vertexStart, srcMesh->mColors[a].begin() + vertexStart + numVertices, dstMesh->mColors[a]); - } - } - - // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex - size_t vertex = 0; - dstMesh->mNumFaces = submesh.mNumFaces; - dstMesh->mFaces = new aiFace[dstMesh->mNumFaces]; - for( size_t a = 0; a < dstMesh->mNumFaces; ++a) - { - size_t s = srcMesh->mFaceSize[ faceStart + a]; - aiFace& face = dstMesh->mFaces[a]; - face.mNumIndices = s; - face.mIndices = new unsigned int[s]; - for( size_t b = 0; b < s; ++b) - face.mIndices[b] = vertex++; - } + aiMesh* dstMesh = CreateMesh( pParser, srcMesh, submesh, srcController, vertexStart, faceStart); // store the mesh, and store its new index in the node newMeshRefs.push_back( mMeshes.size()); mMeshIndexByID[index] = mMeshes.size(); mMeshes.push_back( dstMesh); - vertexStart += numVertices; faceStart += submesh.mNumFaces; + vertexStart += dstMesh->mNumVertices; faceStart += submesh.mNumFaces; // assign the material index dstMesh->mMaterialIndex = matIdx; @@ -576,6 +503,192 @@ void ColladaLoader::BuildMeshesForNode( const ColladaParser& pParser, const Coll } } +// ------------------------------------------------------------------------------------------------ +// Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh +aiMesh* ColladaLoader::CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, + const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace) +{ + aiMesh* dstMesh = new aiMesh; + + // count the vertices addressed by its faces + const size_t numVertices = std::accumulate( pSrcMesh->mFaceSize.begin() + pStartFace, + pSrcMesh->mFaceSize.begin() + pStartFace + pSubMesh.mNumFaces, 0); + + // copy positions + dstMesh->mNumVertices = numVertices; + dstMesh->mVertices = new aiVector3D[numVertices]; + std::copy( pSrcMesh->mPositions.begin() + pStartVertex, pSrcMesh->mPositions.begin() + + pStartVertex + numVertices, dstMesh->mVertices); + + // normals, if given. HACK: (thom) Due to the fucking Collada spec we never + // know if we have the same number of normals as there are positions. So we + // also ignore any vertex attribute if it has a different count + if( pSrcMesh->mNormals.size() == pSrcMesh->mPositions.size()) + { + dstMesh->mNormals = new aiVector3D[numVertices]; + std::copy( pSrcMesh->mNormals.begin() + pStartVertex, pSrcMesh->mNormals.begin() + + pStartVertex + numVertices, dstMesh->mNormals); + } + + // tangents, if given. + if( pSrcMesh->mTangents.size() == pSrcMesh->mPositions.size()) + { + dstMesh->mTangents = new aiVector3D[numVertices]; + std::copy( pSrcMesh->mTangents.begin() + pStartVertex, pSrcMesh->mTangents.begin() + + pStartVertex + numVertices, dstMesh->mTangents); + } + + // bitangents, if given. + if( pSrcMesh->mBitangents.size() == pSrcMesh->mPositions.size()) + { + dstMesh->mBitangents = new aiVector3D[numVertices]; + std::copy( pSrcMesh->mBitangents.begin() + pStartVertex, pSrcMesh->mBitangents.begin() + + pStartVertex + numVertices, dstMesh->mBitangents); + } + + // same for texturecoords, as many as we have + for( size_t a = 0; a < AI_MAX_NUMBER_OF_TEXTURECOORDS; a++) + { + if( pSrcMesh->mTexCoords[a].size() == pSrcMesh->mPositions.size()) + { + dstMesh->mTextureCoords[a] = new aiVector3D[numVertices]; + for( size_t b = 0; b < numVertices; ++b) + dstMesh->mTextureCoords[a][b] = pSrcMesh->mTexCoords[a][pStartVertex+b]; + + dstMesh->mNumUVComponents[a] = pSrcMesh->mNumUVComponents[a]; + } + } + + // same for vertex colors, as many as we have + for( size_t a = 0; a < AI_MAX_NUMBER_OF_COLOR_SETS; a++) + { + if( pSrcMesh->mColors[a].size() == pSrcMesh->mPositions.size()) + { + dstMesh->mColors[a] = new aiColor4D[numVertices]; + std::copy( pSrcMesh->mColors[a].begin() + pStartVertex, pSrcMesh->mColors[a].begin() + pStartVertex + numVertices, dstMesh->mColors[a]); + } + } + + // create faces. Due to the fact that each face uses unique vertices, we can simply count up on each vertex + size_t vertex = 0; + dstMesh->mNumFaces = pSubMesh.mNumFaces; + dstMesh->mFaces = new aiFace[dstMesh->mNumFaces]; + for( size_t a = 0; a < dstMesh->mNumFaces; ++a) + { + size_t s = pSrcMesh->mFaceSize[ pStartFace + a]; + aiFace& face = dstMesh->mFaces[a]; + face.mNumIndices = s; + face.mIndices = new unsigned int[s]; + for( size_t b = 0; b < s; ++b) + face.mIndices[b] = vertex++; + } + + // create bones if given + if( pSrcController) + { + // refuse if the vertex count does not match +// if( pSrcController->mWeightCounts.size() != dstMesh->mNumVertices) +// throw new ImportErrorException( "Joint Controller vertex count does not match mesh vertex count"); + + // resolve references - joint names + const Collada::Accessor& jointNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointNameSource); + const Collada::Data& jointNames = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointNamesAcc.mSource); + // joint offset matrices + const Collada::Accessor& jointMatrixAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mJointOffsetMatrixSource); + const Collada::Data& jointMatrices = pParser.ResolveLibraryReference( pParser.mDataLibrary, jointMatrixAcc.mSource); + // joint vertex_weight name list - should refer to the same list as the joint names above. If not, report and reconsider + const Collada::Accessor& weightNamesAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputJoints.mAccessor); + if( &weightNamesAcc != &jointNamesAcc) + throw new ImportErrorException( "Temporary implementational lazyness. If you read this, please report to the author."); + // vertex weights + const Collada::Accessor& weightsAcc = pParser.ResolveLibraryReference( pParser.mAccessorLibrary, pSrcController->mWeightInputWeights.mAccessor); + const Collada::Data& weights = pParser.ResolveLibraryReference( pParser.mDataLibrary, weightsAcc.mSource); + + if( !jointNames.mIsStringArray || jointMatrices.mIsStringArray || weights.mIsStringArray) + throw new ImportErrorException( "Data type mismatch while resolving mesh joints"); + // sanity check: we rely on the vertex weights always coming as pairs of BoneIndex-WeightIndex + if( pSrcController->mWeightInputJoints.mOffset != 0 || pSrcController->mWeightInputWeights.mOffset != 1) + throw new ImportErrorException( "Unsupported vertex_weight adresssing scheme. Fucking collada spec."); + + // create containers to collect the weights for each bone + size_t numBones = jointNames.mStrings.size(); + std::vector > dstBones( numBones); + + // build a temporary array of pointers to the start of each vertex's weights + typedef std::vector< std::pair > IndexPairVector; + std::vector weightStartPerVertex( pSrcController->mWeightCounts.size()); + IndexPairVector::const_iterator pit = pSrcController->mWeights.begin(); + for( size_t a = 0; a < pSrcController->mWeightCounts.size(); ++a) + { + weightStartPerVertex[a] = pit; + pit += pSrcController->mWeightCounts[a]; + } + + // now for each vertex put the corresponding vertex weights into each bone's weight collection + for( size_t a = pStartVertex; a < pStartVertex + numVertices; ++a) + { + // which position index was responsible for this vertex? that's also the index by which + // the controller assigns the vertex weights + size_t orgIndex = pSrcMesh->mFacePosIndices[a]; + // find the vertex weights for this vertex + IndexPairVector::const_iterator iit = weightStartPerVertex[orgIndex]; + size_t pairCount = pSrcController->mWeightCounts[orgIndex]; + + for( size_t b = 0; b < pairCount; ++b, ++iit) + { + size_t jointIndex = iit->first; + size_t vertexIndex = iit->second; + + aiVertexWeight w; + w.mVertexId = a - pStartVertex; + w.mWeight = weights.mValues[vertexIndex]; + dstBones[jointIndex].push_back( w); + } + } + + // count the number of bones which influence vertices of the current submesh + size_t numRemainingBones = 0; + for( std::vector >::const_iterator it = dstBones.begin(); it != dstBones.end(); ++it) + if( it->size() > 0) + numRemainingBones++; + + // create bone array and copy bone weights one by one + dstMesh->mNumBones = numRemainingBones; + dstMesh->mBones = new aiBone*[numRemainingBones]; + size_t boneCount = 0; + for( size_t a = 0; a < numBones; ++a) + { + // omit bones without weights + if( dstBones[a].size() == 0) + continue; + + // create bone with its weights + aiBone* bone = new aiBone; + bone->mName = jointNames.mStrings[a]; + bone->mOffsetMatrix.a1 = jointMatrices.mValues[a*16 + 0]; + bone->mOffsetMatrix.a2 = jointMatrices.mValues[a*16 + 1]; + bone->mOffsetMatrix.a3 = jointMatrices.mValues[a*16 + 2]; + bone->mOffsetMatrix.a4 = jointMatrices.mValues[a*16 + 3]; + bone->mOffsetMatrix.b1 = jointMatrices.mValues[a*16 + 4]; + bone->mOffsetMatrix.b2 = jointMatrices.mValues[a*16 + 5]; + bone->mOffsetMatrix.b3 = jointMatrices.mValues[a*16 + 6]; + bone->mOffsetMatrix.b4 = jointMatrices.mValues[a*16 + 7]; + bone->mOffsetMatrix.c1 = jointMatrices.mValues[a*16 + 8]; + bone->mOffsetMatrix.c2 = jointMatrices.mValues[a*16 + 9]; + bone->mOffsetMatrix.c3 = jointMatrices.mValues[a*16 + 10]; + bone->mOffsetMatrix.c4 = jointMatrices.mValues[a*16 + 11]; + bone->mNumWeights = dstBones[a].size(); + bone->mWeights = new aiVertexWeight[bone->mNumWeights]; + std::copy( dstBones[a].begin(), dstBones[a].end(), bone->mWeights); + + // and insert bone + dstMesh->mBones[boneCount++] = bone; + } + } + + return dstMesh; +} + // ------------------------------------------------------------------------------------------------ // Stores all meshes in the given scene void ColladaLoader::StoreSceneMeshes( aiScene* pScene) diff --git a/code/ColladaLoader.h b/code/ColladaLoader.h index 52362b89d..544ae7098 100644 --- a/code/ColladaLoader.h +++ b/code/ColladaLoader.h @@ -114,6 +114,10 @@ protected: void BuildMeshesForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget); + /** Creates a mesh for the given ColladaMesh face subset and returns the newly created mesh */ + aiMesh* CreateMesh( const ColladaParser& pParser, const Collada::Mesh* pSrcMesh, const Collada::SubMesh& pSubMesh, + const Collada::Controller* pSrcController, size_t pStartVertex, size_t pStartFace); + /** Builds cameras for the given node and references them */ void BuildCamerasForNode( const ColladaParser& pParser, const Collada::Node* pNode, aiNode* pTarget); diff --git a/code/ColladaParser.cpp b/code/ColladaParser.cpp index 609aef7ff..bd401aaef 100644 --- a/code/ColladaParser.cpp +++ b/code/ColladaParser.cpp @@ -1758,6 +1758,9 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPer if( pPrimType == Prim_TriFans || pPrimType == Prim_Polygon) numPrimitives = 1; + pMesh->mFaceSize.reserve( numPrimitives); + pMesh->mFacePosIndices.reserve( indices.size() / numOffsets); + for( size_t a = 0; a < numPrimitives; a++) { // determine number of points for this primitive @@ -1801,6 +1804,9 @@ void ColladaParser::ReadPrimitives( Mesh* pMesh, std::vector& pPer // and extract per-index channels using there specified offset BOOST_FOREACH( const InputChannel& input, pPerIndexChannels) ExtractDataObjectFromChannel( input, vindex[input.mOffset], pMesh); + + // store the vertex-data index for later assignment of bone vertex weights + pMesh->mFacePosIndices.push_back( vindex[perVertexOffset]); } }