Browse Source

Assimp: Add initial support for skeletal mesh animations

Mitchell Stokes 10 years ago
parent
commit
e2c5214f39
2 changed files with 143 additions and 1 deletions
  1. 140 1
      pandatool/src/assimp/assimpLoader.cxx
  2. 3 0
      pandatool/src/assimp/assimpLoader.h

+ 140 - 1
pandatool/src/assimp/assimpLoader.cxx

@@ -32,6 +32,9 @@
 #include "look_at.h"
 #include "texturePool.h"
 #include "character.h"
+#include "animBundle.h"
+#include "animBundleNode.h"
+#include "animChannelMatrixXfmTable.h"
 #include "pvector.h"
 
 #include "pandaIOSystem.h"
@@ -169,7 +172,7 @@ build_graph() {
 }
 
 ////////////////////////////////////////////////////////////////////
-//     Function: AssimpLoader::find_ndoe
+//     Function: AssimpLoader::find_node
 //       Access: Private
 //  Description: Finds a node by name.
 ////////////////////////////////////////////////////////////////////
@@ -434,6 +437,88 @@ create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *pare
   }
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AssimpLoader::create_anim_channel
+//       Access: Private
+//  Description: Creates a AnimChannelMatrixXfmTable from an aiNodeAnim
+////////////////////////////////////////////////////////////////////
+void AssimpLoader::
+create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *parent, const aiNode &node)
+{
+  PT(AnimChannelMatrixXfmTable) group = new AnimChannelMatrixXfmTable(parent, node.mName.C_Str());
+
+  // See if there is a channel for this node
+  aiNodeAnim *node_anim = NULL;
+  for (size_t i = 0; i < anim.mNumChannels; ++i) {
+    if (anim.mChannels[i]->mNodeName == node.mName) {
+      node_anim = anim.mChannels[i];
+    }
+  }
+
+  if (node_anim) {
+    assimp_cat.debug()
+      << "Found channel for node: " << node.mName.C_Str() << "\n";
+    //assimp_cat.debug()
+    //  << "Num Position Keys " << node_anim->mNumPositionKeys << "\n";
+    //assimp_cat.debug()
+    //  << "Num Rotation Keys " << node_anim->mNumRotationKeys << "\n";
+    //assimp_cat.debug()
+    //  << "Num Scaling Keys " << node_anim->mNumScalingKeys << "\n";
+
+    // Convert positions
+    PTA_stdfloat tablex = PTA_stdfloat::empty_array(node_anim->mNumPositionKeys);
+    PTA_stdfloat tabley = PTA_stdfloat::empty_array(node_anim->mNumPositionKeys);
+    PTA_stdfloat tablez = PTA_stdfloat::empty_array(node_anim->mNumPositionKeys);
+    for (size_t i = 0; i < node_anim->mNumPositionKeys; ++i) {
+      tablex[i] = node_anim->mPositionKeys[i].mValue.x;
+      tabley[i] = node_anim->mPositionKeys[i].mValue.y;
+      tablez[i] = node_anim->mPositionKeys[i].mValue.z;
+    }
+    group->set_table('x', tablex);
+    group->set_table('y', tabley);
+    group->set_table('z', tablez);
+
+    // Convert rotations
+    PTA_stdfloat tableh = PTA_stdfloat::empty_array(node_anim->mNumRotationKeys);
+    PTA_stdfloat tablep = PTA_stdfloat::empty_array(node_anim->mNumRotationKeys);
+    PTA_stdfloat tabler = PTA_stdfloat::empty_array(node_anim->mNumRotationKeys);
+    for (size_t i = 0; i < node_anim->mNumRotationKeys; ++i) {
+      aiQuaternion ai_quat = node_anim->mRotationKeys[i].mValue;
+      LVecBase3 hpr = LQuaternion(ai_quat.w, ai_quat.x, ai_quat.y, ai_quat.z).get_hpr();
+      tableh[i] = hpr.get_x();
+      tablep[i] = hpr.get_y();
+      tabler[i] = hpr.get_z();
+    }
+    group->set_table('h', tableh);
+    group->set_table('p', tablep);
+    group->set_table('r', tabler);
+
+    // Convert scales
+    PTA_stdfloat tablei = PTA_stdfloat::empty_array(node_anim->mNumScalingKeys);
+    PTA_stdfloat tablej = PTA_stdfloat::empty_array(node_anim->mNumScalingKeys);
+    PTA_stdfloat tablek = PTA_stdfloat::empty_array(node_anim->mNumScalingKeys);
+    for (size_t i = 0; i < node_anim->mNumScalingKeys; ++i) {
+      tablei[i] = node_anim->mScalingKeys[i].mValue.x;
+      tablej[i] = node_anim->mScalingKeys[i].mValue.y;
+      tablek[i] = node_anim->mScalingKeys[i].mValue.z;
+    }
+    group->set_table('i', tablei);
+    group->set_table('j', tablej);
+    group->set_table('k', tablek);
+  }
+  else {
+    assimp_cat.debug()
+      << "No channel found for node: " << node.mName.C_Str() << "\n";
+  }
+
+
+  for (size_t i = 0; i < node.mNumChildren; ++i) {
+    if (_bonemap.find(node.mChildren[i]->mName.C_Str()) != _bonemap.end()) {
+        create_anim_channel(anim, bundle, group, *node.mChildren[i]);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AssimpLoader::load_mesh
 //       Access: Private
@@ -508,6 +593,60 @@ load_mesh(size_t index) {
   PT(GeomVertexArrayFormat) tb_aformat = new GeomVertexArrayFormat;
   tb_aformat->add_column(InternalName::make("transform_blend"), 1, Geom::NT_uint16, Geom::C_index);
 
+  // Check to see if we need to convert any animations
+  for (size_t i = 0; i < _scene->mNumAnimations; ++i) {
+    aiAnimation &ai_anim = *_scene->mAnimations[i];
+    bool convert_anim = false;
+
+    assimp_cat.debug()
+      << "Checking to see if anim (" << ai_anim.mName.C_Str() << ") matches character (" << mesh.mName.C_Str() << ")\n";
+    for (size_t j = 0; j < ai_anim.mNumChannels; ++j) {
+      assimp_cat.debug()
+        << "Searching for " << ai_anim.mChannels[j]->mNodeName.C_Str() << " in bone map" << "\n";
+      if (_bonemap.find(ai_anim.mChannels[j]->mNodeName.C_Str()) != _bonemap.end()) {
+        convert_anim = true;
+        break;
+      }
+    }
+
+    if (convert_anim) {
+      assimp_cat.debug()
+        << "Found animation (" << ai_anim.mName.C_Str() << ") for character (" << mesh.mName.C_Str() << ")\n";
+      // Find the root bone node
+      const aiNode *root = _bonemap[mesh.mBones[0]->mName.C_Str()];
+      while (root->mParent && _bonemap.find(root->mParent->mName.C_Str()) != _bonemap.end()) {
+        root = root->mParent;
+      }
+
+      // Now create the animation
+      unsigned int frames = 0;
+      for (size_t j = 0; j < ai_anim.mNumChannels; ++j) {
+        if (ai_anim.mChannels[j]->mNumPositionKeys > frames) {
+          frames = ai_anim.mChannels[j]->mNumPositionKeys;
+        }
+        if (ai_anim.mChannels[j]->mNumRotationKeys > frames) {
+          frames = ai_anim.mChannels[j]->mNumRotationKeys;
+        }
+        if (ai_anim.mChannels[j]->mNumScalingKeys > frames) {
+          frames = ai_anim.mChannels[j]->mNumScalingKeys;
+        }
+      }
+      PN_stdfloat fps = frames / (ai_anim.mTicksPerSecond * ai_anim.mDuration);
+      assimp_cat.debug()
+        << "FPS " << fps << "\n";
+      assimp_cat.debug()
+        << "Frames " << frames << "\n";
+
+      PT(AnimBundle) bundle = new AnimBundle(mesh.mName.C_Str(), fps, frames);
+      PT(AnimGroup) skeleton = new AnimGroup(bundle, "<skeleton>");
+      create_anim_channel(ai_anim, bundle, skeleton, *root);
+
+      // Attach the animation to the character node
+      PT(AnimBundleNode) bundle_node = new AnimBundleNode("anim", bundle);
+      character->add_child(bundle_node);
+    }
+  }
+
   //TODO: if there is only one UV set, hackily iterate over the texture stages and clear the texcoord name things
 
   PT(GeomVertexFormat) format = new GeomVertexFormat;

+ 3 - 0
pandatool/src/assimp/assimpLoader.h

@@ -27,6 +27,8 @@
 class Character;
 class CharacterJointBundle;
 class PartGroup;
+class AnimBundle;
+class AnimGroup;
 
 struct char_cmp {
   bool operator () (const char *a, const char *b) const {
@@ -76,6 +78,7 @@ private:
   void load_texture_stage(const aiMaterial &mat, const aiTextureType &ttype, CPT(TextureAttrib) &tattr);
   void load_material(size_t index);
   void create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *parent, const aiNode &node);
+  void create_anim_channel(const aiAnimation &anim, AnimBundle *bundle, AnimGroup *parent, const aiNode &node);
   void load_mesh(size_t index);
   void load_node(const aiNode &node, PandaNode *parent);
   void load_light(const aiLight &light);