diff --git a/pandatool/src/assimp/assimpLoader.cxx b/pandatool/src/assimp/assimpLoader.cxx index f0322dc506..ceea1b8a0d 100644 --- a/pandatool/src/assimp/assimpLoader.cxx +++ b/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, ""); + 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; diff --git a/pandatool/src/assimp/assimpLoader.h b/pandatool/src/assimp/assimpLoader.h index f9544d2ed5..ed390791db 100644 --- a/pandatool/src/assimp/assimpLoader.h +++ b/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);