diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index a198a29121..258e160c77 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/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 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(PT(Character) character, PT(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, ""); + create_joint(character, bundle, skeleton, *root); + } + + // Create transform blend table + PT(TransformBlendTable) tbtable = new TransformBlendTable; + pvector 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); + } } } diff --git a/pandatool/src/assimp/assimpLoader.h b/pandatool/src/assimp/assimpLoader.h index 3c8391b324..851ccc3a24 100644 --- a/pandatool/src/assimp/assimpLoader.h +++ b/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 phash_map BoneMap; +typedef phash_map 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(PT(Character) character, PT(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);