//////////////////////////////////////////////////////////////////////////////// // mesh.cpp // Author : Francesco Giordana // Start Date : January 13, 2005 // Copyright : (C) 2006 by Francesco Giordana // Email : fra.giordana@tiscali.it //////////////////////////////////////////////////////////////////////////////// /********************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU Lesser General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * **********************************************************************************/ #include "mesh.h" #include namespace OgreMayaExporter { /***** Class Mesh *****/ // constructor Mesh::Mesh(const MString& name) { m_name = name; m_numTriangles = 0; m_pSkeleton = NULL; m_sharedGeom.vertices.clear(); m_sharedGeom.dagMap.clear(); m_vertexClips.clear(); m_BSClips.clear(); } // destructor Mesh::~Mesh() { clear(); } // clear data void Mesh::clear() { int i; m_name = ""; m_numTriangles = 0; for (i=0; isetEnvelope(0); } } else { for (i=0; im_pBlendShape) pSubmesh->m_pBlendShape->setEnvelope(0); } } // Get the blend shape poses if (params.useSharedGeom) { for (i=0; iloadPoses(di.dagPath,params,m_sharedGeom.vertices,di.numVertices,di.offset); } } else { for (i=0; im_pBlendShape) pSubmesh->m_pBlendShape->loadPoses(pSubmesh->m_dagPath,params,pSubmesh->m_vertices, pSubmesh->m_vertices.size(),0,i); } } // Restore blend shape deformers original envelopes if (params.useSharedGeom) { for (i=0; irestoreEnvelope(); } } else { for (i=0; im_pBlendShape) pSubmesh->m_pBlendShape->restoreEnvelope(); } } // Read blend shape animations if (params.exportBSAnims) { stat = loadBlendShapeAnimations(params); } return MS::kSuccess; } // Load blend shape animations MStatus Mesh::loadBlendShapeAnimations(ParamList& params) { int i,j,k; std::cout << "Loading blend shape animations...\n"; std::cout.flush(); // Read the list of blend shape clips to export for (i=0; i tracks; for (j=0; jloadTrack(ci.start,ci.stop,ci.rate,params,startPoseId); tracks.push_back(t); startPoseId += di.pBlendShape->getPoses().size(); } } // Merge the tracks into a single track (shared geometry must have a single animation track) if (tracks.size() > 0) { Track newTrack; // Merge keyframes at the same time position from all tracks for (j=0; jposerefs.size(); pri++) { // Create a new pose reference vertexPoseRef poseref; // Copy pose reference settings from source keyframe poseref.poseIndex = pSrcKeyframe->poserefs[pri].poseIndex; poseref.poseWeight = pSrcKeyframe->poserefs[pri].poseWeight; // Add the new pose reference to the new keyframe newKeyframe.poserefs.push_back(poseref); } } // Add the keyframe to the new joined track newTrack.m_vertexKeyframes.push_back(newKeyframe); } // Add the joined track to current animation clip a.addTrack(newTrack); } } else { // Create a track for each submesh std::vector tracks; for (j=0; jm_pBlendShape) { Track t = pSubmesh->m_pBlendShape->loadTrack(ci.start,ci.stop,ci.rate,params,startPoseId); a.addTrack(t); startPoseId += pSubmesh->m_pBlendShape->getPoses().size(); } } } std::cout << "length: " << a.m_length << "\n"; std::cout << "num keyframes: " << a.m_tracks[0].m_vertexKeyframes.size() << "\n"; std::cout.flush(); m_BSClips.push_back(a); } return MS::kSuccess; } /******************** Methods to parse geometry data from Maya ************************/ // Get uvsets info from the maya mesh MStatus Mesh::getUVSets(const MDagPath& meshDag) { MFnMesh mesh(meshDag); int i; MStatus stat; // Get uv texture coordinate sets' names if (mesh.numUVSets() > 0) { stat = mesh.getUVSetNames(newuvsets); if (MS::kSuccess != stat) { std::cout << "Error retrieving UV sets names\n"; std::cout.flush(); return MS::kFailure; } } // Save uvsets info for (i=m_uvsets.size(); inumOutputConnections(); unsigned int uiGeometry; for(uiGeometry = 0; uiGeometry < uiNumGeometries; ++uiGeometry ) { unsigned int uiIndex = pSkinCluster->indexForOutputConnection(uiGeometry); MObject kOutputObject = pSkinCluster->outputShapeAtIndex(uiIndex); if(kOutputObject == mesh.object()) { std::cout << "Found skin cluster " << pSkinCluster->name().asChar() << " for mesh " << mesh.name().asChar() << "\n"; std::cout.flush(); } else { delete pSkinCluster; pSkinCluster = NULL; } } } if (pSkinCluster) { // load the skeleton std::cout << "Loading skeleton data...\n"; std::cout.flush(); if (!m_pSkeleton) m_pSkeleton = new Skeleton(); stat = m_pSkeleton->load(pSkinCluster,params); if (MS::kSuccess != stat) { std::cout << "Error loading skeleton data\n"; std::cout.flush(); } else { std::cout << "OK\n"; std::cout.flush(); } } } return MS::kSuccess; } // Get blend shape deformer linked to the maya mesh MStatus Mesh::getBlendShapeDeformer(const MDagPath &meshDag, OgreMayaExporter::ParamList ¶ms) { MStatus stat; MFnMesh mesh(meshDag); if (params.exportBlendShapes) { // get connected blend shape deformer (if present) MItDependencyNodes kDepNodeIt( MFn::kBlendShape ); for( ;!kDepNodeIt.isDone() && !pBlendShape; kDepNodeIt.next()) { MObject kObject = kDepNodeIt.item(); MItDependencyGraph itGraph(kObject,MFn::kMesh,MItDependencyGraph::kDownstream,MItDependencyGraph::kDepthFirst); for (;!itGraph.isDone() && !pBlendShape; itGraph.next()) { MFnMesh connectedMesh(itGraph.thisNode()); if (connectedMesh.fullPathName() == mesh.fullPathName()) { pBlendShape = new BlendShape(); pBlendShape->load(kObject); std::cout << "Found blend shape deformer " << pBlendShape->getName().asChar() << " for mesh " << mesh.name().asChar() << "\n"; } } } } return MS::kSuccess; } // Get connected shaders MStatus Mesh::getShaders(const MDagPath& meshDag) { MStatus stat; MFnMesh mesh(meshDag); stat = mesh.getConnectedShaders(0,shaders,shaderPolygonMapping); std::cout.flush(); if (MS::kSuccess != stat) { std::cout << "Error getting connected shaders\n"; std::cout.flush(); return MS::kFailure; } std::cout << "Found " << shaders.length() << " connected shaders\n"; std::cout.flush(); if (shaders.length() <= 0) { std::cout << "No connected shaders, skipping mesh\n"; std::cout.flush(); return MS::kFailure; } // create a series of arrays of faces for each different submesh polygonSets.clear(); polygonSets.resize(shaders.length()); return MS::kSuccess; } // Get vertex data MStatus Mesh::getVertices(const MDagPath &meshDag, OgreMayaExporter::ParamList ¶ms) { int i; MFnMesh mesh(meshDag); // prepare vertex table newvertices.resize(mesh.numVertices()); newweights.resize(mesh.numVertices()); newjointIds.resize(mesh.numVertices()); for (i=0; igetWeights(meshDag,component,vertexWeights,numWeights); /* //normalize vertex weights int widx; // first, truncate the weights to given precision long weightSum = 0; for (widx=0; widx < vertexWeights.length(); widx++) { long w = (long) (((float)vertexWeights[widx]) / ((float)PRECISION)); vertexWeights[widx] = w; weightSum += w; } // then divide by the sum of the weights to add up to 1 // (if there is at least one weight > 0) if (weightSum > 0) { float newSum = 0; for (widx=0; widx < numWeights; widx++) { long w = (long) ((float)vertexWeights[widx] / ((float)PRECISION)); w = (long) (((float)w) / ((float)weightSum)); vertexWeights[widx] = (float) (((long)w) * ((float)PRECISION)); newSum += vertexWeights[widx]; } if (newSum < 1.0f) vertexWeights[numWeights-1] += PRECISION; } // else set all weights to 0 else { for (widx=0; widx < numWeights; widx++) vertexWeights[widx] = 0; }*/ // save the normalized weights newweights[i]=vertexWeights; if (MS::kSuccess != stat) { std::cout << "Error retrieving vertex weights\n"; std::cout.flush(); } // get ids for the joints if (m_pSkeleton) { MDagPathArray influenceObjs; pSkinCluster->influenceObjects(influenceObjs,&stat); if (MS::kSuccess != stat) { std::cout << "Error retrieving influence objects for given skin cluster\n"; std::cout.flush(); } newjointIds[i].setLength(newweights[i].length()); for (j=0; jgetJoints().size() && !foundJoint; k++) { if (partialPathName == m_pSkeleton->getJoints()[k].name) { foundJoint=true; newjointIds[i][j] = m_pSkeleton->getJoints()[k].id; } } } } } return MS::kSuccess; } // Get faces data MStatus Mesh::getFaces(const MDagPath &meshDag, ParamList ¶ms) { int i,j,k; MStatus stat; MFnMesh mesh(meshDag); // create an iterator to go through mesh polygons if (mesh.numPolygons() > 0) { std::cout << "Iterate over mesh polygons\n"; std::cout.flush(); MItMeshPolygon faceIter(mesh.object(),&stat); if (MS::kSuccess != stat) { std::cout << "Error accessing mesh polygons\n"; std::cout.flush(); return MS::kFailure; } std::cout << "num polygons = " << mesh.numPolygons() << "\n"; std::cout.flush(); // iterate over mesh polygons for (; !faceIter.isDone(); faceIter.next()) { int numTris=0; int iTris; bool different; int vtxIdx, nrmIdx; faceIter.numTriangles(numTris); // for every triangle composing current polygon extract triangle info for (iTris=0; iTris 1) color.r = 1; else if (color.r < PRECISION) color.r = 0; if (color.g > 1) color.g = 1; else if (color.g < PRECISION) color.g = 0; if (color.b > 1) color.b = 1; else if (color.b < PRECISION) color.b = 0; if (color.a > 1) color.a = 1; else if (color.a < PRECISION) color.a = 0; } else { color = MColor(1,1,1,1); } if (newvertices[vtxIdx].next == -2) // first time we encounter a vertex in this position { // save vertex position newpoints[vtxIdx].cartesianize(); newvertices[vtxIdx].pointIdx = vtxIdx; // save vertex normal newvertices[vtxIdx].normalIdx = nrmIdx; // save vertex colour newvertices[vtxIdx].r = color.r; newvertices[vtxIdx].g = color.g; newvertices[vtxIdx].b = color.b; newvertices[vtxIdx].a = color.a; // save vertex texture coordinates newvertices[vtxIdx].u.resize(newuvsets.length()); newvertices[vtxIdx].v.resize(newuvsets.length()); // save vbas newvertices[vtxIdx].vba.resize(newweights[vtxIdx].length()); for (j=0; j= 0) polygonSets[shaderPolygonMapping[faceIter.index()]].push_back(newFace); } // end iteration of triangles } } std::cout << "done reading mesh triangles\n"; std::cout.flush(); return MS::kSuccess; } // Build shared geometry MStatus Mesh::buildSharedGeometry(const MDagPath &meshDag,ParamList& params) { int i,j,k; std::cout << "Create list of shared vertices\n"; std::cout.flush(); // save a new entry in the shared geometry map: we associate the index of the first // vertex we're loading with the dag path from which it has been read dagInfo di; di.offset = m_sharedGeom.vertices.size(); di.dagPath = meshDag; di.pBlendShape = pBlendShape; // load shared vertices for (i=0; itexcoords.size(); jtexcoords.push_back(newTexCoords); } } // save number of vertices referring to this mesh dag in the dag path map di.numVertices = m_sharedGeom.vertices.size() - di.offset; m_sharedGeom.dagMap.push_back(di); std::cout << "done creating vertices list\n"; std::cout.flush(); return MS::kSuccess; } // Create submeshes MStatus Mesh::createSubmeshes(const MDagPath& meshDag,ParamList& params) { int i; MStatus stat; MFnMesh mesh(meshDag); for (i=0; i 0) { //create new submesh Submesh* pSubmesh = new Submesh(); //load linked shader stat = pSubmesh->loadMaterial(shaders[i],newuvsets,params); if (stat != MS::kSuccess) { MFnDependencyNode shadingGroup(shaders[i]); std::cout << "Error loading submesh linked to shader " << shadingGroup.name().asChar() << "\n"; std::cout.flush(); return MS::kFailure; } //load vertex and face data stat = pSubmesh->load(meshDag,polygonSets[i],newvertices,newpoints,newnormals,newuvsets,params,opposite); //if we're not using shared geometry, save a pointer to the blend shape deformer if (pBlendShape && !params.useSharedGeom) pSubmesh->m_pBlendShape = pBlendShape; //add submesh to current mesh m_submeshes.push_back(pSubmesh); //update number of triangles composing the mesh m_numTriangles += pSubmesh->numTriangles(); } } return MS::kSuccess; } /******************** Methods to read vertex animations from Maya ************************/ //load a vertex animation clip MStatus Mesh::loadClip(MString& clipName,float start,float stop,float rate,ParamList& params) { MStatus stat; MString msg; std::vector times; // calculate times from clip sample rate times.clear(); for (float t=start; t= 0) length = times[times.size()-1] - times[0]; if (length < 0) { std::cout << "invalid time range for the clip, we skip it\n"; std::cout.flush(); return MS::kFailure; } // create a new animation Animation a; a.m_name = clipName; a.m_length = length; a.m_tracks.clear(); // if we're using shared geometry, create a single animation track for the whole mesh if (params.useSharedGeom) { // load the animation track stat = loadMeshTrack(a,times,params); if (stat != MS::kSuccess) { std::cout << "Error loading mesh vertex animation\n"; std::cout.flush(); } } // else creae a different animation track for each submesh else { // load all tracks (one for each submesh) stat = loadSubmeshTracks(a,times,params); if (stat != MS::kSuccess) { std::cout << "Error loading submeshes vertex animation\n"; std::cout.flush(); return MS::kFailure; } } // add newly created animation to animations list m_vertexClips.push_back(a); // display info std::cout << "length: " << a.m_length << "\n"; std::cout << "num keyframes: " << a.m_tracks[0].m_vertexKeyframes.size() << "\n"; std::cout.flush(); // clip successfully loaded return MS::kSuccess; } //load an animation track for the whole mesh (using shared geometry) MStatus Mesh::loadMeshTrack(Animation& a,std::vector ×, OgreMayaExporter::ParamList ¶ms) { int i; MStatus stat; // create a new track Track t; t.m_type = TT_MORPH; t.m_target = T_MESH; t.m_vertexKeyframes.clear(); // get keyframes at given times for (i=0; i ×, OgreMayaExporter::ParamList ¶ms) { int i,j; MStatus stat; // create a new track for each submesh std::vector tracks; for (i=0; iloadKeyframe(tracks[j],times[i]-times[0],params); if (stat != MS::kSuccess) { std::cout << "Error reading animation keyframe at time: " << times[i] << " for submesh: " << j << "\n"; std::cout.flush(); } } } // add tracks to given animation for (i=0; i< tracks.size(); i++) a.addTrack(tracks[i]); // track sucessfully loaded return MS::kSuccess; return MS::kSuccess; } // Load a keyframe for the whole mesh MStatus Mesh::loadKeyframe(Track& t,float time,ParamList& params) { int i,j; // create a new keyframe vertexKeyframe k; // set keyframe time k.time = time; for (i=0; icreateOgreSubmesh(pMesh,params); } // Set skeleton link (if present) if (m_pSkeleton && params.exportSkeleton) { int ri = params.skeletonFilename.rindex('\\'); int end = params.skeletonFilename.length() - 1; MString filename = params.skeletonFilename.substring(ri+1,end); pMesh->setSkeletonName(filename.asChar()); } // Write poses if (params.exportBlendShapes) { createOgrePoses(pMesh,params); } // Write vertex animations if (params.exportVertAnims) { createOgreVertexAnimations(pMesh,params); } // Write pose animations if (params.exportBSAnims) { createOgrePoseAnimations(pMesh,params); } // Create a bounding box for the mesh Ogre::AxisAlignedBox bbox = pMesh->getBounds(); for(i=0; im_boundingBox.min(); MPoint max1 = m_submeshes[i]->m_boundingBox.max(); Ogre::Vector3 min2(min1.x,min1.y,min1.z); Ogre::Vector3 max2(max1.x,max1.y,max1.z); Ogre::AxisAlignedBox newbbox; newbbox.setExtents(min2,max2); bbox.merge(newbbox); } // Define mesh bounds pMesh->_setBounds(bbox,false); // Build edges list if (params.buildEdges) { pMesh->buildEdgeList(); } // Build tangents if (params.buildTangents) { bool canBuild = true; unsigned short srcTex, destTex; try { pMesh->suggestTangentVectorBuildParams(srcTex, destTex); } catch(Ogre::Exception e) { canBuild = false; } if (canBuild) pMesh->buildTangentVectors(srcTex, destTex); } // Export the binary mesh Ogre::MeshSerializer serializer; serializer.exportMesh(pMesh.getPointer(),params.meshFilename.asChar()); pMesh.setNull(); return MS::kSuccess; } // Create shared geometry data for an Ogre mesh MStatus Mesh::createOgreSharedGeometry(Ogre::MeshPtr pMesh,ParamList& params) { int i,j; MStatus stat; pMesh->sharedVertexData = new Ogre::VertexData(); pMesh->sharedVertexData->vertexCount = m_sharedGeom.vertices.size(); // Define a new vertex declaration Ogre::VertexDeclaration* pDecl = new Ogre::VertexDeclaration(); pMesh->sharedVertexData->vertexDeclaration = pDecl; unsigned buf = 0; size_t offset = 0; // Add vertex position pDecl->addElement(buf,offset,Ogre::VET_FLOAT3,Ogre::VES_POSITION); offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3); // Add vertex normal if (params.exportVertNorm) { pDecl->addElement(buf, offset, Ogre::VET_FLOAT3, Ogre::VES_NORMAL); offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3); } // Add vertex colour if (params.exportVertCol) { pDecl->addElement(buf, offset, Ogre::VET_FLOAT4, Ogre::VES_DIFFUSE); offset += Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT4); } // Add texture coordinates for (i=0; iaddElement(buf, offset, uvType, Ogre::VES_TEXTURE_COORDINATES, i); offset += Ogre::VertexElement::getTypeSize(uvType); } // Get optimal vertex declaration Ogre::VertexDeclaration* pOptimalDecl = pDecl->getAutoOrganisedDeclaration(params.exportVBA,params.exportBlendShapes || params.exportVertAnims); // Create the vertex buffer using the newly created vertex declaration stat = createOgreVertexBuffer(pMesh,pDecl,m_sharedGeom.vertices); // Write vertex bone assignements list if (params.exportVBA) { // Create a new vertex bone assignements list Ogre::Mesh::VertexBoneAssignmentList vbas; // Scan list of shared geometry vertices for (i=0; i 0.0f) vbas.insert(Ogre::Mesh::VertexBoneAssignmentList::value_type(i, vba)); } } // Rationalise the bone assignements list pMesh->_rationaliseBoneAssignments(pMesh->sharedVertexData->vertexCount,vbas); // Add bone assignements to the mesh for (Ogre::Mesh::VertexBoneAssignmentList::iterator bi = vbas.begin(); bi != vbas.end(); bi++) { pMesh->addBoneAssignment(bi->second); } pMesh->_compileBoneAssignments(); pMesh->_updateCompiledBoneAssignments(); } // Reorganize vertex buffers pMesh->sharedVertexData->reorganiseBuffers(pOptimalDecl); return MS::kSuccess; } // Create an Ogre compatible vertex buffer MStatus Mesh::createOgreVertexBuffer(Ogre::MeshPtr pMesh,Ogre::VertexDeclaration* pDecl,const std::vector& vertices) { Ogre::HardwareVertexBufferSharedPtr vbuf = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer(pDecl->getVertexSize(0), pMesh->sharedVertexData->vertexCount, Ogre::HardwareBuffer::HBU_STATIC_WRITE_ONLY); pMesh->sharedVertexData->vertexBufferBinding->setBinding(0, vbuf); size_t vertexSize = pDecl->getVertexSize(0); char* pBase = static_cast(vbuf->lock(Ogre::HardwareBuffer::HBL_DISCARD)); Ogre::VertexDeclaration::VertexElementList elems = pDecl->findElementsBySource(0); Ogre::VertexDeclaration::VertexElementList::iterator ei, eiend; eiend = elems.end(); float* pFloat; // Fill the vertex buffer with shared geometry data long vi; Ogre::ColourValue col; float ucoord, vcoord; for (vi=0; viunlock(); return MS::kSuccess; } // Create mesh poses for an Ogre mesh MStatus Mesh::createOgrePoses(Ogre::MeshPtr pMesh,ParamList& params) { int i,j,k; if (params.useSharedGeom) { // Read poses associated from all blendshapes associated to the shared geometry for (i=0; igetPoses().size(); j++) { // Get the pose pose* p = &(pBS->getPoses()[j]); if (p->name == "") { p->name = "pose"; p->name += j; } // Create a new pose for the ogre mesh Ogre::Pose* pPose = pMesh->createPose(0,p->name.asChar()); // Set the pose attributes for (k=0; koffsets.size(); k++) { Ogre::Vector3 offset(p->offsets[k].x,p->offsets[k].y,p->offsets[k].z); pPose->addVertex(p->offsets[k].index,offset); } } } } } else { // Get poses associated to the submeshes for (i=0; im_pBlendShape; // Check if this submesh has a blend shape deformer associated if (pBS) { // Get all poses from current blend shape deformer for (j=0; jgetPoses().size(); j++) { // Get the pose pose* p = &(pBS->getPoses()[j]); if (p->name == "") { p->name = "pose"; p->name += j; } // Create a new pose for the ogre mesh Ogre::Pose* pPose = pMesh->createPose(p->index,p->name.asChar()); // Set the pose attributes for (k=0; koffsets.size(); k++) { Ogre::Vector3 offset(p->offsets[k].x,p->offsets[k].y,p->offsets[k].z); pPose->addVertex(p->offsets[k].index,offset); } } } } } return MS::kSuccess; } // Create vertex animations for an Ogre mesh MStatus Mesh::createOgreVertexAnimations(Ogre::MeshPtr pMesh,ParamList& params) { int i,j,k; std::cout << "pippo\n"; std::cout.flush(); // Read the list of vertex animation clips for (i=0; icreateAnimation(m_vertexClips[i].m_name.asChar(),m_vertexClips[i].m_length); // Create all tracks for current animation for (j=0; jm_target == T_MESH) pTrack = pAnimation->createVertexTrack(0,pMesh->sharedVertexData,Ogre::VAT_MORPH); else { pTrack = pAnimation->createVertexTrack(t->m_index+1,pMesh->getSubMesh(t->m_index)->vertexData, Ogre::VAT_MORPH); } // Create keyframes for current track for (k=0; km_vertexKeyframes.size(); k++) { // Create a new keyframe Ogre::VertexMorphKeyFrame* pKeyframe = pTrack->createVertexMorphKeyFrame(t->m_vertexKeyframes[k].time); // Create vertex buffer for current keyframe Ogre::HardwareVertexBufferSharedPtr pBuffer = Ogre::HardwareBufferManager::getSingleton().createVertexBuffer( Ogre::VertexElement::getTypeSize(Ogre::VET_FLOAT3), t->m_vertexKeyframes[k].positions.size(), Ogre::HardwareBuffer::HBU_STATIC, true); float* pFloat = static_cast(pBuffer->lock(Ogre::HardwareBuffer::HBL_DISCARD)); // Fill the vertex buffer with vertex positions int vi; std::vector& positions = t->m_vertexKeyframes[k].positions; for (vi=0; vi(positions[vi].x); *pFloat++ = static_cast(positions[vi].y); *pFloat++ = static_cast(positions[vi].z); } // Unlock vertex buffer pBuffer->unlock(); // Set vertex buffer for current keyframe pKeyframe->setVertexBuffer(pBuffer); } } } return MS::kSuccess; } // Create pose animations for an Ogre mesh MStatus Mesh::createOgrePoseAnimations(Ogre::MeshPtr pMesh,ParamList& params) { int i,j,k; // Get all loaded blend shape clips for (i=0; icreateAnimation(m_BSClips[i].m_name.asChar(),m_BSClips[i].m_length); // Create animation tracks for this animation for (j=0; jm_target == T_MESH) pTrack = pAnimation->createVertexTrack(0,pMesh->sharedVertexData,Ogre::VAT_POSE); else { pTrack = pAnimation->createVertexTrack(t->m_index+1,pMesh->getSubMesh(t->m_index)->vertexData, Ogre::VAT_POSE); } // Create keyframes for current track for (k=0; km_vertexKeyframes.size(); k++) { Ogre::VertexPoseKeyFrame* pKeyframe = pTrack->createVertexPoseKeyFrame(t->m_vertexKeyframes[k].time); int pri; for (pri=0; prim_vertexKeyframes[k].poserefs.size(); pri++) { vertexPoseRef* pr = &t->m_vertexKeyframes[k].poserefs[pri]; pKeyframe->addPoseReference(pr->poseIndex,pr->poseWeight); } } } } return MS::kSuccess; } }; //end of namespace