Assimp: Add support for skinned meshes (no animations yet).

This commit is contained in:
Mitchell Stokes 2015-10-05 20:42:16 -07:00
parent 7f3f546cdd
commit 43958e820c
2 changed files with 169 additions and 2 deletions

View File

@ -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(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, "<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);
}
}
}

View File

@ -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<const char *, const aiNode *, char_cmp> BoneMap;
typedef phash_map<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(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);