Ogre::SkeletonPtr MilkshapePlugin::doExportSkeleton(msModel* pModel, Ogre::MeshPtr& mesh) { Ogre::LogManager &logMgr = Ogre::LogManager::getSingleton(); Ogre::String msg; // // choose filename // OPENFILENAME ofn; memset (&ofn, 0, sizeof (OPENFILENAME)); char szFile[MS_MAX_PATH]; char szFileTitle[MS_MAX_PATH]; char szDefExt[32] = "skeleton"; char szFilter[128] = "OGRE .skeleton Files (*.skeleton)\0*.skeleton\0All Files (*.*)\0*.*\0\0"; szFile[0] = '\0'; szFileTitle[0] = '\0'; ofn.lStructSize = sizeof (OPENFILENAME); ofn.lpstrDefExt = szDefExt; ofn.lpstrFilter = szFilter; ofn.lpstrFile = szFile; ofn.nMaxFile = MS_MAX_PATH; ofn.lpstrFileTitle = szFileTitle; ofn.nMaxFileTitle = MS_MAX_PATH; ofn.Flags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT | OFN_PATHMUSTEXIST; ofn.lpstrTitle = "Export to OGRE Skeleton"; if (!::GetSaveFileName (&ofn)) return Ogre::SkeletonPtr(); // Strip off the path Ogre::String skelName = szFile; size_t lastSlash = skelName.find_last_of("\\"); skelName = skelName.substr(lastSlash+1); // Set up logMgr.logMessage("Trying to create Skeleton object"); Ogre::SkeletonPtr ogreskel = Ogre::SkeletonManager::getSingleton().create(skelName, Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); logMgr.logMessage("Skeleton object created"); // Complete the details // Do the bones int numBones = msModel_GetBoneCount(pModel); msg = "Number of bones: " + Ogre::StringConverter::toString(numBones); logMgr.logMessage(msg); int i; // Create all the bones in turn for (i = 0; i < numBones; ++i) { msBone* bone = msModel_GetBoneAt(pModel, i); Ogre::Bone* ogrebone = ogreskel->createBone(bone->szName); msVec3 msBonePos, msBoneRot; msBone_GetPosition(bone, msBonePos); msBone_GetRotation(bone, msBoneRot); Ogre::Vector3 bonePos(msBonePos[0], msBonePos[1], msBonePos[2]); ogrebone->setPosition(bonePos); // Hmm, Milkshape has chosen a Euler angle representation of orientation which is not smart // Rotation Matrix or Quaternion would have been the smarter choice // Might we have Gimbal lock here? What order are these 3 angles supposed to be applied? // Grr, we'll try our best anyway... Ogre::Quaternion qx, qy, qz, qfinal; qx.FromAngleAxis(Ogre::Radian(msBoneRot[0]), Ogre::Vector3::UNIT_X); qy.FromAngleAxis(Ogre::Radian(msBoneRot[1]), Ogre::Vector3::UNIT_Y); qz.FromAngleAxis(Ogre::Radian(msBoneRot[2]), Ogre::Vector3::UNIT_Z); // Assume rotate by x then y then z qfinal = qz * qy * qx; ogrebone->setOrientation(qfinal); Ogre::LogManager::getSingleton().stream() << "Bone #" << i << ": " << "Name='" << bone->szName << "' " << "Position: " << bonePos << " " << "Ms3d Rotation: {" << msBoneRot[0] << ", " << msBoneRot[1] << ", " << msBoneRot[2] << "} " << "Orientation: " << qfinal; } // Now we've created all the bones, link them up logMgr.logMessage("Establishing bone hierarchy.."); for (i = 0; i < numBones; ++i) { msBone* bone = msModel_GetBoneAt(pModel, i); if (strlen(bone->szParentName) == 0) { // Root bone msg = "Root bone detected: Name='" + Ogre::String(bone->szName) + "' Index=" + Ogre::StringConverter::toString(i); logMgr.logMessage(msg); } else { Ogre::Bone* ogrechild = ogreskel->getBone(bone->szName); Ogre::Bone* ogreparent = ogreskel->getBone(bone->szParentName); if (ogrechild == 0) { msg = "Error: could not locate child bone '" + Ogre::String(bone->szName) + "'"; logMgr.logMessage(msg); continue; } if (ogreparent == 0) { msg = "Error: could not locate parent bone '" + Ogre::String(bone->szParentName) + "'"; logMgr.logMessage(msg); continue; } // Make child ogreparent->addChild(ogrechild); } } logMgr.logMessage("Bone hierarchy established."); // Create the Animation(s) doExportAnimations(pModel, ogreskel); // Create skeleton serializer & export Ogre::SkeletonSerializer serializer; msg = "Exporting skeleton to " + Ogre::String(szFile); logMgr.logMessage(msg); serializer.exportSkeleton(ogreskel.getPointer(), szFile); logMgr.logMessage("Skeleton exported"); msg = "Linking mesh to skeleton file '" + skelName + "'"; Ogre::LogManager::getSingleton().logMessage(msg); mesh->_notifySkeleton(ogreskel); return ogreskel; }
CalVector CMilkBoneNode::GetRelativeTranslation(float time) { // get the position msVec3 position; msBone_GetPosition(m_pIBone, position); CalVector initial_translation; initial_translation[0] = position[0]; initial_translation[1] = position[1]; initial_translation[2] = position[2]; // return if the initial state is requested or if there are no keyframes if((time < 0.0f) || (msBone_GetPositionKeyCount(m_pIBone) == 0)) { return initial_translation; } // calculate the real frame time // REMEMBER: milkshape starts at 1.0! float frameTime; frameTime = 1.0f + time * (float)theExporter.GetInterface()->GetFps(); // find the keyframe just before the requested time msPositionKey *pKeyBefore; pKeyBefore = msBone_GetPositionKeyAt(m_pIBone, msBone_GetPositionKeyCount(m_pIBone)-1); int keyId; for(keyId = 0; keyId < msBone_GetPositionKeyCount(m_pIBone); keyId++) { // get the keyframe msPositionKey *pKey; pKey = msBone_GetPositionKeyAt(m_pIBone, keyId); // stop if we are over the requested time if(pKey->fTime > frameTime) break; pKeyBefore = pKey; } // get the keyframe just after the requested time msPositionKey *pKeyAfter; pKeyAfter = msBone_GetPositionKeyAt(m_pIBone, 0); if(keyId < msBone_GetPositionKeyCount(m_pIBone)) { pKeyAfter = msBone_GetPositionKeyAt(m_pIBone, keyId); } // calculate the "just before" translation component CalVector translationBefore; if(pKeyBefore != 0) { //translationBefore = ConvertToVector(pKeyBefore->Translation); translationBefore[0] = pKeyBefore->Position[0]; translationBefore[1] = pKeyBefore->Position[1]; translationBefore[2] = pKeyBefore->Position[2]; // return if there is no key after this one if(pKeyAfter == 0) { return initial_translation + translationBefore; } } // calculate the "just after" translation component CalVector translationAfter; if(pKeyAfter != 0) { //translationAfter = ConvertToVector(pKeyAfter->Translation); translationAfter[0] = pKeyAfter->Position[0]; translationAfter[1] = pKeyAfter->Position[1]; translationAfter[2] = pKeyAfter->Position[2]; // return if there is no key before this one if(pKeyBefore == 0) return initial_translation + translationAfter; } // return if both keys are actually the same if(pKeyBefore == pKeyAfter) { return initial_translation + translationAfter; } // calculate the blending factor float factor; factor = (frameTime - pKeyBefore->fTime) / (pKeyAfter->fTime - pKeyBefore->fTime); // Have to blend the two translations. translationBefore = translationBefore + (translationAfter - translationBefore); return initial_translation + translationBefore; }