Json::Value FileSaver::StoreFrame(KeyFrame* frame, const std::string& dir, bool single) { Json::Value value; value["id"] = frame->GetID(); value["time"] = frame->GetTime(); value["tween"] = frame->HasClassicTween(); value["lerp"] = libanim::FileSaver::StoreLerps(frame->GetLerps()); for (size_t i = 0, n = frame->Size(); i < n; ++i) value["actor"][i] = StoreActor(frame->GetSprite(i), dir, single); value["skeleton"] = StoreSkeleton(frame->GetSkeletonData()); return value; }
void StdMeshSkeletonLoader::LoadSkeletonXml(const char* groupname, const char* filename, const char *sourcefile, size_t size) { if (sourcefile == NULL) { throw Ogre::InsufficientData(FormatString("Failed to load '%s/%s'", groupname, filename).getData()); } std::shared_ptr<StdMeshLoader::StdMeshXML> skeleton(new StdMeshLoader::StdMeshXML(filename, sourcefile)); TiXmlElement* skeleton_elem = skeleton->RequireFirstChild(NULL, "skeleton"); TiXmlElement* bones_elem = skeleton->RequireFirstChild(skeleton_elem, "bones"); // Read bones. Don't insert into Master bone table yet, as the master bone // table is sorted hierarchically, and we will read the hierarchy only // afterwards. std::vector<StdMeshBone*> bones; for (TiXmlElement* bone_elem = bones_elem->FirstChildElement("bone"); bone_elem != NULL; bone_elem = bone_elem->NextSiblingElement("bone")) { StdMeshBone* bone = new StdMeshBone; bones.push_back(bone); bone->ID = skeleton->RequireIntAttribute(bone_elem, "id"); bone->Name = skeleton->RequireStrAttribute(bone_elem, "name"); // TODO: Make sure ID and name are unique bone->Parent = NULL; // Index of bone will be set when building Master Bone Table later TiXmlElement* position_elem = skeleton->RequireFirstChild(bone_elem, "position"); TiXmlElement* rotation_elem = skeleton->RequireFirstChild(bone_elem, "rotation"); TiXmlElement* axis_elem = skeleton->RequireFirstChild(rotation_elem, "axis"); StdMeshVector d, r; d.x = skeleton->RequireFloatAttribute(position_elem, "x"); d.y = skeleton->RequireFloatAttribute(position_elem, "y"); d.z = skeleton->RequireFloatAttribute(position_elem, "z"); float angle = skeleton->RequireFloatAttribute(rotation_elem, "angle"); r.x = skeleton->RequireFloatAttribute(axis_elem, "x"); r.y = skeleton->RequireFloatAttribute(axis_elem, "y"); r.z = skeleton->RequireFloatAttribute(axis_elem, "z"); bone->Transformation.scale = StdMeshVector::UnitScale(); bone->Transformation.rotate = StdMeshQuaternion::AngleAxis(angle, r); bone->Transformation.translate = d; // We need this later for applying animations, and attaching meshes, therefore cache it here bone->InverseTransformation = StdMeshTransformation::Inverse(bone->Transformation); } // Bone hierarchy TiXmlElement* bonehierarchy_elem = skeleton->RequireFirstChild(skeleton_elem, "bonehierarchy"); for (TiXmlElement* boneparent_elem = bonehierarchy_elem->FirstChildElement("boneparent"); boneparent_elem != NULL; boneparent_elem = boneparent_elem->NextSiblingElement("boneparent")) { const char* child_name = skeleton->RequireStrAttribute(boneparent_elem, "bone"); const char* parent_name = skeleton->RequireStrAttribute(boneparent_elem, "parent"); // Lookup the two bones StdMeshBone* child = NULL; StdMeshBone* parent = NULL; for (unsigned int i = 0; i < bones.size() && (!child || !parent); ++i) { if (!child && bones[i]->Name == child_name) child = bones[i]; if (!parent && bones[i]->Name == parent_name) parent = bones[i]; } if (!child) skeleton->Error(FormatString("There is no such bone with name '%s'", child_name), boneparent_elem); if (!parent) skeleton->Error(FormatString("There is no such bone with name '%s'", parent_name), boneparent_elem); child->Parent = parent; parent->Children.push_back(child); } std::shared_ptr<StdMeshSkeleton> Skeleton(new StdMeshSkeleton); // Fill master bone table in hierarchical order: for (unsigned int i = 0; i < bones.size(); ++i) if (bones[i]->Parent == NULL) Skeleton->AddMasterBone(bones[i]); // Load Animations TiXmlElement* animations_elem = skeleton_elem->FirstChildElement("animations"); if (animations_elem) { for (TiXmlElement* animation_elem = animations_elem->FirstChildElement("animation"); animation_elem != NULL; animation_elem = animation_elem->NextSiblingElement("animation")) { StdCopyStrBuf name(skeleton->RequireStrAttribute(animation_elem, "name")); if (Skeleton->Animations.find(name) != Skeleton->Animations.end()) skeleton->Error(FormatString("There is already an animation with name '%s'", name.getData()), animation_elem); StdMeshAnimation& animation = Skeleton->Animations.insert(std::make_pair(name, StdMeshAnimation())).first->second; animation.Name = name; animation.Length = skeleton->RequireFloatAttribute(animation_elem, "length"); animation.Tracks.resize(Skeleton->GetNumBones()); animation.OriginSkeleton = &(*Skeleton); TiXmlElement* tracks_elem = skeleton->RequireFirstChild(animation_elem, "tracks"); for (TiXmlElement* track_elem = tracks_elem->FirstChildElement("track"); track_elem != NULL; track_elem = track_elem->NextSiblingElement("track")) { const char* bone_name = skeleton->RequireStrAttribute(track_elem, "bone"); StdMeshBone* bone = NULL; for (unsigned int i = 0; !bone && i < Skeleton->GetNumBones(); ++i) if (Skeleton->Bones[i]->Name == bone_name) bone = Skeleton->Bones[i]; if (!bone) skeleton->Error(FormatString("There is no such bone with name '%s'", bone_name), track_elem); if (animation.Tracks[bone->Index] != NULL) skeleton->Error(FormatString("There is already a track for bone '%s' in animation '%s'", bone_name, animation.Name.getData()), track_elem); StdMeshTrack* track = new StdMeshTrack; animation.Tracks[bone->Index] = track; TiXmlElement* keyframes_elem = skeleton->RequireFirstChild(track_elem, "keyframes"); for (TiXmlElement* keyframe_elem = keyframes_elem->FirstChildElement("keyframe"); keyframe_elem != NULL; keyframe_elem = keyframe_elem->NextSiblingElement("keyframe")) { float time = skeleton->RequireFloatAttribute(keyframe_elem, "time"); StdMeshKeyFrame& frame = track->Frames[time]; TiXmlElement* translate_elem = keyframe_elem->FirstChildElement("translate"); TiXmlElement* rotate_elem = keyframe_elem->FirstChildElement("rotate"); TiXmlElement* scale_elem = keyframe_elem->FirstChildElement("scale"); StdMeshVector d, s, r; d.x = d.y = d.z = 0.0f; s = StdMeshVector::UnitScale(); r.x = r.y = 0.0f; r.z = 1.0f; float angle = 0.0f; if (translate_elem) { d.x = skeleton->RequireFloatAttribute(translate_elem, "x"); d.y = skeleton->RequireFloatAttribute(translate_elem, "y"); d.z = skeleton->RequireFloatAttribute(translate_elem, "z"); } if (rotate_elem) { TiXmlElement* axis_elem = skeleton->RequireFirstChild(rotate_elem, "axis"); angle = skeleton->RequireFloatAttribute(rotate_elem, "angle"); r.x = skeleton->RequireFloatAttribute(axis_elem, "x"); r.y = skeleton->RequireFloatAttribute(axis_elem, "y"); r.z = skeleton->RequireFloatAttribute(axis_elem, "z"); } if (scale_elem) { s.x = skeleton->RequireFloatAttribute(scale_elem, "x"); s.y = skeleton->RequireFloatAttribute(scale_elem, "y"); s.z = skeleton->RequireFloatAttribute(scale_elem, "z"); } frame.Transformation.scale = s; frame.Transformation.rotate = StdMeshQuaternion::AngleAxis(angle, r); frame.Transformation.translate = bone->InverseTransformation.rotate * (bone->InverseTransformation.scale * d); frame.Transformation = OgreToClonk::TransformTransformation(frame.Transformation); } } } } // is there even any xml file that we load from? // it looks like this could never work: if the mesh has no skeleton, then the code below will fail because of a null pointer... // Apply parent transformation to each bone transformation. We need to // do this late since animation keyframe computation needs the bone // transformations, not bone+parent. for (unsigned int i = 0; i < Skeleton->GetNumBones(); ++i) { // Apply parent transformation if (Skeleton->Bones[i]->Parent) Skeleton->Bones[i]->Transformation = Skeleton->Bones[i]->Parent->Transformation * OgreToClonk::TransformTransformation(Skeleton->Bones[i]->Transformation); else Skeleton->Bones[i]->Transformation = OgreToClonk::TransformTransformation(Skeleton->Bones[i]->Transformation); // Update inverse Skeleton->Bones[i]->InverseTransformation = StdMeshTransformation::Inverse(Skeleton->Bones[i]->Transformation); } StoreSkeleton(groupname, filename, Skeleton); }