|
|
|
@ -141,6 +141,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 << "<scene>" << endstr;
|
|
|
|
@ -1129,6 +1132,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;
|
|
|
|
|
}
|
|
|
|
@ -1205,7 +1209,13 @@ void ColladaExporter::WriteFloatArray( const std::string& pIdString, FloatDataTy
|
|
|
|
|
case FloatType_Weight:
|
|
|
|
|
mOutput << startstr << "<param name=\"WEIGHT\" type=\"float\" />" << endstr;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// customized, add animation related
|
|
|
|
|
case FloatType_Time:
|
|
|
|
|
mOutput << startstr << "<param name=\"TIME\" type=\"float\" />" << endstr;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</accessor>" << endstr;
|
|
|
|
@ -1235,7 +1245,172 @@ void ColladaExporter::WriteSceneLibrary()
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</library_visual_scenes>" << 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 << "<animation id=\"" + idstrEscaped + "\" name=\"" + animation_name_escaped + "\">" << 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<ai_real> 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<ai_real> 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( size_t 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<std::string> names;
|
|
|
|
|
for ( size_t 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 << "<source id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|
|
|
|
|
|
// source array
|
|
|
|
|
mOutput << startstr << "<Name_array id=\"" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\"> ";
|
|
|
|
|
for( size_t a = 0; a < names.size(); ++a ) {
|
|
|
|
|
mOutput << names[a] << " ";
|
|
|
|
|
}
|
|
|
|
|
mOutput << "</Name_array>" << endstr;
|
|
|
|
|
|
|
|
|
|
mOutput << startstr << "<technique_common>" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|
|
|
|
|
|
mOutput << startstr << "<accessor source=\"#" << XMLEscape(arrayId) << "\" count=\"" << names.size() << "\" stride=\"" << 1 << "\">" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|
|
|
|
|
|
mOutput << startstr << "<param name=\"INTERPOLATION\" type=\"name\"></param>" << endstr;
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</accessor>" << endstr;
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</technique_common>" << endstr;
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</source>" << 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 << "<sampler id=\"" << XMLEscape(node_idstr) << "\">" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|
|
|
|
|
|
mOutput << startstr << "<input semantic=\"INPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-input") ) << "\"/>" << endstr;
|
|
|
|
|
mOutput << startstr << "<input semantic=\"OUTPUT\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-output") ) << "\"/>" << endstr;
|
|
|
|
|
mOutput << startstr << "<input semantic=\"INTERPOLATION\" source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-interpolation") ) << "\"/>" << endstr;
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</sampler>" << endstr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (size_t a = 0; a < anim->mNumChannels; ++a) {
|
|
|
|
|
const aiNodeAnim * nodeAnim = anim->mChannels[a];
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
// channels
|
|
|
|
|
mOutput << startstr << "<channel source=\"#" << XMLEscape( nodeAnim->mNodeName.data + std::string("_matrix-sampler") ) << "\" target=\"" << XMLEscape(nodeAnim->mNodeName.data) << "/matrix\"/>" << endstr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</animation>" << endstr;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
|
void ColladaExporter::WriteAnimationsLibrary()
|
|
|
|
|
{
|
|
|
|
|
const std::string scene_name_escaped = XMLEscape(mScene->mRootNode->mName.C_Str());
|
|
|
|
|
|
|
|
|
|
if ( mScene->mNumAnimations > 0 ) {
|
|
|
|
|
mOutput << startstr << "<library_animations>" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|
|
|
|
|
|
// start recursive write at the root node
|
|
|
|
|
for( size_t a = 0; a < mScene->mNumAnimations; ++a)
|
|
|
|
|
WriteAnimationLibrary( a );
|
|
|
|
|
|
|
|
|
|
PopTag();
|
|
|
|
|
mOutput << startstr << "</library_animations>" << endstr;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// ------------------------------------------------------------------------------------------------
|
|
|
|
|
// Helper to find a bone by name in the scene
|
|
|
|
|
aiBone* findBone( const aiScene* scene, const char * name) {
|
|
|
|
@ -1251,6 +1426,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<const aiNode*> 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)
|
|
|
|
@ -1278,12 +1506,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
|
|
|
|
|
<< "<node ";
|
|
|
|
|
if(is_skeleton_root)
|
|
|
|
|
mOutput << "id=\"" << "skeleton_root" << "\" "; // For now, only support one skeleton in a scene.
|
|
|
|
|
mOutput << (is_joint ? "s" : "") << "id=\"" << node_name_escaped;
|
|
|
|
|
mOutput << "\" name=\"" << node_name_escaped
|
|
|
|
|
*/
|
|
|
|
|
mOutput << startstr << "<node ";
|
|
|
|
|
if(is_skeleton_root) {
|
|
|
|
|
mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"" : "") ; // For now, only support one skeleton in a scene.
|
|
|
|
|
mFoundSkeletonRootNodeID = node_name_escaped;
|
|
|
|
|
} else {
|
|
|
|
|
mOutput << "id=\"" << node_name_escaped << "\" " << (is_joint ? "sid=\"" + node_name_escaped +"\"": "") ;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
mOutput << " name=\"" << node_name_escaped
|
|
|
|
|
<< "\" type=\"" << node_type
|
|
|
|
|
<< "\">" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
@ -1291,7 +1529,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 << "<matrix sid=\"transform\">";
|
|
|
|
|
|
|
|
|
|
// customized, sid should be 'matrix' to match with loader code.
|
|
|
|
|
//mOutput << startstr << "<matrix sid=\"transform\">";
|
|
|
|
|
mOutput << startstr << "<matrix sid=\"matrix\">";
|
|
|
|
|
|
|
|
|
|
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 << " ";
|
|
|
|
@ -1335,7 +1577,13 @@ void ColladaExporter::WriteNode( const aiScene* pScene, aiNode* pNode)
|
|
|
|
|
<< endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|
|
|
|
|
|
mOutput << startstr << "<skeleton>#skeleton_root</skeleton>" << 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 << "<skeleton>#" << mFoundSkeletonRootNodeID << "</skeleton>" << endstr;
|
|
|
|
|
}
|
|
|
|
|
mOutput << startstr << "<bind_material>" << endstr;
|
|
|
|
|
PushTag();
|
|
|
|
|