From 90ba199ad412aa07400fb55af25d87fd93a2d421 Mon Sep 17 00:00:00 2001 From: Victor NG Date: Sat, 21 Oct 2017 18:28:14 +0800 Subject: [PATCH] Update ColladaExporter.cpp --- code/ColladaExporter.cpp | 254 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 250 insertions(+), 4 deletions(-) diff --git a/code/ColladaExporter.cpp b/code/ColladaExporter.cpp index 06604bc8e..52562a9f7 100644 --- a/code/ColladaExporter.cpp +++ b/code/ColladaExporter.cpp @@ -137,6 +137,9 @@ void ColladaExporter::WriteFile() WriteControllerLibrary(); WriteSceneLibrary(); + + // customized, Writes the animation library + WriteAnimationsLibrary(); // useless Collada fu at the end, just in case we haven't had enough indirections, yet. mOutput << startstr << "" << endstr; @@ -1125,6 +1128,7 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy case FloatType_Color: floatsPerElement = 3; break; case FloatType_Mat4x4: floatsPerElement = 16; break; case FloatType_Weight: floatsPerElement = 1; break; + case FloatType_Time: floatsPerElement = 1; break; default: return; } @@ -1201,7 +1205,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy case FloatType_Weight: mOutput << startstr << "" << endstr; break; - } + + // customized, add animation related + case FloatType_Time: + mOutput << startstr << "" << endstr; + break; + + } PopTag(); mOutput << startstr << "" << endstr; @@ -1231,7 +1241,170 @@ void ColladaExporter::WriteSceneLibrary() PopTag(); mOutput << startstr << "" << endstr; } +// ------------------------------------------------------------------------------------------------ +void ColladaExporter::WriteAnimationLibrary(size_t pIndex) +{ + const aiAnimation * anim = mScene->mAnimations[pIndex]; + + if ( anim->mNumChannels == 0 && anim->mNumMeshChannels == 0 && anim->mNumMorphMeshChannels ==0 ) + return; + + const std::string animation_name_escaped = XMLEscape( anim->mName.C_Str() ); + std::string idstr = anim->mName.C_Str(); + std::string ending = std::string( "AnimId" ) + to_string(pIndex); + if (idstr.length() >= ending.length()) { + if (0 != idstr.compare (idstr.length() - ending.length(), ending.length(), ending)) { + idstr = idstr + ending; + } + } else { + idstr = idstr + ending; + } + const std::string idstrEscaped = XMLEscape(idstr); + + mOutput << startstr << "" << endstr; + PushTag(); + + for (size_t a = 0; a < anim->mNumChannels; ++a) { + const aiNodeAnim * nodeAnim = anim->mChannels[a]; + + // sanity check + if ( nodeAnim->mNumPositionKeys != nodeAnim->mNumScalingKeys || nodeAnim->mNumPositionKeys != nodeAnim->mNumRotationKeys ) continue; + + { + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-input"); + + std::vector frames; + for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + frames.push_back(nodeAnim->mPositionKeys[i].mTime); + } + + WriteFloatArray( node_idstr , FloatType_Time, (const ai_real*) frames.data(), frames.size()); + frames.clear(); + } + + { + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-output"); + + std::vector keyframes; + keyframes.reserve(nodeAnim->mNumPositionKeys * 16); + for( size_t i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + + aiVector3D Scaling = nodeAnim->mScalingKeys[i].mValue; + aiMatrix4x4 ScalingM; // identity + ScalingM[0][0] = Scaling.x; ScalingM[1][1] = Scaling.y; ScalingM[2][2] = Scaling.z; + + aiQuaternion RotationQ = nodeAnim->mRotationKeys[i].mValue; + aiMatrix4x4 s = aiMatrix4x4( RotationQ.GetMatrix() ); + aiMatrix4x4 RotationM(s.a1, s.a2, s.a3, 0, s.b1, s.b2, s.b3, 0, s.c1, s.c2, s.c3, 0, 0, 0, 0, 1); + + aiVector3D Translation = nodeAnim->mPositionKeys[i].mValue; + aiMatrix4x4 TranslationM; // identity + TranslationM[0][3] = Translation.x; TranslationM[1][3] = Translation.y; TranslationM[2][3] = Translation.z; + + // Combine the above transformations + aiMatrix4x4 mat = TranslationM * RotationM * ScalingM; + + for( uint j = 0; j < 4; ++j) + keyframes.insert(keyframes.end(), mat[j], mat[j] + 4); + } + + WriteFloatArray( node_idstr, FloatType_Mat4x4, (const ai_real*) keyframes.data(), keyframes.size() / 16); + } + + { + std::vector names; + for (int i = 0; i < nodeAnim->mNumPositionKeys; ++i) { + if ( nodeAnim->mPreState == aiAnimBehaviour_DEFAULT + || nodeAnim->mPreState == aiAnimBehaviour_LINEAR + || nodeAnim->mPreState == aiAnimBehaviour_REPEAT + ) { + names.push_back( "LINEAR" ); + } else if (nodeAnim->mPostState == aiAnimBehaviour_CONSTANT) { + names.push_back( "STEP" ); + } + } + + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-interpolation"); + std::string arrayId = node_idstr + "-array"; + + mOutput << startstr << "" << endstr; + PushTag(); + + // source array + mOutput << startstr << " "; + for( size_t a = 0; a < names.size(); ++a ) + mOutput << names[a] << " "; + mOutput << "" << endstr; + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + } + + } + + for (size_t a = 0; a < anim->mNumChannels; ++a) { + const aiNodeAnim * nodeAnim = anim->mChannels[a]; + + { + // samplers + const std::string node_idstr = nodeAnim->mNodeName.data + std::string("_matrix-sampler"); + mOutput << startstr << "" << endstr; + PushTag(); + + mOutput << startstr << "mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr; + mOutput << startstr << "mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr; + + PopTag(); + mOutput << startstr << "" << endstr; + } + } + + for (size_t a = 0; a < anim->mNumChannels; ++a) { + const aiNodeAnim * nodeAnim = anim->mChannels[a]; + + { + // channels + mOutput << startstr << "mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr; + } + } + + PopTag(); + mOutput << startstr << "" << endstr; + +} +// ------------------------------------------------------------------------------------------------ +void ColladaExporter::WriteAnimationsLibrary() +{ + const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str()); + + if ( mScene->mNumAnimations > 0 ) { + mOutput << startstr << "" << endstr; + PushTag(); + + // start recursive write at the root node + for( size_t a = 0; a < mScene->mNumAnimations; ++a) + WriteAnimationLibrary( a ); + + PopTag(); + mOutput << startstr << "" << endstr; + } +} // ------------------------------------------------------------------------------------------------ // Helper to find a bone by name in the scene aiBone* findBone( const aiScene* scene, const char * name) { @@ -1247,6 +1420,59 @@ aiBone* findBone( const aiScene* scene, const char * name) { return NULL; } +// ------------------------------------------------------------------------------------------------ +const aiNode * findBoneNode( const aiNode* aNode, const aiBone* bone) +{ + if ( aNode && bone && aNode->mName == bone->mName ) { + return aNode; + } + + if ( aNode && bone ) { + for (unsigned int i=0; i < aNode->mNumChildren; ++i) { + aiNode * aChild = aNode->mChildren[i]; + const aiNode * foundFromChild = 0; + if ( aChild ) { + foundFromChild = findBoneNode( aChild, bone ); + if ( foundFromChild ) return foundFromChild; + } + } + } + + return NULL; +} + +const aiNode * findSkeletonRootNode( const aiScene* scene, const aiMesh * mesh) +{ + std::set topParentBoneNodes; + if ( mesh && mesh->mNumBones > 0 ) { + for (unsigned int i=0; i < mesh->mNumBones; ++i) { + aiBone * bone = mesh->mBones[i]; + + const aiNode * node = findBoneNode( scene->mRootNode, bone); + if ( node ) { + while ( node->mParent && findBone(scene, node->mParent->mName.C_Str() ) != 0 ) { + node = node->mParent; + } + topParentBoneNodes.insert( node ); + } + } + } + + if ( !topParentBoneNodes.empty() ) { + const aiNode * parentBoneNode = *topParentBoneNodes.begin(); + if ( topParentBoneNodes.size() == 1 ) { + return parentBoneNode; + } else { + for (auto it : topParentBoneNodes) { + if ( it->mParent ) return it->mParent; + } + return parentBoneNode; + } + } + + return NULL; +} + // ------------------------------------------------------------------------------------------------ // Recursively writes the given node void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) @@ -1274,12 +1500,22 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) } const std::string node_name_escaped = XMLEscape(pNode->mName.data); + /* // customized, Note! the id field is crucial for inter-xml look up, it cannot be replaced with sid ?! mOutput << startstr << "" << endstr; PushTag(); @@ -1287,7 +1523,11 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) // write transformation - we can directly put the matrix there // TODO: (thom) decompose into scale - rot - quad to allow addressing it by animations afterwards const aiMatrix4x4& mat = pNode->mTransformation; - mOutput << startstr << ""; + + // customized, sid should be 'matrix' to match with loader code. + //mOutput << startstr << ""; + mOutput << startstr << ""; + mOutput << mat.a1 << " " << mat.a2 << " " << mat.a3 << " " << mat.a4 << " "; mOutput << mat.b1 << " " << mat.b2 << " " << mat.b3 << " " << mat.b4 << " "; mOutput << mat.c1 << " " << mat.c2 << " " << mat.c3 << " " << mat.c4 << " "; @@ -1331,7 +1571,13 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode) << endstr; PushTag(); - mOutput << startstr << "#skeleton_root" << endstr; + // note! this mFoundSkeletonRootNodeID some how affects animation, it makes the mesh attaches to armature skeleton root node. + // use the first bone to find skeleton root + const aiNode * skeletonRootBoneNode = findSkeletonRootNode( pScene, mesh ); + if ( skeletonRootBoneNode ) { + mFoundSkeletonRootNodeID = XMLEscape( skeletonRootBoneNode->mName.C_Str() ); + } + mOutput << startstr << "#" << mFoundSkeletonRootNodeID << "" << endstr; } mOutput << startstr << "" << endstr; PushTag();