Browse Source

Merge pull request #52 from Moguri/assimp

Assimp: Add support for skinned meshes (no animations yet).
rdb 10 years ago
parent
commit
22036af871
2 changed files with 169 additions and 2 deletions
  1. 151 2
      pandatool/src/assimp/assimpLoader.cxx
  2. 18 0
      pandatool/src/assimp/assimpLoader.h

+ 151 - 2
pandatool/src/assimp/assimpLoader.cxx

@@ -31,12 +31,24 @@
 #include "pointLight.h"
 #include "look_at.h"
 #include "texturePool.h"
+#include "character.h"
+#include "pvector.h"
 
 #include "pandaIOSystem.h"
 #include "pandaLogger.h"
 
 #include "assimp/postprocess.h"
 
+struct BoneWeight {
+  CPT(JointVertexTransform) joint_vertex_xform;
+  float weight;
+
+  BoneWeight(CPT(JointVertexTransform) joint_vertex_xform, float weight)
+    : joint_vertex_xform(joint_vertex_xform), weight(weight)
+  {}
+};
+typedef pvector<BoneWeight> BoneWeightList;
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AssimpLoader::Constructor
 //       Access: Public
@@ -156,6 +168,29 @@ build_graph() {
   delete[] _geom_matindices;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AssimpLoader::find_ndoe
+//       Access: Private
+//  Description: Finds a node by name.
+////////////////////////////////////////////////////////////////////
+const aiNode *AssimpLoader::
+find_node(const aiNode &root, const aiString &name) {
+  const aiNode *node;
+
+  if (root.mName == name) {
+    return &root;
+  } else {
+    for (size_t i = 0; i < root.mNumChildren; ++i) {
+      node = find_node(*root.mChildren[i], name);
+      if (node) {
+          return node;
+      }
+    }
+  }
+
+  return NULL;
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AssimpLoader::load_texture
 //       Access: Private
@@ -377,6 +412,28 @@ load_material(size_t index) {
   _mat_states[index] = state;
 }
 
+////////////////////////////////////////////////////////////////////
+//     Function: AssimpLoader::create_joint
+//       Access: Private
+//  Description: Creates a CharacterJoint from an aiNode
+////////////////////////////////////////////////////////////////////
+void AssimpLoader::
+create_joint(Character *character, CharacterJointBundle *bundle, PartGroup *parent, const aiNode &node)
+{
+  const aiMatrix4x4 &t = node.mTransformation;
+  LMatrix4 mat(t.a1, t.b1, t.c1, t.d1,
+                t.a2, t.b2, t.c2, t.d2,
+                t.a3, t.b3, t.c3, t.d3,
+                t.a4, t.b4, t.c4, t.d4);
+  PT(CharacterJoint) joint = new CharacterJoint(character, bundle, parent, node.mName.C_Str(), mat);
+
+  for (size_t i = 0; i < node.mNumChildren; ++i) {
+    if (_bonemap.find(node.mChildren[i]->mName.C_Str()) != _bonemap.end()) {
+      create_joint(character, bundle, joint, *node.mChildren[i]);
+    }
+  }
+}
+
 ////////////////////////////////////////////////////////////////////
 //     Function: AssimpLoader::load_mesh
 //       Access: Private
@@ -386,6 +443,48 @@ void AssimpLoader::
 load_mesh(size_t index) {
   const aiMesh &mesh = *_scene->mMeshes[index];
 
+  // Check if we need to make a Character
+  PT(Character) character = NULL;
+  if (mesh.HasBones()) {
+    assimp_cat.debug()
+      << "Creating character for " << mesh.mName.C_Str() << "\n";
+
+    // Find and add all bone nodes to the bone map
+    for (size_t i = 0; i < mesh.mNumBones; ++i) {
+      const aiBone &bone = *mesh.mBones[i];
+      const aiNode *node = find_node(*_scene->mRootNode, bone.mName);
+      _bonemap[bone.mName.C_Str()] = node;
+    }
+
+    // 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 a character from the bones
+    character = new Character(mesh.mName.C_Str());
+    PT(CharacterJointBundle) bundle = character->get_bundle(0);
+    PT(PartGroup) skeleton = new PartGroup(bundle, "<skeleton>");
+    create_joint(character, bundle, skeleton, *root);
+  }
+
+  // Create transform blend table
+  PT(TransformBlendTable) tbtable = new TransformBlendTable;
+  pvector<BoneWeightList> bone_weights(mesh.mNumVertices);
+  if (character) {
+    for (size_t i = 0; i < mesh.mNumBones; ++i) {
+      const aiBone &bone = *mesh.mBones[i];
+      CPT(JointVertexTransform) jvt = new JointVertexTransform(character->find_joint(bone.mName.C_Str()));
+
+      for (size_t j = 0; j < bone.mNumWeights; ++j) {
+          const aiVertexWeight &weight = bone.mWeights[j];
+
+          bone_weights[weight.mVertexId].push_back(BoneWeight(jvt, weight.mWeight));
+      }
+    }
+  }
+
   // Create the vertex format.
   PT(GeomVertexArrayFormat) aformat = new GeomVertexArrayFormat;
   aformat->add_column(InternalName::get_vertex(), 3, Geom::NT_stdfloat, Geom::C_point);
@@ -405,14 +504,28 @@ load_mesh(size_t index) {
       aformat->add_column(InternalName::get_texcoord_name(out.str()), 3, Geom::NT_stdfloat, Geom::C_texcoord);
     }
   }
+
+  PT(GeomVertexArrayFormat) tb_aformat = new GeomVertexArrayFormat;
+  tb_aformat->add_column(InternalName::make("transform_blend"), 1, Geom::NT_uint16, Geom::C_index);
+
   //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;
   format->add_array(aformat);
+  if (character) {
+    format->add_array(tb_aformat);
+
+    GeomVertexAnimationSpec aspec;
+    aspec.set_panda();
+    format->set_animation(aspec);
+  }
 
   // Create the GeomVertexData.
   string name (mesh.mName.data, mesh.mName.length);
   PT(GeomVertexData) vdata = new GeomVertexData(name, GeomVertexFormat::register_format(format), Geom::UH_static);
+  if (character) {
+    vdata->set_transform_blend_table(tbtable);
+  }
   vdata->unclean_set_num_rows(mesh.mNumVertices);
 
   // Read out the vertices.
@@ -459,6 +572,22 @@ load_mesh(size_t index) {
     }
   }
 
+  // Now the transform blend table
+  if (character) {
+    GeomVertexWriter transform_blend (vdata, InternalName::get_transform_blend());
+
+    for (size_t i = 0; i < mesh.mNumVertices; ++i) {
+      TransformBlend tblend;
+
+      for (size_t j = 0; j < bone_weights[i].size(); ++j) {
+        tblend.add_transform(bone_weights[i][j].joint_vertex_xform, bone_weights[i][j].weight);
+      }
+      transform_blend.add_data1i(tbtable->add_blend(tblend));
+    }
+
+    tbtable->set_rows(SparseArray::lower_on(vdata->get_num_rows()));
+  }
+
   // Now read out the primitives.
   // Keep in mind that we called ReadFile with the aiProcess_Triangulate
   // flag earlier, so we don't have to worry about polygons.
@@ -501,6 +630,10 @@ load_mesh(size_t index) {
 
   _geoms[index] = geom;
   _geom_matindices[index] = mesh.mMaterialIndex;
+
+  if (character) {
+    _charmap[mesh.mName.C_Str()] = character;
+  }
 }
 
 ////////////////////////////////////////////////////////////////////
@@ -511,6 +644,12 @@ load_mesh(size_t index) {
 void AssimpLoader::
 load_node(const aiNode &node, PandaNode *parent) {
   PT(PandaNode) pnode;
+  PT(Character) character;
+
+  // Skip nodes we've converted to joints
+  if (_bonemap.find(node.mName.C_Str()) != _bonemap.end()) {
+      return;
+  }
 
   // Create the node and give it a name.
   string name (node.mName.data, node.mName.length);
@@ -519,7 +658,13 @@ load_node(const aiNode &node, PandaNode *parent) {
   } else {
     pnode = new PandaNode(name);
   }
-  parent->add_child(pnode);
+
+  if (_charmap.find(node.mName.C_Str()) != _charmap.end()) {
+    character = _charmap[node.mName.C_Str()];
+    parent->add_child(character);
+  } else {
+    parent->add_child(pnode);
+  }
 
   // Load in the transformation matrix.
   const aiMatrix4x4 &t = node.mTransformation;
@@ -545,7 +690,6 @@ load_node(const aiNode &node, PandaNode *parent) {
       meshIndex = node.mMeshes[0];
       gnode->add_geom(_geoms[meshIndex]);
       gnode->set_state(_mat_states[_geom_matindices[meshIndex]]);
-
     } else {
       for (size_t i = 0; i < node.mNumMeshes; ++i) {
         meshIndex = node.mMeshes[i];
@@ -553,6 +697,11 @@ load_node(const aiNode &node, PandaNode *parent) {
           _mat_states[_geom_matindices[meshIndex]]);
       }
     }
+
+    if (character) {
+        assimp_cat.debug() << "Adding char to geom\n";
+      character->add_child(gnode);
+    }
   }
 }
 

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

@@ -19,10 +19,23 @@
 #include "filename.h"
 #include "modelRoot.h"
 #include "texture.h"
+#include "pmap.h"
 
 #include "assimp/scene.h"
 #include "assimp/Importer.hpp"
 
+class Character;
+class CharacterJointBundle;
+class PartGroup;
+
+struct char_cmp {
+  bool operator () (const char *a, const char *b) const {
+    return strcmp(a,b) < 0;
+  }
+};
+typedef pmap<const char *, const aiNode *, char_cmp> BoneMap;
+typedef pmap<const char *, PT(Character), char_cmp> CharacterMap;
+
 ////////////////////////////////////////////////////////////////////
 //       Class : AssimpLoader
 // Description : Class that interfaces with Assimp and builds Panda
@@ -54,10 +67,15 @@ private:
   CPT(RenderState) *_mat_states;
   PT(Geom) *_geoms;
   unsigned int *_geom_matindices;
+  BoneMap _bonemap;
+  CharacterMap _charmap;
+
+  const aiNode *find_node(const aiNode &root, const aiString &name);
 
   void load_texture(size_t index);
   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 load_mesh(size_t index);
   void load_node(const aiNode &node, PandaNode *parent);
   void load_light(const aiLight &light);