//----------------------------------------------------------------------------- void XsiSkeletonExporter::buildBoneHierarchy(Skeleton* pSkeleton, DeformerMap& deformers, AnimationList& animList) { /// Copy all entries from map into a list so iterators won't get invalidated std::list<DeformerEntry*> deformerList; LogOgreAndXSI(L"-- Bones with vertex assignments:"); for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i) { DeformerEntry* deformer = i->second; deformerList.push_back(deformer); LogOgreAndXSI(deformer->obj.GetName()); } /* XSI allows you to use any object at all as a bone, not just chain elements. A typical choice is a hierarchy of nulls, for example. In order to build a skeleton hierarchy which represents the actual one, we need to find the relationships between all the deformers that we found. Well do this by navigating up the scene tree from each bone, looking for a match in the existing bone list or creating a new one where we need it to reach the root. We add bones even if they're not assigned vertices because the animation may depend on them. If the traversal hits the scene root this bone is clearly a root bone (there may be more than one). */ for (std::list<DeformerEntry*>::iterator i = deformerList.begin(); i != deformerList.end(); ++i) { DeformerEntry* deformer = *i; if (deformer->parentName.empty()) { linkBoneWithParent(deformer, deformers, deformerList); } } // Now eliminate all bones without any animated kine parameters // Need to do this after we've determined all relationships for (std::list<DeformerEntry*>::iterator i = deformerList.begin(); i != deformerList.end(); ++i) { DeformerEntry* deformer = *i; validateAsBone(pSkeleton, deformer, deformers, deformerList, animList); } // Now link for (DeformerMap::iterator i = deformers.begin(); i != deformers.end(); ++i) { DeformerEntry* deformer = i->second; // link to parent if (!deformer->parentName.empty()) { DeformerEntry* parent = getDeformer(deformer->parentName, deformers); assert (parent && "Parent not found"); assert (deformer->pBone && "Child bone not created"); assert(parent->pBone && "Parent bone not created"); parent->pBone->addChild(deformer->pBone); } } }
//----------------------------------------------------------------------- void copyFile(const String& src, const String& dst) { std::ifstream in(src.c_str(), std::ios::in | std::ios::binary); std::ofstream out(dst.c_str(), std::ios::out | std::ios::binary | std::ios::trunc); if (!in || !out) { LogOgreAndXSI("Unable to copy texture '" + src + "' to '" + dst + "'"); return; } char tmpBuf[2048]; while (!in.eof()) { in.read(tmpBuf, 2048); std::streamsize c = in.gcount(); out.write(tmpBuf, c); } in.close(); out.close(); }
//----------------------------------------------------------------------------- void XsiSkeletonExporter::linkBoneWithParent(DeformerEntry* child, DeformerMap& deformers, std::list<DeformerEntry*>& deformerList) { X3DObject parent(child->obj.GetParent()); String childName = XSItoOgre(child->obj.GetName()); if (child->obj == mXsiSceneRoot /* safety check for start node */) return; // Check for parenting by a chain end effector // These are sneaky little buggers - we actually want to attach the // child to the end of the final bone in the chain if (parent.IsA(XSI::siChainEffectorID)) { ChainEffector effector(parent); CRefArray chainBones = effector.GetRoot().GetBones(); // get the last parent = chainBones[chainBones.GetCount()-1]; child->parentIsChainEndEffector = true; } // is the parent the scene root? if (parent == mXsiSceneRoot) { // we hit the root node } else { String parentName = XSItoOgre(parent.GetName()); // Otherwise, check to see if the parent is in the deformer list DeformerEntry* parentDeformer = getDeformer(parentName, deformers); if (!parentDeformer) { // not found, create entry for parent parentDeformer = new DeformerEntry(deformers.size(), parent); deformers[parentName] = parentDeformer; deformerList.push_back(parentDeformer); LogOgreAndXSI(CString(L"Added ") + parent.GetName() + CString(L" as a parent of ") + child->obj.GetName() ); } // Link child entry with parent (not bone yet) // link child to parent by name child->parentName = parentName; parentDeformer->childNames.push_back(childName); } }
//----------------------------------------------------------------------------- const AxisAlignedBox& XsiSkeletonExporter::exportSkeleton(const String& skeletonFileName, DeformerMap& deformers, float framesPerSecond, AnimationList& animList) { LogOgreAndXSI(L"** Begin OGRE Skeleton Export **"); copyDeformerMap(deformers); SkeletonPtr skeleton = SkeletonManager::getSingleton().create("export", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME); // construct the hierarchy buildBoneHierarchy(skeleton.get(), deformers, animList); // progress report ProgressManager::getSingleton().progress(); establishInitialTransforms(deformers); // create animations mAABB.setNull(); createAnimations(skeleton.get(), deformers, framesPerSecond, animList, mAABB); // progress report ProgressManager::getSingleton().progress(); // Optimise skeleton->optimiseAllAnimations(); SkeletonSerializer ser; ser.exportSkeleton(skeleton.get(), skeletonFileName); // progress report ProgressManager::getSingleton().progress(); LogOgreAndXSI(L"** OGRE Skeleton Export Complete **"); cleanup(); return mAABB; }
//----------------------------------------------------------------------------- void XsiSkeletonExporter::createAnimations(Skeleton* pSkel, DeformerMap& deformers, float fps, AnimationList& animList, AxisAlignedBox& AABBPadding) { for (AnimationList::iterator ai = animList.begin(); ai != animList.end(); ++ai) { AnimationEntry& animEntry = *ai; // Note that we don't know if this time period includes bone animation // but we sample it anyway just in case; animation optimisation will // eliminate anything that's redundant // A little wasteful perhaps, but it's the only guaranteed way to pick // up all the potentially derived effects on deformers float animLength = (float)(animEntry.endFrame - animEntry.startFrame) / fps; StringUtil::StrStreamType str; str << "Creating animation " << animEntry.animationName << " with length " << animLength << " seconds"; LogOgreAndXSI(str.str()); Animation* anim = pSkel->createAnimation(animEntry.animationName, animLength); createAnimationTracksSampled(anim, animEntry, deformers, fps, AABBPadding); } }