diff --git a/pandatool/src/xfile/Sources.pp b/pandatool/src/xfile/Sources.pp index 3473ef5d80..84b16e37de 100644 --- a/pandatool/src/xfile/Sources.pp +++ b/pandatool/src/xfile/Sources.pp @@ -20,12 +20,16 @@ #define COMBINED_SOURCES $[TARGET]_composite1.cxx #define SOURCES \ - config_xfile.h xFileFace.h xFileMaker.h xFileMaterial.h \ + config_xfile.h \ + xFileAnimationSet.h \ + xFileFace.h xFileMaker.h xFileMaterial.h \ xFileMesh.h xFileNormal.h xFileTemplates.h \ xFileToEggConverter.h xFileVertex.h #define INCLUDED_SOURCES \ - config_xfile.cxx xFileFace.cxx xFileMaker.cxx xFileMaterial.cxx \ + config_xfile.cxx \ + xFileAnimationSet.cxx \ + xFileFace.cxx xFileMaker.cxx xFileMaterial.cxx \ xFileMesh.cxx xFileNormal.cxx xFileTemplates.cxx \ xFileToEggConverter.cxx xFileVertex.cxx diff --git a/pandatool/src/xfile/xFileAnimationSet.cxx b/pandatool/src/xfile/xFileAnimationSet.cxx new file mode 100644 index 0000000000..3af072574d --- /dev/null +++ b/pandatool/src/xfile/xFileAnimationSet.cxx @@ -0,0 +1,167 @@ +// Filename: xFileAnimationSet.cxx +// Created by: drose (02Oct04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#include "xFileAnimationSet.h" +#include "xFileToEggConverter.h" + +#include "eggGroup.h" +#include "eggTable.h" +#include "eggData.h" +#include "eggXfmSAnim.h" +#include "dcast.h" + +//////////////////////////////////////////////////////////////////// +// Function: XFileAnimationSet::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +XFileAnimationSet:: +XFileAnimationSet() { +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileAnimationSet::Destructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +XFileAnimationSet:: +~XFileAnimationSet() { +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileAnimationSet::create_hierarchy +// Access: Public +// Description: Sets up the hierarchy of EggTables corresponding to +// this AnimationSet. +//////////////////////////////////////////////////////////////////// +bool XFileAnimationSet:: +create_hierarchy(XFileToEggConverter *converter) { + // Egg animation tables start off with one Table entry, enclosing a + // Bundle entry. + EggTable *table = new EggTable(get_name()); + converter->get_egg_data().add_child(table); + EggTable *bundle = new EggTable(converter->_char_name); + table->add_child(bundle); + bundle->set_table_type(EggTable::TT_bundle); + + // Then the Bundle contains a "" entry, which begins the + // animation table hierarchy. + EggTable *skeleton = new EggTable(""); + bundle->add_child(skeleton); + + // Fill in the rest of the hierarchy with empty tables. + mirror_table(converter->get_dart_node(), skeleton); + + // Now populate those empty tables with the frame data. + JointData::const_iterator ji; + for (ji = _joint_data.begin(); ji != _joint_data.end(); ++ji) { + const string &joint_name = (*ji).first; + const FrameData &table = (*ji).second; + + EggXfmSAnim *anim_table = get_table(joint_name); + if (anim_table == (EggXfmSAnim *)NULL) { + xfile_cat.warning() + << "Frame " << joint_name << ", named by animation data, not defined.\n"; + } else { + // If we have animation data, apply it. + FrameData::const_iterator fi; + for (fi = table.begin(); fi != table.end(); ++fi) { + anim_table->add_data(*fi); + } + anim_table->optimize(); + } + } + + // Put some data in the empty tables also. + Tables::iterator ti; + for (ti = _tables.begin(); ti != _tables.end(); ++ti) { + const string &joint_name = (*ti).first; + EggXfmSAnim *anim_table = (*ti).second._table; + EggGroup *joint = (*ti).second._joint; + if (anim_table->empty() && joint != (EggGroup *)NULL) { + // If there's no animation data, assign the rest transform. + anim_table->add_data(joint->get_transform()); + } + anim_table->optimize(); + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileAnimationSet::get_table +// Access: Public +// Description: Returns the table associated with the indicated joint +// name. +//////////////////////////////////////////////////////////////////// +EggXfmSAnim *XFileAnimationSet:: +get_table(const string &joint_name) const { + Tables::const_iterator ti; + ti = _tables.find(joint_name); + if (ti != _tables.end()) { + return (*ti).second._table; + } + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileAnimationSet::create_frame_data +// Access: Public +// Description: Returns a reference to a new FrameData table +// corresponding to the indicated joint. +//////////////////////////////////////////////////////////////////// +XFileAnimationSet::FrameData &XFileAnimationSet:: +create_frame_data(const string &joint_name) { + return _joint_data[joint_name]; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileAnimationSet::mirror_table +// Access: Private +// Description: Builds up a new set of EggTable nodes, as a +// mirror of the existing set of EggGroup (joint) +// nodes, and saves each new table in the _tables +// record. +//////////////////////////////////////////////////////////////////// +void XFileAnimationSet:: +mirror_table(EggGroup *model_node, EggTable *anim_node) { + EggGroupNode::iterator gi; + for (gi = model_node->begin(); gi != model_node->end(); ++gi) { + EggNode *child = (*gi); + if (child->is_of_type(EggGroup::get_class_type())) { + EggGroup *group = DCAST(EggGroup, child); + if (group->get_group_type() == EggGroup::GT_joint) { + // When we come to a , create a new Table for it. + EggTable *new_table = new EggTable(group->get_name()); + anim_node->add_child(new_table); + EggXfmSAnim *xform = new EggXfmSAnim("xform"); + new_table->add_child(xform); + TablePair &table_pair = _tables[group->get_name()]; + table_pair._table = xform; + table_pair._joint = group; + + // Now recurse. + mirror_table(group, new_table); + + } else { + // If we come to an ordinary , skip past it. + mirror_table(group, anim_node); + } + } + } +} diff --git a/pandatool/src/xfile/xFileAnimationSet.h b/pandatool/src/xfile/xFileAnimationSet.h new file mode 100644 index 0000000000..caf58d4615 --- /dev/null +++ b/pandatool/src/xfile/xFileAnimationSet.h @@ -0,0 +1,69 @@ +// Filename: xFileAnimationSet.h +// Created by: drose (02Oct04) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001 - 2004, Disney Enterprises, Inc. All rights reserved +// +// All use of this software is subject to the terms of the Panda 3d +// Software license. You should have received a copy of this license +// along with this source code; you will also find a current copy of +// the license at http://etc.cmu.edu/panda3d/docs/license/ . +// +// To contact the maintainers of this program write to +// panda3d-general@lists.sourceforge.net . +// +//////////////////////////////////////////////////////////////////// + +#ifndef XFILEANIMATIONSET_H +#define XFILEANIMATIONSET_H + +#include "pandatoolbase.h" +#include "pmap.h" +#include "pvector.h" +#include "luse.h" +#include "namable.h" + +class XFileToEggConverter; +class EggGroup; +class EggTable; +class EggXfmSAnim; + +//////////////////////////////////////////////////////////////////// +// Class : XFileAnimationSet +// Description : This represents a tree of EggTables, corresponding to +// Animation entries in the X file. There is one +// EggTable for each joint in the character's joint +// set, and the whole tree is structured as a +// mirror of the joint set. +//////////////////////////////////////////////////////////////////// +class XFileAnimationSet : public Namable { +public: + XFileAnimationSet(); + ~XFileAnimationSet(); + + bool create_hierarchy(XFileToEggConverter *converter); + EggXfmSAnim *get_table(const string &joint_name) const; + + typedef pvector FrameData; + FrameData &create_frame_data(const string &joint_name); + +private: + void mirror_table(EggGroup *model_node, EggTable *anim_node); + + typedef pmap JointData; + JointData _joint_data; + + class TablePair { + public: + EggGroup *_joint; + EggXfmSAnim *_table; + }; + + typedef pmap Tables; + Tables _tables; +}; + +#endif + diff --git a/pandatool/src/xfile/xFileMesh.cxx b/pandatool/src/xfile/xFileMesh.cxx index 42c489ab2a..b0f3bac73a 100644 --- a/pandatool/src/xfile/xFileMesh.cxx +++ b/pandatool/src/xfile/xFileMesh.cxx @@ -21,6 +21,7 @@ #include "xFileVertex.h" #include "xFileNormal.h" #include "xFileMaterial.h" +#include "config_xfile.h" #include "eggVertexPool.h" #include "eggVertex.h" @@ -38,6 +39,7 @@ XFileMesh(CoordinateSystem cs) : _cs(cs) { _has_colors = false; _has_uvs = false; _has_materials = false; + _egg_parent = NULL; } //////////////////////////////////////////////////////////////////// @@ -251,30 +253,41 @@ add_material(XFileMaterial *material) { return next_index; } +//////////////////////////////////////////////////////////////////// +// Function: XFileMesh::set_egg_parent +// Access: Public +// Description: Specifies the egg node that will eventually be the +// parent of this mesh, when create_polygons() is later +// called. +//////////////////////////////////////////////////////////////////// +void XFileMesh:: +set_egg_parent(EggGroupNode *egg_parent) { + // We actually put the mesh under its own group. + EggGroup *egg_group = new EggGroup(get_name()); + egg_parent->add_child(egg_group); + + _egg_parent = egg_group; +} + //////////////////////////////////////////////////////////////////// // Function: XFileMesh::create_polygons // Access: Public // Description: Creates a slew of EggPolygons according to the faces -// in the mesh, and adds them to the indicated parent -// node. +// in the mesh, and adds them to the +// previously-indicated parent node. //////////////////////////////////////////////////////////////////// bool XFileMesh:: -create_polygons(EggGroupNode *egg_parent, XFileToEggConverter *converter) { - if (has_name()) { - // Put a named mesh within its own group. - EggGroup *egg_group = new EggGroup(get_name()); - egg_parent->add_child(egg_group); - egg_parent = egg_group; - } +create_polygons(XFileToEggConverter *converter) { + nassertr(_egg_parent != (EggGroupNode *)NULL, false); EggVertexPool *vpool = new EggVertexPool(get_name()); - egg_parent->add_child(vpool); + _egg_parent->add_child(vpool); Faces::const_iterator fi; for (fi = _faces.begin(); fi != _faces.end(); ++fi) { XFileFace *face = (*fi); EggPolygon *egg_poly = new EggPolygon; - egg_parent->add_child(egg_poly); + _egg_parent->add_child(egg_poly); // Set up the vertices for the polygon. XFileFace::Vertices::reverse_iterator vi; @@ -282,7 +295,8 @@ create_polygons(EggGroupNode *egg_parent, XFileToEggConverter *converter) { int vertex_index = (*vi)._vertex_index; int normal_index = (*vi)._normal_index; if (vertex_index < 0 || vertex_index >= (int)_vertices.size()) { - nout << "Vertex index out of range in Mesh.\n"; + xfile_cat.error() + << "Vertex index out of range in Mesh.\n"; return false; } XFileVertex *vertex = _vertices[vertex_index]; @@ -294,6 +308,7 @@ create_polygons(EggGroupNode *egg_parent, XFileToEggConverter *converter) { // Create a temporary EggVertex before adding it to the pool. EggVertex temp_vtx; + temp_vtx.set_external_index(vertex_index); temp_vtx.set_pos(LCAST(double, vertex->_point)); if (vertex->_has_color) { temp_vtx.set_color(vertex->_color); @@ -311,7 +326,7 @@ create_polygons(EggGroupNode *egg_parent, XFileToEggConverter *converter) { // Transform the vertex into the appropriate (global) coordinate // space. - temp_vtx.transform(egg_parent->get_node_to_vertex()); + temp_vtx.transform(_egg_parent->get_node_to_vertex()); // Now get a real EggVertex matching our template. EggVertex *egg_vtx = vpool->create_unique_vertex(temp_vtx); @@ -326,11 +341,32 @@ create_polygons(EggGroupNode *egg_parent, XFileToEggConverter *converter) { } } + // Now go through all of the vertices and skin them up. + EggVertexPool::iterator vi; + for (vi = vpool->begin(); vi != vpool->end(); ++vi) { + EggVertex *egg_vtx = (*vi); + int vertex_index = egg_vtx->get_external_index(); + + SkinWeights::const_iterator swi; + for (swi = _skin_weights.begin(); swi != _skin_weights.end(); ++swi) { + const SkinWeightsData &data = (*swi); + WeightMap::const_iterator wmi = data._weight_map.find(vertex_index); + if (wmi != data._weight_map.end()) { + EggGroup *joint = converter->find_joint(data._joint_name, + data._matrix_offset); + if (joint != (EggGroup *)NULL) { + double weight = (*wmi).second; + joint->ref_vertex(egg_vtx, weight); + } + } + } + } + if (!has_normals()) { // If we don't have explicit normals, make some up, per the DX // spec. Since the DX spec doesn't mention anything about a // crease angle, we should be as generous as possible. - egg_parent->recompute_vertex_normals(180.0, _cs); + _egg_parent->recompute_vertex_normals(180.0, _cs); } return true; @@ -574,7 +610,8 @@ read_mesh_data(const Datagram &raw_data) { } if (di.get_remaining_size() != 0) { - nout << "Ignoring " << di.get_remaining_size() << " trailing Mesh.\n"; + xfile_cat.warning() + << "Ignoring " << di.get_remaining_size() << " trailing Mesh.\n"; } return true; @@ -604,7 +641,8 @@ read_normal_data(const Datagram &raw_data) { int num_faces = di.get_int32(); if (num_faces != _faces.size()) { - nout << "Incorrect number of faces in MeshNormals.\n"; + xfile_cat.error() + << "Incorrect number of faces in MeshNormals.\n"; return false; } @@ -612,7 +650,8 @@ read_normal_data(const Datagram &raw_data) { XFileFace *face = _faces[i]; int num_vertices = di.get_int32(); if (num_vertices != face->_vertices.size()) { - nout << "Incorrect number of vertices for face in MeshNormals.\n"; + xfile_cat.error() + << "Incorrect number of vertices for face in MeshNormals.\n"; return false; } for (int j = 0; j < num_vertices; j++) { @@ -621,8 +660,9 @@ read_normal_data(const Datagram &raw_data) { } if (di.get_remaining_size() != 0) { - nout << "Ignoring " << di.get_remaining_size() - << " trailing MeshNormals.\n"; + xfile_cat.warning() + << "Ignoring " << di.get_remaining_size() + << " trailing MeshNormals.\n"; } return true; @@ -643,7 +683,8 @@ read_color_data(const Datagram &raw_data) { for (i = 0; i < num_colors; i++) { unsigned int vertex_index = di.get_int32(); if (vertex_index < 0 || vertex_index >= _vertices.size()) { - nout << "Vertex index out of range in MeshVertexColors.\n"; + xfile_cat.error() + << "Vertex index out of range in MeshVertexColors.\n"; return false; } XFileVertex *vertex = _vertices[vertex_index]; @@ -655,8 +696,9 @@ read_color_data(const Datagram &raw_data) { } if (di.get_remaining_size() != 0) { - nout << "Ignoring " << di.get_remaining_size() - << " trailing MeshVertexColors.\n"; + xfile_cat.warning() + << "Ignoring " << di.get_remaining_size() + << " trailing MeshVertexColors.\n"; } return true; @@ -674,7 +716,8 @@ read_uv_data(const Datagram &raw_data) { int num_vertices = di.get_int32(); if (num_vertices != _vertices.size()) { - nout << "Wrong number of vertices in MeshTextureCoords.\n"; + xfile_cat.error() + << "Wrong number of vertices in MeshTextureCoords.\n"; return false; } @@ -687,13 +730,62 @@ read_uv_data(const Datagram &raw_data) { } if (di.get_remaining_size() != 0) { - nout << "Ignoring " << di.get_remaining_size() - << " trailing MeshTextureCoords.\n"; + xfile_cat.warning() + << "Ignoring " << di.get_remaining_size() + << " trailing MeshTextureCoords.\n"; } return true; } +//////////////////////////////////////////////////////////////////// +// Function: XFileMesh::read_skin_weights_data +// Access: Public +// Description: Fills the structure based on the raw data from the +// SkinWeights template. +//////////////////////////////////////////////////////////////////// +bool XFileMesh:: +read_skin_weights_data(const Datagram &raw_data) { + DatagramIterator di(raw_data); + + // Create a new SkinWeightsData record for the table. We'll need + // this data later when we create the vertices. + _skin_weights.push_back(SkinWeightsData()); + SkinWeightsData &data = _skin_weights.back(); + + // The DX system encodes a pointer to a character string in four + // bytes within the stream. Weird, in a Microsofty sort of way. + data._joint_name = (const char *)di.get_uint32(); + + int num_weights = di.get_int32(); + + vector_int vindices; + vindices.reserve(num_weights); + + // Unpack the list of vertices first + int i; + for (i = 0; i < num_weights; i++) { + int vindex = di.get_int32(); + if (vindex < 0 || vindex > (int)_vertices.size()) { + xfile_cat.error() + << "Illegal vertex index " << vindex << " in SkinWeights.\n"; + return false; + } + vindices.push_back(vindex); + } + + // Then unpack the weight for each vertex. + for (i = 0; i < num_weights; i++) { + float weight = di.get_float32(); + data._weight_map[vindices[i]] = weight; + } + + // Finally, read the matrix offset. + data._matrix_offset.read_datagram(di); + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: XFileMesh::read_material_list_data // Access: Public @@ -708,7 +800,8 @@ read_material_list_data(const Datagram &raw_data) { unsigned int num_faces = di.get_int32(); if (num_faces > _faces.size()) { - nout << "Too many faces in MaterialList.\n"; + xfile_cat.error() + << "Too many faces in MaterialList.\n"; return false; } @@ -730,8 +823,9 @@ read_material_list_data(const Datagram &raw_data) { } if (di.get_remaining_size() != 0) { - nout << "Ignoring " << di.get_remaining_size() - << " trailing MeshMaterialList.\n"; + xfile_cat.warning() + << "Ignoring " << di.get_remaining_size() + << " trailing MeshMaterialList.\n"; } return true; diff --git a/pandatool/src/xfile/xFileMesh.h b/pandatool/src/xfile/xFileMesh.h index 46b3487156..92a4d9522a 100644 --- a/pandatool/src/xfile/xFileMesh.h +++ b/pandatool/src/xfile/xFileMesh.h @@ -58,8 +58,9 @@ public: int add_normal(XFileNormal *normal); int add_material(XFileMaterial *material); - bool create_polygons(EggGroupNode *egg_parent, - XFileToEggConverter *converter); + void set_egg_parent(EggGroupNode *egg_parent); + + bool create_polygons(XFileToEggConverter *converter); bool has_normals() const; bool has_colors() const; @@ -79,6 +80,7 @@ public: bool read_normal_data(const Datagram &raw_data); bool read_color_data(const Datagram &raw_data); bool read_uv_data(const Datagram &raw_data); + bool read_skin_weights_data(const Datagram &raw_data); bool read_material_list_data(const Datagram &raw_data); private: @@ -94,6 +96,17 @@ private: Materials _materials; Faces _faces; + typedef pmap WeightMap; + + class SkinWeightsData { + public: + string _joint_name; + WeightMap _weight_map; + LMatrix4f _matrix_offset; + }; + typedef pvector SkinWeights; + SkinWeights _skin_weights; + typedef pmap > UniqueVertices; typedef pmap > UniqueNormals; typedef pmap > UniqueMaterials; @@ -105,6 +118,8 @@ private: bool _has_colors; bool _has_uvs; bool _has_materials; + + EggGroupNode *_egg_parent; }; #endif diff --git a/pandatool/src/xfile/xFileToEggConverter.cxx b/pandatool/src/xfile/xFileToEggConverter.cxx index d72bf1f10a..5895d30756 100644 --- a/pandatool/src/xfile/xFileToEggConverter.cxx +++ b/pandatool/src/xfile/xFileToEggConverter.cxx @@ -20,15 +20,38 @@ #include "xFileMesh.h" #include "xFileMaterial.h" #include "xFileTemplates.h" +#include "xFileAnimationSet.h" #include "config_xfile.h" #include "eggData.h" #include "eggGroup.h" +#include "eggXfmSAnim.h" +#include "eggGroupUniquifier.h" #include "datagram.h" #include "eggMaterialCollection.h" #include "eggTextureCollection.h" #include "dcast.h" +#define MY_DEFINE_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \ + EXTERN_C const GUID DECLSPEC_SELECTANY name \ + = { l, w1, w2, { b1, b2, b3, b4, b5, b6, b7, b8 } } + +// These are defined in d3dx9mesh.h, which we may not have available +// (so far, Panda is only dependent on dx8 API's). +#ifndef DXFILEOBJ_XSkinMeshHeader +// {3CF169CE-FF7C-44ab-93C0-F78F62D172E2} +MY_DEFINE_GUID(DXFILEOBJ_XSkinMeshHeader, +0x3cf169ce, 0xff7c, 0x44ab, 0x93, 0xc0, 0xf7, 0x8f, 0x62, 0xd1, 0x72, 0xe2); +#endif + +#ifndef DXFILEOBJ_SkinWeights +// {6F0D123B-BAD2-4167-A0D0-80224F25FABB} +MY_DEFINE_GUID(DXFILEOBJ_SkinWeights, +0x6f0d123b, 0xbad2, 0x4167, 0xa0, 0xd0, 0x80, 0x22, 0x4f, 0x25, 0xfa, 0xbb); +#endif + + + //////////////////////////////////////////////////////////////////// // Function: XFileToEggConverter::Constructor // Access: Public @@ -36,8 +59,10 @@ //////////////////////////////////////////////////////////////////// XFileToEggConverter:: XFileToEggConverter() { + _make_char = false; _dx_file = NULL; _dx_file_enum = NULL; + _dart_node = NULL; } //////////////////////////////////////////////////////////////////// @@ -47,10 +72,12 @@ XFileToEggConverter() { //////////////////////////////////////////////////////////////////// XFileToEggConverter:: XFileToEggConverter(const XFileToEggConverter ©) : - SomethingToEggConverter(copy) + SomethingToEggConverter(copy), + _make_char(copy._make_char) { _dx_file = NULL; _dx_file_enum = NULL; + _dart_node = NULL; } //////////////////////////////////////////////////////////////////// @@ -138,7 +165,25 @@ convert_file(const Filename &filename) { _egg_data->set_coordinate_system(CS_yup_left); } - return get_toplevel(); + if (!get_toplevel()) { + return false; + } + + if (!create_polygons()) { + return false; + } + + if (_make_char) { + // Now make sure that each joint has a unique name. + EggGroupUniquifier uniquifier; + uniquifier.uniquify(_dart_node); + } + + if (!create_hierarchy()) { + return false; + } + + return true; } //////////////////////////////////////////////////////////////////// @@ -157,6 +202,37 @@ close() { _dx_file->Release(); _dx_file = NULL; } + + // Clean up all the other stuff. + Meshes::const_iterator mi; + for (mi = _meshes.begin(); mi != _meshes.end(); ++mi) { + delete (*mi); + } + _meshes.clear(); + + for (mi = _toplevel_meshes.begin(); mi != _toplevel_meshes.end(); ++mi) { + delete (*mi); + } + _toplevel_meshes.clear(); + + AnimationSets::const_iterator asi; + for (asi = _animation_sets.begin(); asi != _animation_sets.end(); ++asi) { + delete (*asi); + } + _animation_sets.clear(); + + _joints.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::get_dart_node +// Access: Public +// Description: Returns the root of the joint hierarchy, if +// _make_char is true, or NULL otherwise. +//////////////////////////////////////////////////////////////////// +EggGroup *XFileToEggConverter:: +get_dart_node() const { + return _dart_node; } //////////////////////////////////////////////////////////////////// @@ -183,6 +259,98 @@ create_unique_material(const EggMaterial ©) { return _materials.create_unique_material(copy, ~EggMaterial::E_mref_name); } +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::find_joint (one parameter) +// Access: Public +// Description: This is called by set_animation_frame, for +// the purposes of building the frame data for the +// animation--it needs to know the original rest frame +// transform. +//////////////////////////////////////////////////////////////////// +EggGroup *XFileToEggConverter:: +find_joint(const string &joint_name) { + Joints::iterator ji; + ji = _joints.find(joint_name); + if (ji != _joints.end()) { + JointDef &joint_def = (*ji).second; + if (joint_def._node == (EggGroup *)NULL) { + // An invalid joint detected earlier. + return NULL; + } + + return joint_def._node; + } + + // Joint name is unknown. Issue a warning, then insert NULL into + // the table so we don't get the same warning again with the next + // polygon. + if (_make_char) { + xfile_cat.warning() + << "Joint name " << joint_name << " in animation data is undefined.\n"; + } + _joints[joint_name]._node = NULL; + + return NULL; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::find_joint (two parameters) +// Access: Public +// Description: This is called by XFileMesh::create_polygons(), for +// the purposes of applying skinning to vertices. It +// searches for the joint matching the indicated name, +// and returns it, possibly creating a new joint if the +// requested matrix_offset demands it. Returns NULL if +// the joint name is unknown. +//////////////////////////////////////////////////////////////////// +EggGroup *XFileToEggConverter:: +find_joint(const string &joint_name, const LMatrix4f &matrix_offset) { + return find_joint(joint_name); + /* + Joints::iterator ji; + ji = _joints.find(joint_name); + if (ji != _joints.end()) { + JointDef &joint_def = (*ji).second; + if (joint_def._node == (EggGroup *)NULL) { + // An invalid joint detected earlier. + return NULL; + } + + OffsetJoints::iterator oji = joint_def._offsets.find(matrix_offset); + if (oji != joint_def._offsets.end()) { + // We've previously created a joint for this matrix, so just + // reuse it. + return (*oji).second; + } + + if (!joint_def._offsets.empty()) { + const LMatrix4f &mat = (*joint_def._offsets.begin()).first; + } + + // We need to create a new joint for this matrix. + EggGroup *new_joint = new EggGroup("synth"); + joint_def._node->add_child(new_joint); + + new_joint->set_group_type(EggGroup::GT_joint); + new_joint->set_transform(LCAST(double, matrix_offset)); + joint_def._offsets[matrix_offset] = new_joint; + + return new_joint; + } + + // Joint name is unknown. Issue a warning, then insert NULL into + // the table so we don't get the same warning again with the next + // polygon. + if (_make_char) { + xfile_cat.warning() + << "Joint name " << joint_name << " in animation data is undefined.\n"; + } + _joints[joint_name]._node = NULL; + + return NULL; + */ +} + //////////////////////////////////////////////////////////////////// // Function: XFileToEggConverter::get_toplevel // Access: Private @@ -195,13 +363,22 @@ get_toplevel() { HRESULT hr; LPDIRECTXFILEDATA obj; - PT(EggGroup) egg_toplevel = new EggGroup; - bool any_frames = false; + EggGroupNode *egg_parent = _egg_data; + + // If we are converting an animatable model, make an extra node to + // represent the root of the hierarchy. + if (_make_char) { + _dart_node = new EggGroup(_char_name); + egg_parent->add_child(_dart_node); + _dart_node->set_dart_type(EggGroup::DT_default); + egg_parent = _dart_node; + } + + _any_frames = false; hr = _dx_file_enum->GetNextDataObject(&obj); while (hr == DXFILE_OK) { - if (!convert_toplevel_object(obj, _egg_data, - egg_toplevel, any_frames)) { + if (!convert_toplevel_object(obj, egg_parent)) { return false; } hr = _dx_file_enum->GetNextDataObject(&obj); @@ -213,13 +390,6 @@ get_toplevel() { return false; } - if (!any_frames) { - // If the file contained no frames at all, then all of the meshes - // that appeared at the toplevel were meant to be directly - // included. - _egg_data->steal_children(*egg_toplevel); - } - return true; } @@ -230,8 +400,7 @@ get_toplevel() { // any Frames, to the appropriate egg structures. //////////////////////////////////////////////////////////////////// bool XFileToEggConverter:: -convert_toplevel_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent, - EggGroupNode *egg_toplevel, bool &any_frames) { +convert_toplevel_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { HRESULT hr; // Determine what type of data object we have. @@ -251,7 +420,7 @@ convert_toplevel_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent, // referenced below. } else if (*type == TID_D3DRMFrame) { - any_frames = true; + _any_frames = true; if (!convert_frame(obj, egg_parent)) { return false; } @@ -261,12 +430,17 @@ convert_toplevel_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent, return false; } + } else if (*type == TID_D3DRMAnimationSet) { + if (!convert_animation_set(obj)) { + return false; + } + } else if (*type == TID_D3DRMMesh) { // Assume a Mesh at the toplevel is just present to define a // reference that will be included below. Convert it into the - // egg_toplevel group, where it will be ignored unless there are + // _toplevel_meshes set, where it will be ignored unless there are // no frames at all in the file. - if (!convert_mesh(obj, egg_toplevel)) { + if (!convert_mesh(obj, egg_parent, true)) { return false; } @@ -352,7 +526,7 @@ convert_data_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { } } else if (*type == TID_D3DRMMesh) { - if (!convert_mesh(obj, egg_parent)) { + if (!convert_mesh(obj, egg_parent, false)) { return false; } @@ -381,6 +555,24 @@ convert_frame(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { EggGroup *group = new EggGroup(name); egg_parent->add_child(group); + if (_make_char) { + group->set_group_type(EggGroup::GT_joint); + if (name.empty()) { + // Make up a name for this unnamed joint. + group->set_name("unnamed"); + + } else { + JointDef joint_def; + joint_def._node = group; + bool inserted = _joints.insert(Joints::value_type(name, joint_def)).second; + if (!inserted) { + xfile_cat.warning() + << "Nonunique Frame name " << name + << " encountered; animation will be ambiguous.\n"; + } + } + } + // Now walk through the children of the frame. LPDIRECTXFILEOBJECT child_obj; @@ -434,6 +626,398 @@ convert_transform(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation_set +// Access: Private +// Description: Begins an AnimationSet. This is the root of one +// particular animation (table of frames per joint) to +// be applied to the model within this file. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation_set(LPDIRECTXFILEDATA obj) { + HRESULT hr; + + XFileAnimationSet *animation_set = new XFileAnimationSet(); + animation_set->set_name(get_object_name(obj)); + + // Now walk through the children of the set; each one animates a + // different joint. + LPDIRECTXFILEOBJECT child_obj; + + hr = obj->GetNextObject(&child_obj); + while (hr == DXFILE_OK) { + if (!convert_animation_set_object(child_obj, *animation_set)) { + return false; + } + hr = obj->GetNextObject(&child_obj); + } + + if (hr != DXFILEERR_NOMOREOBJECTS) { + xfile_cat.error() + << "Error extracting children of AnimationSet " + << get_object_name(obj) << ".\n"; + delete animation_set; + return false; + } + + _animation_sets.push_back(animation_set); + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation_set_object +// Access: Private +// Description: Converts the indicated object, a child of a +// AnimationSet. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation_set_object(LPDIRECTXFILEOBJECT obj, + XFileAnimationSet &animation_set) { + HRESULT hr; + LPDIRECTXFILEDATA data_obj; + LPDIRECTXFILEDATAREFERENCE ref_obj; + + // See if the object is a data object. + hr = obj->QueryInterface(IID_IDirectXFileData, (void **)&data_obj); + if (hr == DD_OK) { + // It is. + return convert_animation_set_data_object(data_obj, animation_set); + } + + // Or maybe it's a reference to a previous object. + hr = obj->QueryInterface(IID_IDirectXFileDataReference, (void **)&ref_obj); + if (hr == DD_OK) { + // It is. + if (ref_obj->Resolve(&data_obj) == DXFILE_OK) { + return convert_animation_set_data_object(data_obj, animation_set); + } + } + + // It isn't. + if (xfile_cat.is_debug()) { + xfile_cat.debug() + << "Ignoring animation set object of unknown type: " + << get_object_name(obj) << "\n"; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation_set_data_object +// Access: Private +// Description: Converts the indicated data object, a child of a +// AnimationSet. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation_set_data_object(LPDIRECTXFILEDATA obj, XFileAnimationSet &animation_set) { + HRESULT hr; + + // Determine what type of data object we have. + const GUID *type; + hr = obj->GetType(&type); + if (hr != DXFILE_OK) { + xfile_cat.error() + << "Unable to get type of template\n"; + return false; + } + + if (*type == TID_D3DRMAnimation) { + if (!convert_animation(obj, animation_set)) { + return false; + } + } else { + if (xfile_cat.is_debug()) { + xfile_cat.debug() + << "Ignoring animation set data object of unknown type: " + << get_object_name(obj) << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation +// Access: Private +// Description: Converts the indicated Animation template object. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation(LPDIRECTXFILEDATA obj, XFileAnimationSet &animation_set) { + HRESULT hr; + + // Within an Animation template, we expect to find a reference to a + // frame, possibly an AnimationOptions object, and one or more + // AnimationKey objects. + LPDIRECTXFILEOBJECT child_obj; + + // First, walk through the list of children, to find the one that is + // the frame reference. We need to know this up front so we know + // which table we should be building up. + string frame_name; + bool got_frame_name = false; + + pvector children; + + hr = obj->GetNextObject(&child_obj); + while (hr == DXFILE_OK) { + LPDIRECTXFILEDATAREFERENCE ref_obj; + if (child_obj->QueryInterface(IID_IDirectXFileDataReference, (void **)&ref_obj) == DD_OK) { + // Here's a reference! + LPDIRECTXFILEDATA data_obj; + if (ref_obj->Resolve(&data_obj) == DXFILE_OK) { + const GUID *type; + if (data_obj->GetType(&type) == DXFILE_OK) { + if (*type == TID_D3DRMFrame) { + // Ok, this one is a reference to a frame. Save the name. + frame_name = get_object_name(data_obj); + got_frame_name = true; + } + } + } + } else { + children.push_back(child_obj); + } + + hr = obj->GetNextObject(&child_obj); + } + + if (hr != DXFILEERR_NOMOREOBJECTS) { + xfile_cat.error() + << "Error extracting children of Animation " + << get_object_name(obj) << ".\n"; + return false; + } + + if (!got_frame_name) { + xfile_cat.error() + << "Animation " << get_object_name(obj) + << " includes no reference to a frame.\n"; + return false; + } + + FrameData &table = animation_set.create_frame_data(frame_name); + + // Now go back again and get the actual data. + pvector::iterator ci; + for (ci = children.begin(); ci != children.end(); ++ci) { + if (!convert_animation_object((*ci), frame_name, table)) { + return false; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation_object +// Access: Private +// Description: Converts the indicated object, a child of a +// Animation. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation_object(LPDIRECTXFILEOBJECT obj, const string &joint_name, + XFileToEggConverter::FrameData &table) { + HRESULT hr; + LPDIRECTXFILEDATA data_obj; + LPDIRECTXFILEDATAREFERENCE ref_obj; + + // See if the object is a data object. + hr = obj->QueryInterface(IID_IDirectXFileData, (void **)&data_obj); + if (hr == DD_OK) { + // It is. + return convert_animation_data_object(data_obj, joint_name, table); + } + + // Or maybe it's a reference to a previous object. + hr = obj->QueryInterface(IID_IDirectXFileDataReference, (void **)&ref_obj); + if (hr == DD_OK) { + // It is. + if (ref_obj->Resolve(&data_obj) == DXFILE_OK) { + return convert_animation_data_object(data_obj, joint_name, table); + } + } + + // It isn't. + if (xfile_cat.is_debug()) { + xfile_cat.debug() + << "Ignoring animation set object of unknown type: " + << get_object_name(obj) << "\n"; + } + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation_data_object +// Access: Private +// Description: Converts the indicated data object, a child of a +// Animation. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation_data_object(LPDIRECTXFILEDATA obj, const string &joint_name, + XFileToEggConverter::FrameData &table) { + HRESULT hr; + + // Determine what type of data object we have. + const GUID *type; + hr = obj->GetType(&type); + if (hr != DXFILE_OK) { + xfile_cat.error() + << "Unable to get type of template\n"; + return false; + } + + if (*type == TID_D3DRMAnimationOptions) { + // Quietly ignore AnimationOptions. + + } else if (*type == TID_D3DRMAnimationKey) { + if (!convert_animation_key(obj, joint_name, table)) { + return false; + } + + } else { + if (xfile_cat.is_debug()) { + xfile_cat.debug() + << "Ignoring animation set data object of unknown type: " + << get_object_name(obj) << "\n"; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_animation_key +// Access: Private +// Description: Converts the indicated AnimationKey template object. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_animation_key(LPDIRECTXFILEDATA obj, const string &joint_name, + XFileToEggConverter::FrameData &table) { + Datagram raw_data; + if (!get_data(obj, raw_data)) { + return false; + } + + DatagramIterator di(raw_data); + int key_type = di.get_uint32(); + int nkeys = di.get_uint32(); + + int last_time = 0; + + for (int i = 0; i < nkeys; i++) { + int time = di.get_uint32(); + + int nvalues = di.get_uint32(); + pvector values; + values.reserve(nvalues); + for (int j = 0; j < nvalues; j++) { + float value = di.get_float32(); + values.push_back(value); + } + + while (last_time <= time) { + if (!set_animation_frame(joint_name, table, last_time, key_type, + &values[0], nvalues)) { + return false; + } + last_time++; + } + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::set_animation_frame +// Access: Private +// Description: Sets a single frame of the animation data. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +set_animation_frame(const string &joint_name, + XFileToEggConverter::FrameData &table, int frame, + int key_type, const float *values, int nvalues) { + LMatrix4d mat; + + // Pad out the table by duplicating the last row as necessary. + if ((int)table.size() <= frame) { + if (table.empty()) { + // Get the initial transform from the joint's rest transform. + EggGroup *joint = find_joint(joint_name); + if (joint != (EggGroup *)NULL) { + mat = joint->get_transform(); + } else { + mat = LMatrix4d::ident_mat(); + } + } else { + // Get the initial transform from the last frame of animation. + mat = table.back(); + } + table.push_back(mat); + while ((int)table.size() <= frame) { + table.push_back(mat); + } + + } else { + mat = table.back(); + } + + // Now modify the last row in the table. + switch (key_type) { + /* + case 0: + // Key type 0: rotation + break; + */ + + /* + case 1: + // Key type 1: scale + break; + */ + + case 2: + // Key type 2: position + if (nvalues != 3) { + xfile_cat.error() + << "Incorrect number of values in animation table: " + << nvalues << " for position data.\n"; + return false; + } + mat.set_row(3, LVecBase3d(values[0], values[1], values[2])); + break; + + /* + case 3: + // Key type 3: ???? + break; + */ + + case 4: + // Key type 4: full matrix + if (nvalues != 16) { + xfile_cat.error() + << "Incorrect number of values in animation table: " + << nvalues << " for matrix data.\n"; + return false; + } + mat.set(values[0], values[1], values[2], values[3], + values[4], values[5], values[6], values[7], + values[8], values[9], values[10], values[11], + values[12], values[13], values[14], values[15]); + break; + + default: + xfile_cat.error() + << "Unsupported key type " << key_type << " in animation table.\n"; + return false; + } + + table.back() = mat; + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: XFileToEggConverter::convert_mesh // Access: Private @@ -441,7 +1025,8 @@ convert_transform(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { // structures. //////////////////////////////////////////////////////////////////// bool XFileToEggConverter:: -convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { +convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent, + bool is_toplevel) { HRESULT hr; Datagram raw_data; @@ -449,9 +1034,12 @@ convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { return false; } - XFileMesh mesh(_egg_data->get_coordinate_system()); - mesh.set_name(get_object_name(obj)); - if (!mesh.read_mesh_data(raw_data)) { + XFileMesh *mesh = new XFileMesh(_egg_data->get_coordinate_system()); + mesh->set_name(get_object_name(obj)); + mesh->set_egg_parent(egg_parent); + + if (!mesh->read_mesh_data(raw_data)) { + delete mesh; return false; } @@ -460,7 +1048,7 @@ convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { hr = obj->GetNextObject(&child_obj); while (hr == DXFILE_OK) { - if (!convert_mesh_object(child_obj, mesh)) { + if (!convert_mesh_object(child_obj, *mesh)) { return false; } hr = obj->GetNextObject(&child_obj); @@ -470,11 +1058,14 @@ convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent) { xfile_cat.error() << "Error extracting children of mesh " << get_object_name(obj) << ".\n"; + delete mesh; return false; } - if (!mesh.create_polygons(egg_parent, this)) { - return false; + if (is_toplevel) { + _toplevel_meshes.push_back(mesh); + } else { + _meshes.push_back(mesh); } return true; @@ -556,6 +1147,14 @@ convert_mesh_data_object(LPDIRECTXFILEDATA obj, XFileMesh &mesh) { return false; } + } else if (*type == DXFILEOBJ_XSkinMeshHeader) { + // Quietly ignore a skin mesh header. + + } else if (*type == DXFILEOBJ_SkinWeights) { + if (!convert_skin_weights(obj, mesh)) { + return false; + } + } else { if (xfile_cat.is_debug()) { xfile_cat.debug() @@ -627,6 +1226,26 @@ convert_mesh_uvs(LPDIRECTXFILEDATA obj, XFileMesh &mesh) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::convert_skin_weights +// Access: Private +// Description: Converts the indicated SkinWeights template +// object. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +convert_skin_weights(LPDIRECTXFILEDATA obj, XFileMesh &mesh) { + Datagram raw_data; + if (!get_data(obj, raw_data)) { + return false; + } + + if (!mesh.read_skin_weights_data(raw_data)) { + return false; + } + + return true; +} + //////////////////////////////////////////////////////////////////// // Function: XFileToEggConverter::convert_mesh_material_list // Access: Private @@ -874,6 +1493,67 @@ convert_texture(LPDIRECTXFILEDATA obj, XFileMaterial &material) { return true; } +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::create_polygons +// Access: Private +// Description: Creates all the polygons associated with +// previously-saved meshes. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +create_polygons() { + bool okflag = true; + + Meshes::const_iterator mi; + for (mi = _meshes.begin(); mi != _meshes.end(); ++mi) { + if (!(*mi)->create_polygons(this)) { + okflag = false; + } + delete (*mi); + } + _meshes.clear(); + + for (mi = _toplevel_meshes.begin(); mi != _toplevel_meshes.end(); ++mi) { + if (!_any_frames) { + if (!(*mi)->create_polygons(this)) { + okflag = false; + } + } + delete (*mi); + } + _toplevel_meshes.clear(); + + return okflag; +} + +//////////////////////////////////////////////////////////////////// +// Function: XFileToEggConverter::create_hierarchy +// Access: Private +// Description: Creates the animation table hierarchies for the +// previously-saved animation sets. +//////////////////////////////////////////////////////////////////// +bool XFileToEggConverter:: +create_hierarchy() { + bool okflag = true; + + if (!_make_char && !_animation_sets.empty()) { + xfile_cat.warning() + << "Ignoring animation data without -a.\n"; + } + + AnimationSets::const_iterator asi; + for (asi = _animation_sets.begin(); asi != _animation_sets.end(); ++asi) { + if (_make_char) { + if (!(*asi)->create_hierarchy(this)) { + okflag = false; + } + } + delete (*asi); + } + _animation_sets.clear(); + + return okflag; +} + //////////////////////////////////////////////////////////////////// // Function: XFileToEggConverter::get_object_name // Access: Private diff --git a/pandatool/src/xfile/xFileToEggConverter.h b/pandatool/src/xfile/xFileToEggConverter.h index 689e895447..31c127092f 100644 --- a/pandatool/src/xfile/xFileToEggConverter.h +++ b/pandatool/src/xfile/xFileToEggConverter.h @@ -20,9 +20,13 @@ #define XFILETOEGGCONVERTER_H #include "pandatoolbase.h" +#include "xFileAnimationSet.h" #include "somethingToEggConverter.h" #include "eggTextureCollection.h" #include "eggMaterialCollection.h" +#include "pvector.h" +#include "pmap.h" +#include "luse.h" #define WIN32_LEAN_AND_MEAN #include @@ -31,10 +35,11 @@ #include #undef WIN32_LEAN_AND_MEAN -class EggGroupNode; class Datagram; class XFileMesh; class XFileMaterial; +class EggGroup; +class EggGroupNode; class EggTexture; class EggMaterial; @@ -56,24 +61,53 @@ public: virtual bool convert_file(const Filename &filename); void close(); + EggGroup *get_dart_node() const; + EggTexture *create_unique_texture(const EggTexture ©); EggMaterial *create_unique_material(const EggMaterial ©); + EggGroup *find_joint(const string &joint_name); + EggGroup *find_joint(const string &joint_name, + const LMatrix4f &matrix_offset); + +public: + bool _make_char; + string _char_name; private: + typedef XFileAnimationSet::FrameData FrameData; + bool get_toplevel(); - bool convert_toplevel_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent, - EggGroupNode *egg_toplevel, bool &any_frames); + bool convert_toplevel_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent); bool convert_object(LPDIRECTXFILEOBJECT obj, EggGroupNode *egg_parent); bool convert_data_object(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent); bool convert_frame(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent); bool convert_transform(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent); - bool convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent); + bool convert_animation_set(LPDIRECTXFILEDATA obj); + bool convert_animation_set_object(LPDIRECTXFILEOBJECT obj, + XFileAnimationSet &animation_set); + bool convert_animation_set_data_object(LPDIRECTXFILEDATA obj, + XFileAnimationSet &animation_set); + bool convert_animation(LPDIRECTXFILEDATA obj, + XFileAnimationSet &animation_set); + bool convert_animation_object(LPDIRECTXFILEOBJECT obj, + const string &joint_name, FrameData &table); + bool convert_animation_data_object(LPDIRECTXFILEDATA obj, + const string &joint_name, + FrameData &table); + bool convert_animation_key(LPDIRECTXFILEDATA obj, const string &joint_name, + FrameData &table); + bool set_animation_frame(const string &joint_name, FrameData &table, + int frame, int key_type, + const float *values, int nvalues); + bool convert_mesh(LPDIRECTXFILEDATA obj, EggGroupNode *egg_parent, + bool is_toplevel); bool convert_mesh_object(LPDIRECTXFILEOBJECT obj, XFileMesh &mesh); bool convert_mesh_data_object(LPDIRECTXFILEDATA obj, XFileMesh &mesh); bool convert_mesh_normals(LPDIRECTXFILEDATA obj, XFileMesh &mesh); bool convert_mesh_colors(LPDIRECTXFILEDATA obj, XFileMesh &mesh); bool convert_mesh_uvs(LPDIRECTXFILEDATA obj, XFileMesh &mesh); + bool convert_skin_weights(LPDIRECTXFILEDATA obj, XFileMesh &mesh); bool convert_mesh_material_list(LPDIRECTXFILEDATA obj, XFileMesh &mesh); bool convert_material_list_object(LPDIRECTXFILEOBJECT obj, XFileMesh &mesh); bool convert_material_list_data_object(LPDIRECTXFILEDATA obj, XFileMesh &mesh); @@ -82,12 +116,42 @@ private: bool convert_material_data_object(LPDIRECTXFILEDATA obj, XFileMaterial &material); bool convert_texture(LPDIRECTXFILEDATA obj, XFileMaterial &material); + bool create_polygons(); + bool create_hierarchy(); + string get_object_name(LPDIRECTXFILEOBJECT obj); bool get_data(LPDIRECTXFILEDATA obj, Datagram &raw_data); LPDIRECTXFILE _dx_file; LPDIRECTXFILEENUMOBJECT _dx_file_enum; + bool _any_frames; + + typedef pvector Meshes; + Meshes _meshes; + Meshes _toplevel_meshes; + + typedef pvector AnimationSets; + AnimationSets _animation_sets; + + typedef pmap OffsetJoints; + + // A joint definition consists of the pointer to the EggGroup that + // represents the actual joint, plus a table of synthetic joints + // that were created for each animation set's offset matrix (we need + // to create a different joint to apply each unique offset matrix in + // an animation set). + class JointDef { + public: + EggGroup *_node; + OffsetJoints _offsets; + }; + + typedef pmap Joints; + Joints _joints; + + EggGroup *_dart_node; + EggTextureCollection _textures; EggMaterialCollection _materials; }; diff --git a/pandatool/src/xfile/xfile_composite1.cxx b/pandatool/src/xfile/xfile_composite1.cxx index 2b1d8bac57..f06b5bb7d2 100644 --- a/pandatool/src/xfile/xfile_composite1.cxx +++ b/pandatool/src/xfile/xfile_composite1.cxx @@ -1,5 +1,6 @@ #include "config_xfile.cxx" +#include "xFileAnimationSet.cxx" #include "xFileFace.cxx" #include "xFileMaker.cxx" #include "xFileMaterial.cxx" diff --git a/pandatool/src/xfileprogs/eggToX.cxx b/pandatool/src/xfileprogs/eggToX.cxx index 46206a5cd6..99fa3d516e 100644 --- a/pandatool/src/xfileprogs/eggToX.cxx +++ b/pandatool/src/xfileprogs/eggToX.cxx @@ -33,7 +33,7 @@ EggToX() : EggToSomething("DirectX", ".x", true, false) { ("This program reads an Egg file and outputs an equivalent, " "or nearly equivalent, DirectX-style .x file. Only simple " "hierarchy and polygon meshes are supported; advanced features " - "like LOD's, decals, and characters cannot be supported."); + "like LOD's, decals, and animation or skinning are not supported."); add_option ("m", "", 0, diff --git a/pandatool/src/xfileprogs/xFileToEgg.cxx b/pandatool/src/xfileprogs/xFileToEgg.cxx index 842e8e80cd..0ddaf371f2 100644 --- a/pandatool/src/xfileprogs/xFileToEgg.cxx +++ b/pandatool/src/xfileprogs/xFileToEgg.cxx @@ -36,9 +36,19 @@ XFileToEgg() : add_transform_options(); set_program_description - ("This program converts DirectX retained-mode (.x) files to egg. This " - "is a simple converter that only supports basic polygons, materials, " - "and textures, in a hierarchy; animation is not supported at this time."); + ("This program converts DirectX retained-mode (.x) files to egg. " + "Polygon meshes, materials, and textures, as well as skeleton " + "animation and skinning data, are supported. All animations " + "found in the source .x file are written together into the same " + "egg file."); + + add_option + ("a", "name", 0, + "Convert as an animatable model, converting Frames into Joints. This " + "should be specified for a model which is intended to be animated. The " + "default is to convert the model as a normal static model, which is " + "usually more optimal if animation is not required.", + &XFileToEgg::dispatch_string, &_make_char, &_char_name); redescribe_option ("cs", @@ -60,6 +70,9 @@ run() { XFileToEggConverter converter; converter.set_egg_data(&_data, false); + converter._make_char = _make_char; + converter._char_name = _char_name; + // Copy in the path and animation parameters. apply_parameters(converter); diff --git a/pandatool/src/xfileprogs/xFileToEgg.h b/pandatool/src/xfileprogs/xFileToEgg.h index b169167acd..6ebde8134c 100644 --- a/pandatool/src/xfileprogs/xFileToEgg.h +++ b/pandatool/src/xfileprogs/xFileToEgg.h @@ -35,6 +35,10 @@ public: XFileToEgg(); void run(); + +public: + bool _make_char; + string _char_name; }; #endif