diff --git a/pandatool/src/egg-optchar/Sources.pp b/pandatool/src/egg-optchar/Sources.pp index 8d599f4dc4..7ebeaad653 100644 --- a/pandatool/src/egg-optchar/Sources.pp +++ b/pandatool/src/egg-optchar/Sources.pp @@ -7,12 +7,12 @@ dtoolutil:c dtoolbase:c dconfig:c dtoolconfig:m dtool:m pystub #define UNIX_SYS_LIBS m -#begin test_bin_target - #define TARGET egg-optchar +#begin bin_target + #define TARGET egg-optchar-new #define SOURCES \ config_egg_optchar.cxx config_egg_optchar.h \ eggOptchar.cxx eggOptchar.h \ eggOptcharUserData.I eggOptcharUserData.cxx eggOptcharUserData.h -#end test_bin_target +#end bin_target diff --git a/pandatool/src/egg-optchar/eggOptchar.cxx b/pandatool/src/egg-optchar/eggOptchar.cxx index 151ef23102..751abbe5a3 100644 --- a/pandatool/src/egg-optchar/eggOptchar.cxx +++ b/pandatool/src/egg-optchar/eggOptchar.cxx @@ -17,16 +17,16 @@ //////////////////////////////////////////////////////////////////// #include "eggOptchar.h" - #include "eggOptcharUserData.h" + #include "dcast.h" #include "eggJointData.h" #include "eggSliderData.h" #include "eggCharacterCollection.h" #include "eggCharacterData.h" -#include "eggJointPointer.h" -#include "eggTable.h" -#include "compose_matrix.h" +#include "eggBackPointer.h" +#include "string_utils.h" +#include "pset.h" //////////////////////////////////////////////////////////////////// // Function: EggOptchar::Constructor @@ -49,6 +49,38 @@ EggOptchar() { ("ls", "", 0, "List the joint hierarchy instead of performing any operations.", &EggOptchar::dispatch_none, &_list_hierarchy); + + add_option + ("lp", "", 0, + "List the existing joint hierarchy as a series of -p joint,parent " + "commands, suitable for pasting into an egg-optchar command line.", + &EggOptchar::dispatch_none, &_list_hierarchy_p); + + add_option + ("keep", "joint", 0, + "Keep the named joint (or slider) in the character, even if it does " + "not appear to be needed by the animation.", + &EggOptchar::dispatch_vector_string, NULL, &_keep_components); + + add_option + ("expose", "joint", 0, + "Expose the named joint by flagging it with a DCS attribute, so " + "it can be found in the scene graph when the character is loaded, and " + "objects can be parented to it. This implies -keep.", + &EggOptchar::dispatch_vector_string, NULL, &_expose_components); + + add_option + ("keepall", "", 0, + "Keep all joints and sliders in the character.", + &EggOptchar::dispatch_none, &_keep_all); + + add_option + ("p", "joint,parent", 0, + "Moves the named joint under the named parent joint. Use " + "\"-p joint,\" to reparent a joint to the root. The joint transform " + "is recomputed appropriately under its new parent so that the animation " + "is not affected (the effect is similar to NodePath::wrt_reparent_to).", + &EggOptchar::dispatch_vector_string_pair, NULL, &_reparent_joints); } //////////////////////////////////////////////////////////////////// @@ -58,13 +90,22 @@ EggOptchar() { //////////////////////////////////////////////////////////////////// void EggOptchar:: run() { - int num_characters = _collection->get_num_characters(); + // We have to apply the user-specified reparent requests first, + // before we even analyze the joints. This is because reparenting + // the joints may change their properties. + if (apply_user_reparents()) { + cerr << "Reparenting hierarchy.\n"; + // So we'll have to call do_reparent() twice. It seems wasteful, + // but it really is necessary, and it's not that bad. + do_reparent(); + } + int num_characters = _collection->get_num_characters(); int ci; + + // Now we can analyze the joints for their properties. for (ci = 0; ci < num_characters; ci++) { EggCharacterData *char_data = _collection->get_character(ci); - - nout << "Processing " << char_data->get_name() << "\n"; analyze_joints(char_data->get_root_joint()); analyze_sliders(char_data); } @@ -75,15 +116,27 @@ run() { nout << "Character: " << char_data->get_name() << "\n"; list_joints(char_data->get_root_joint(), 0); list_scalars(char_data); + nout << char_data->get_num_joints() << " joints.\n"; + } + + } else if (_list_hierarchy_p) { + for (ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + nout << "Character: " << char_data->get_name() << "\n"; + list_joints_p(char_data->get_root_joint()); + // A newline to cout is needed after the above call. + cout << "\n"; + nout << char_data->get_num_joints() << " joints.\n"; } } else { - // Now, trigger the actual rebuilding of all the joint data. - for (ci = 0; ci < num_characters; ci++) { - EggCharacterData *char_data = _collection->get_character(ci); - char_data->get_root_joint()->do_rebuild(); + // The meat of the program: determine which joints are to be + // removed, and then actually remove them. + determine_removed_components(); + if (remove_joints()) { + do_reparent(); } - + write_eggs(); } } @@ -105,6 +158,236 @@ handle_args(ProgramBase::Args &args) { return EggCharacterFilter::handle_args(args); } +//////////////////////////////////////////////////////////////////// +// Function: ProgramBase::dispatch_vector_string_pair +// Access: Protected, Static +// Description: Standard dispatch function for an option that takes +// a pair of string parameters. The data pointer is to +// StringPairs vector; the pair will be pushed onto the +// end of the vector. +//////////////////////////////////////////////////////////////////// +bool EggOptchar:: +dispatch_vector_string_pair(const string &opt, const string &arg, void *var) { + StringPairs *ip = (StringPairs *)var; + + vector_string words; + tokenize(arg, words, ","); + + if (words.size() == 2) { + StringPair sp; + sp._a = words[0]; + sp._b = words[1]; + ip->push_back(sp); + + } else { + nout << "-" << opt + << " requires a pair of strings separated by a comma.\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::determine_removed_components +// Access: Private +// Description: Flag all joints and sliders that should be removed +// for optimization purposes. +//////////////////////////////////////////////////////////////////// +void EggOptchar:: +determine_removed_components() { + typedef pset Names; + Names keep_names; + Names expose_names; + Names names_used; + + vector_string::const_iterator si; + for (si = _keep_components.begin(); si != _keep_components.end(); ++si) { + keep_names.insert(*si); + } + for (si = _expose_components.begin(); si != _expose_components.end(); ++si) { + keep_names.insert(*si); + expose_names.insert(*si); + } + + // We always keep the root joint, which has no name. + keep_names.insert(""); + + int num_characters = _collection->get_num_characters(); + for (int ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + int num_components = char_data->get_num_components(); + for (int i = 0; i < num_components; i++) { + EggComponentData *comp_data = char_data->get_component(i); + EggOptcharUserData *user_data = + DCAST(EggOptcharUserData, comp_data->get_user_data()); + + const string &name = comp_data->get_name(); + if (_keep_all || keep_names.find(name) != keep_names.end()) { + // Keep this component. + names_used.insert(name); + + if (expose_names.find(name) != expose_names.end()) { + // In fact, expose it. + user_data->_flags |= EggOptcharUserData::F_expose; + } + + } else { + // Remove this component if it's unanimated or empty. + if ((user_data->_flags & (EggOptcharUserData::F_static | EggOptcharUserData::F_empty)) != 0) { + user_data->_flags |= EggOptcharUserData::F_remove; + } + } + } + } + + // Go back and tell the user about component names we didn't use, + // just to be helpful. + for (si = _keep_components.begin(); si != _keep_components.end(); ++si) { + const string &name = (*si); + if (names_used.find(name) == names_used.end()) { + nout << "No such joint: " << name << "\n"; + } + } + for (si = _expose_components.begin(); si != _expose_components.end(); ++si) { + const string &name = (*si); + if (names_used.find(name) == names_used.end()) { + nout << "No such joint: " << name << "\n"; + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::remove_joints +// Access: Private +// Description: Effects the actual removal of joints flagged for +// removal by reparenting the hierarchy appropriately. +// Returns true if anything is done, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggOptchar:: +remove_joints() { + bool did_anything = false; + int num_characters = _collection->get_num_characters(); + for (int ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + int num_joints = char_data->get_num_joints(); + + int num_static = 0; + int num_empty = 0; + int num_identity = 0; + int num_kept = 0; + + for (int i = 0; i < num_joints; i++) { + EggJointData *joint_data = char_data->get_joint(i); + EggOptcharUserData *user_data = + DCAST(EggOptcharUserData, joint_data->get_user_data()); + + EggJointData *best_parent = find_best_parent(joint_data->get_parent()); + + if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) { + // This joint will be removed, so reparent it to nothing. + joint_data->reparent_to((EggJointData *)NULL); + + // Move the vertices associated with this joint into its + // parent. + joint_data->move_vertices_to(best_parent); + + // Determine what kind of node it is we're removing, for the + // user's information. + if ((user_data->_flags & EggOptcharUserData::F_identity) != 0) { + num_identity++; + } else if ((user_data->_flags & EggOptcharUserData::F_static) != 0) { + num_static++; + } else if ((user_data->_flags & EggOptcharUserData::F_empty) != 0) { + num_empty++; + } + did_anything = true; + + } else { + // This joint will be preserved, but maybe its parent will + // change. + joint_data->reparent_to(best_parent); + num_kept++; + } + } + + if (num_joints == num_kept) { + nout << char_data->get_name() << ": keeping " << num_joints + << " joints.\n"; + } else { + nout << char_data->get_name() << ": of " << num_joints + << " joints, removing " << num_identity << " identity, " + << num_static << " static, and " << num_empty + << " empty joints, leaving " << num_kept << ".\n"; + } + } + + return did_anything; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::find_best_parent +// Access: Private +// Description: Searches for this first joint at this level or above +// that is not scheduled to be removed. This is the +// joint that the first child of this joint should be +// reparented to. +//////////////////////////////////////////////////////////////////// +EggJointData *EggOptchar:: +find_best_parent(EggJointData *joint_data) const { + EggOptcharUserData *user_data = + DCAST(EggOptcharUserData, joint_data->get_user_data()); + + if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) { + // Keep going. + nassertr(joint_data->get_parent() != (EggJointData *)NULL, NULL); + return find_best_parent(joint_data->get_parent()); + } + + // This is the one! + return joint_data; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::apply_user_reparents +// Access: Private +// Description: Reparents all the joints that the user suggested on +// the command line. Returns true if any operations +// were performed, false otherwise. +//////////////////////////////////////////////////////////////////// +bool EggOptchar:: +apply_user_reparents() { + bool did_anything = false; + int num_characters = _collection->get_num_characters(); + + StringPairs::const_iterator spi; + for (spi = _reparent_joints.begin(); spi != _reparent_joints.end(); ++spi) { + const StringPair &p = (*spi); + + for (int ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + EggJointData *node_a = char_data->find_joint(p._a); + EggJointData *node_b = char_data->get_root_joint(); + if (!p._b.empty()) { + node_b = char_data->find_joint(p._b); + } + + if (node_a == (EggJointData *)NULL) { + nout << "No joint named " << p._a << " in " << char_data->get_name() + << ".\n"; + } else if (node_b == (EggJointData *)NULL) { + nout << "No joint named " << p._b << " in " << char_data->get_name() + << ".\n"; + } else { + node_a->reparent_to(node_b); + did_anything = true; + } + } + } + + return did_anything; +} + //////////////////////////////////////////////////////////////////// // Function: EggOptchar::analyze_joints // Access: Private @@ -263,6 +546,29 @@ list_joints(EggJointData *joint_data, int indent_level) { } } +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::list_joints_p +// Access: Private +// Description: Outputs a list of the joint hierarchy as a series of +// -p joint,parent commands. +//////////////////////////////////////////////////////////////////// +void EggOptchar:: +list_joints_p(EggJointData *joint_data) { + // As above, don't list the root joint. + + int num_children = joint_data->get_num_children(); + for (int i = 0; i < num_children; i++) { + EggJointData *child_data = joint_data->get_child(i); + // We send output to cout instead of nout to avoid the + // word-wrapping, and also to allow the user to redirect this + // easily to a file. + cout << " -p " << child_data->get_name() << "," + << joint_data->get_name(); + + list_joints_p(child_data); + } +} + //////////////////////////////////////////////////////////////////// // Function: EggOptchar::list_scalars // Access: Private @@ -284,20 +590,36 @@ list_scalars(EggCharacterData *char_data) { //////////////////////////////////////////////////////////////////// void EggOptchar:: describe_component(EggComponentData *comp_data, int indent_level) { - indent(nout, indent_level) + // We use cout instead of nout so the user can easily redirect this + // to a file. + indent(cout, indent_level) << comp_data->get_name(); EggOptcharUserData *user_data = DCAST(EggOptcharUserData, comp_data->get_user_data()); if (user_data->is_identity()) { - nout << " (identity)"; + cout << " (identity)"; } else if (user_data->is_static()) { - nout << " (static)"; + cout << " (static)"; } if (user_data->is_empty()) { - nout << " (empty)"; + cout << " (empty)"; + } + cout << "\n"; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggOptchar::do_reparent +// Access: Private +// Description: Performs all of the queued up reparenting operations. +//////////////////////////////////////////////////////////////////// +void EggOptchar:: +do_reparent() { + int num_characters = _collection->get_num_characters(); + for (int ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + char_data->do_reparent(); } - nout << "\n"; } diff --git a/pandatool/src/egg-optchar/eggOptchar.h b/pandatool/src/egg-optchar/eggOptchar.h index 25bffbff20..6d9ec31b73 100644 --- a/pandatool/src/egg-optchar/eggOptchar.h +++ b/pandatool/src/egg-optchar/eggOptchar.h @@ -25,6 +25,7 @@ #include "luse.h" #include "pvector.h" +#include "vector_string.h" class EggCharacterData; class EggComponentData; @@ -49,13 +50,35 @@ protected: virtual bool handle_args(Args &args); private: + static bool dispatch_vector_string_pair(const string &opt, const string &arg, void *var); + + void determine_removed_components(); + bool remove_joints(); + EggJointData *find_best_parent(EggJointData *joint_data) const; + + bool apply_user_reparents(); void analyze_joints(EggJointData *joint_data); void analyze_sliders(EggCharacterData *char_data); void list_joints(EggJointData *joint_data, int indent_level); + void list_joints_p(EggJointData *joint_data); void list_scalars(EggCharacterData *char_data); void describe_component(EggComponentData *comp_data, int indent_level); + void do_reparent(); bool _list_hierarchy; + bool _list_hierarchy_p; + bool _keep_all; + + class StringPair { + public: + string _a; + string _b; + }; + typedef pvector StringPairs; + StringPairs _reparent_joints; + + vector_string _keep_components; + vector_string _expose_components; }; #endif diff --git a/pandatool/src/egg-optchar/eggOptcharUserData.h b/pandatool/src/egg-optchar/eggOptcharUserData.h index bcee4e6ca6..4e195fc12d 100644 --- a/pandatool/src/egg-optchar/eggOptcharUserData.h +++ b/pandatool/src/egg-optchar/eggOptcharUserData.h @@ -43,7 +43,8 @@ public: F_static = 0x0001, F_identity = 0x0002, F_empty = 0x0004, - F_remove = 0x0008 + F_remove = 0x0008, + F_expose = 0x0010, }; int _flags; LMatrix4d _static_mat; diff --git a/pandatool/src/eggcharbase/Sources.pp b/pandatool/src/eggcharbase/Sources.pp index d7c25f13a5..90478dcddb 100644 --- a/pandatool/src/eggcharbase/Sources.pp +++ b/pandatool/src/eggcharbase/Sources.pp @@ -12,7 +12,8 @@ eggCharacterCollection.h eggCharacterCollection.I \ eggCharacterData.h eggCharacterData.I eggCharacterFilter.h \ eggComponentData.h eggComponentData.I eggJointData.h \ - eggJointData.I eggJointPointer.h eggJointNodePointer.h \ + eggJointData.I eggJointPointer.h eggJointPointer.I \ + eggJointNodePointer.h \ eggMatrixTablePointer.h eggScalarTablePointer.h \ eggSliderData.h eggSliderData.I \ eggVertexPointer.h @@ -32,7 +33,7 @@ eggCharacterData.I eggCharacterData.h eggCharacterFilter.h \ eggComponentData.I eggComponentData.h \ eggJointData.h eggJointData.I \ - eggJointPointer.h \ + eggJointPointer.h eggJointPointer.I \ eggJointNodePointer.h \ eggMatrixTablePointer.h \ eggScalarTablePointer.h \ diff --git a/pandatool/src/eggcharbase/eggCharacterCollection.I b/pandatool/src/eggcharbase/eggCharacterCollection.I index aeed66fa70..1a379d9569 100644 --- a/pandatool/src/eggcharbase/eggCharacterCollection.I +++ b/pandatool/src/eggcharbase/eggCharacterCollection.I @@ -112,3 +112,13 @@ get_character_by_model_index(int model_index) const { (EggCharacterData *)NULL); return _characters_by_model_index[model_index]; } + +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterCollection::ModelDescription::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EggCharacterCollection::ModelDescription:: +ModelDescription() { + _root_node = (EggObject *)NULL; +} diff --git a/pandatool/src/eggcharbase/eggCharacterCollection.cxx b/pandatool/src/eggcharbase/eggCharacterCollection.cxx index 3eeba8ca46..f5900b8a6f 100644 --- a/pandatool/src/eggcharbase/eggCharacterCollection.cxx +++ b/pandatool/src/eggcharbase/eggCharacterCollection.cxx @@ -98,7 +98,7 @@ add_egg(EggData *egg) { TopEggNodes::iterator ti; for (ti = top_nodes.begin(); ti != top_nodes.end(); ++ti) { EggNode *model_root = (*ti).first; - EggNodeList &egg_nodes = (*ti).second; + ModelDescription &desc = (*ti).second; int model_index = _next_model_index++; if (egg_info._models.empty()) { @@ -109,8 +109,9 @@ add_egg(EggData *egg) { char_data->add_model(model_index, model_root); nassertr(model_index == (int)_characters_by_model_index.size(), -1); _characters_by_model_index.push_back(char_data); + root_joint->add_back_pointer(model_index, desc._root_node); - match_egg_nodes(char_data, root_joint, egg_nodes, + match_egg_nodes(char_data, root_joint, desc._top_nodes, egg_index, model_index); scan_for_morphs(model_root, model_index, char_data); @@ -268,12 +269,16 @@ scan_for_top_joints(EggNode *egg_node, EggNode *model_root, EggGroup *group = DCAST(EggGroup, egg_node); if (group->has_lod()) { - // This flag has an LOD specification. + // This group has an LOD specification; that indicates multiple + // skeleton hierarchies for this character, one for each LOD. + // We call each of these a separate model. model_root = group; } if (group->get_group_type() == EggGroup::GT_joint) { // A node begins a model hierarchy. - _top_egg_nodes[character_name][model_root].push_back(group); + ModelDescription &desc = _top_egg_nodes[character_name][model_root]; + desc._root_node = model_root; + desc._top_nodes.push_back(group); return; } } @@ -307,12 +312,14 @@ scan_for_top_tables(EggTable *bundle, EggNode *model_root, if (table->get_name() == "") { // Here it is! Now the immediate children of this node are // the top tables. + ModelDescription &desc = _top_egg_nodes[character_name][model_root]; + desc._root_node = table; EggGroupNode::iterator cgi; for (cgi = table->begin(); cgi != table->end(); ++cgi) { EggNode *grandchild = (*cgi); if (grandchild->is_of_type(EggTable::get_class_type())) { - _top_egg_nodes[character_name][model_root].push_back(grandchild); + desc._top_nodes.push_back(grandchild); } } } @@ -459,8 +466,10 @@ match_egg_nodes(EggCharacterData *char_data, EggJointData *joint_data, EggNode *egg_node = (*ei); EggJointData *data = make_joint_data(char_data); joint_data->_children.push_back(data); + char_data->_joints.push_back(data); char_data->_components.push_back(data); data->_parent = joint_data; + data->_new_parent = joint_data; found_egg_match(char_data, data, egg_node, egg_index, model_index); } @@ -565,8 +574,10 @@ match_egg_nodes(EggCharacterData *char_data, EggJointData *joint_data, EggNode *egg_node = (*ei); EggJointData *data = make_joint_data(char_data); joint_data->_children.push_back(data); + char_data->_joints.push_back(data); char_data->_components.push_back(data); data->_parent = joint_data; + data->_new_parent = joint_data; found_egg_match(char_data, data, egg_node, egg_index, model_index); } } diff --git a/pandatool/src/eggcharbase/eggCharacterCollection.h b/pandatool/src/eggcharbase/eggCharacterCollection.h index 688c1bcb2e..9ca703aa55 100644 --- a/pandatool/src/eggcharbase/eggCharacterCollection.h +++ b/pandatool/src/eggcharbase/eggCharacterCollection.h @@ -19,13 +19,13 @@ #ifndef EGGCHARACTERCOLLECTION_H #define EGGCHARACTERCOLLECTION_H -#include +#include "pandatoolbase.h" #include "eggCharacterData.h" -#include -#include -#include +#include "eggData.h" +#include "eggNode.h" +#include "pointerTo.h" class EggTable; class EggAttributes; @@ -66,7 +66,7 @@ public: class EggInfo { public: PT(EggData) _egg; - typedef pvector Models; + typedef pvector< PT(EggNode) > Models; Models _models; int _first_model_index; }; @@ -95,7 +95,14 @@ private: // The _top_egg_nodes member is only used temporarily, when adding // each pre-existing egg file to the structure for the first time. typedef pvector EggNodeList; - typedef pmap TopEggNodes; + class ModelDescription { + public: + INLINE ModelDescription(); + EggNodeList _top_nodes; + EggObject *_root_node; + }; + + typedef pmap TopEggNodes; typedef pmap TopEggNodesByName; TopEggNodesByName _top_egg_nodes; diff --git a/pandatool/src/eggcharbase/eggCharacterData.I b/pandatool/src/eggcharbase/eggCharacterData.I index 0e7866a17d..630d5a4711 100644 --- a/pandatool/src/eggcharbase/eggCharacterData.I +++ b/pandatool/src/eggcharbase/eggCharacterData.I @@ -94,6 +94,30 @@ find_joint(const string &name) const { return _root_joint->find_joint(name); } +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterData::get_num_joints +// Access: Public +// Description: Returns the total number of joints in the character +// joint hierarchy. +//////////////////////////////////////////////////////////////////// +INLINE int EggCharacterData:: +get_num_joints() const { + return _joints.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterData::get_joint +// Access: Public +// Description: Returns the nth joint in the character joint +// hierarchy. This returns all of the joints in the +// hierarchy in an arbitrary ordering. +//////////////////////////////////////////////////////////////////// +INLINE EggJointData *EggCharacterData:: +get_joint(int n) const { + nassertr(n >= 0 && n < (int)_joints.size(), NULL); + return _joints[n]; +} + //////////////////////////////////////////////////////////////////// // Function: EggCharacterData::get_num_sliders // Access: Public diff --git a/pandatool/src/eggcharbase/eggCharacterData.cxx b/pandatool/src/eggcharbase/eggCharacterData.cxx index 01900689a2..25e9ef383e 100644 --- a/pandatool/src/eggcharbase/eggCharacterData.cxx +++ b/pandatool/src/eggcharbase/eggCharacterData.cxx @@ -72,6 +72,117 @@ add_model(int model_index, EggNode *model_root) { _models.push_back(m); } +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterData::get_num_frames +// Access: Public +// Description: Returns the number of frames of animation of the +// indicated model. This is more reliable than asking a +// particular joint or slider of the animation for its +// number of frames, since a particular joint may have +// only 1 frame (if it is unanimated), even though the +// overall animation has many frames. +//////////////////////////////////////////////////////////////////// +int EggCharacterData:: +get_num_frames(int model_index) const { + int max_num_frames = 0; + Components::const_iterator ci; + for (ci = _components.begin(); ci != _components.end(); ++ci) { + EggComponentData *component = (*ci); + int num_frames = component->get_num_frames(model_index); + if (num_frames > 1) { + // We have a winner. Assume all other components will be + // similar. + return num_frames; + } + max_num_frames = max(max_num_frames, num_frames); + } + + // Every component had either 1 frame or 0 frames. Return the + // maximum of these. + return max_num_frames; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterData::do_reparent +// Access: Public +// Description: Begins the process of restructuring the joint +// hierarchy according to the previous calls to +// reparent_to() on various joints. This will reparent +// the joint hierachy in all models as requested, while +// adjusting the transforms as appropriate so that each +// joint retains the same net transform across all +// frames that it had before the operation. Returns +// true on success, false on failure. +//////////////////////////////////////////////////////////////////// +bool EggCharacterData:: +do_reparent() { + typedef pset InvalidSet; + InvalidSet invalid_set; + + // First, make sure the list of new_children is accurate. + Joints::const_iterator ji; + for (ji = _joints.begin(); ji != _joints.end(); ++ji) { + EggJointData *joint_data = (*ji); + joint_data->do_begin_reparent(); + } + // We also need to clear the children on the root joint, but the + // root joint doesn't get any of the other operations (including + // finish_reparent) applied to it. + _root_joint->do_begin_reparent(); + + // Now compute the new transforms for the joints' new positions. + // This is done recursively through the new parent hierarchy, so we + // can take advantage of caching the net value for a particular + // frame. + Models::const_iterator mi; + for (mi = _models.begin(); mi != _models.end(); ++mi) { + int model_index = (*mi)._model_index; + int num_frames = get_num_frames(model_index); + for (int f = 0; f < num_frames; f++) { + // First, walk through all the joints and flush the computed net + // transforms from before. + for (ji = _joints.begin(); ji != _joints.end(); ++ji) { + EggJointData *joint_data = (*ji); + joint_data->do_begin_compute_reparent(); + } + _root_joint->do_begin_compute_reparent(); + + // Now go back through and compute the reparented transforms, + // caching net transforms as necessary. + for (ji = _joints.begin(); ji != _joints.end(); ++ji) { + EggJointData *joint_data = (*ji); + if (!joint_data->do_compute_reparent(model_index, f)) { + // Oops, we got an invalid transform. + invalid_set.insert(joint_data); + } + } + } + } + + // Now remove all of the old children and add in the new children. + for (ji = _joints.begin(); ji != _joints.end(); ++ji) { + EggJointData *joint_data = (*ji); + if (!joint_data->do_finish_reparent()) { + invalid_set.insert(joint_data); + } + } + + InvalidSet::const_iterator si; + for (si = invalid_set.begin(); si != invalid_set.end(); ++si) { + EggJointData *joint_data = (*si); + // Don't bother reporting joints that no longer have a parent, + // since we don't care about joints that are now outside the + // hierarchy. + if (joint_data->get_parent() != (EggJointData *)NULL) { + nout << "Warning: reparenting " << joint_data->get_name() + << " to " << joint_data->get_parent()->get_name() + << " results in a skew transform.\n"; + } + } + + return invalid_set.empty(); +} + //////////////////////////////////////////////////////////////////// // Function: EggCharacterData::find_slider // Access: Public diff --git a/pandatool/src/eggcharbase/eggCharacterData.h b/pandatool/src/eggcharbase/eggCharacterData.h index c74422fa22..f5a6a95f81 100644 --- a/pandatool/src/eggcharbase/eggCharacterData.h +++ b/pandatool/src/eggcharbase/eggCharacterData.h @@ -68,9 +68,13 @@ public: INLINE int get_num_models() const; INLINE int get_model_index(int n) const; INLINE EggNode *get_model_root(int n) const; + int get_num_frames(int model_index) const; INLINE EggJointData *get_root_joint() const; INLINE EggJointData *find_joint(const string &name) const; + INLINE int get_num_joints() const; + INLINE EggJointData *get_joint(int n) const; + bool do_reparent(); INLINE int get_num_sliders() const; INLINE EggSliderData *get_slider(int n) const; @@ -100,6 +104,9 @@ protected: typedef pvector Sliders; Sliders _sliders; + typedef pvector Joints; + Joints _joints; + typedef pvector Components; Components _components; diff --git a/pandatool/src/eggcharbase/eggComponentData.h b/pandatool/src/eggcharbase/eggComponentData.h index ddde2d1498..6a1db3abcd 100644 --- a/pandatool/src/eggcharbase/eggComponentData.h +++ b/pandatool/src/eggcharbase/eggComponentData.h @@ -45,6 +45,8 @@ public: void add_name(const string &name); bool matches_name(const string &name) const; + virtual int get_num_frames(int model_index) const=0; + virtual void add_back_pointer(int model_index, EggObject *egg_object)=0; virtual void write(ostream &out, int indent_level = 0) const=0; diff --git a/pandatool/src/eggcharbase/eggJointData.I b/pandatool/src/eggcharbase/eggJointData.I index 258a475496..8385916325 100644 --- a/pandatool/src/eggcharbase/eggJointData.I +++ b/pandatool/src/eggcharbase/eggJointData.I @@ -47,3 +47,17 @@ get_child(int n) const { nassertr(n >= 0 && n < (int)_children.size(), (EggJointData *)NULL); return _children[n]; } + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::reparent_to +// Access: Public +// Description: Indicates an intention to change the parent of this +// joint to the indicated joint, or NULL to remove it +// from the hierarchy. The joint is not reparented +// immediately, but rather all of the joints are +// reparented at once when do_reparent() is called. +//////////////////////////////////////////////////////////////////// +INLINE void EggJointData:: +reparent_to(EggJointData *new_parent) { + _new_parent = new_parent; +} diff --git a/pandatool/src/eggcharbase/eggJointData.cxx b/pandatool/src/eggcharbase/eggJointData.cxx index 8c27785ba5..164fa48217 100644 --- a/pandatool/src/eggcharbase/eggJointData.cxx +++ b/pandatool/src/eggcharbase/eggJointData.cxx @@ -39,6 +39,7 @@ EggJointData(EggCharacterCollection *collection, EggComponentData(collection, char_data) { _parent = (EggJointData *)NULL; + _new_parent = (EggJointData *)NULL; } //////////////////////////////////////////////////////////////////// @@ -66,7 +67,7 @@ find_joint(const string &name) { //////////////////////////////////////////////////////////////////// // Function: EggJointData::get_num_frames -// Access: Public +// Access: Public, Virtual // Description: Returns the number of frames of animation for this // particular joint in the indicated model. //////////////////////////////////////////////////////////////////// @@ -119,6 +120,26 @@ get_net_frame(int model_index, int n) const { return mat; } +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::move_vertices_to +// Access: Public +// Description: Moves the vertices assigned to this joint into the +// indicated joint, without changing their weight +// assignments. +//////////////////////////////////////////////////////////////////// +void EggJointData:: +move_vertices_to(EggJointData *new_owner) { + int num_models = get_num_models(); + for (int model_index = 0; model_index < num_models; model_index++) { + if (has_model(model_index) && new_owner->has_model(model_index)) { + EggJointPointer *joint, *new_joint; + DCAST_INTO_V(joint, get_model(model_index)); + DCAST_INTO_V(new_joint, new_owner->get_model(model_index)); + joint->move_vertices_to(new_joint); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggJointData::do_rebuild // Access: Public @@ -186,6 +207,7 @@ optimize() { //////////////////////////////////////////////////////////////////// void EggJointData:: add_back_pointer(int model_index, EggObject *egg_object) { + nassertv(egg_object != (EggObject *)NULL); if (egg_object->is_of_type(EggGroup::get_class_type())) { // It must be a . EggJointNodePointer *joint = new EggJointNodePointer(egg_object); @@ -226,3 +248,196 @@ write(ostream &out, int indent_level) const { indent(out, indent_level) << "}\n"; } + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::do_begin_reparent +// Access: Protected +// Description: Clears out the _children vector in preparation for +// refilling it from the _new_parent information. +//////////////////////////////////////////////////////////////////// +void EggJointData:: +do_begin_reparent() { + _children.clear(); + + int num_models = get_num_models(); + for (int model_index = 0; model_index < num_models; model_index++) { + if (has_model(model_index)) { + EggJointPointer *joint; + DCAST_INTO_V(joint, get_model(model_index)); + joint->begin_rebuild(); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::do_begin_compute_reparent +// Access: Protected +// Description: Eliminates any cached values before beginning a walk +// through all the joints for do_compute_reparent(), for +// a given model/frame. +//////////////////////////////////////////////////////////////////// +void EggJointData:: +do_begin_compute_reparent() { + _got_new_net_frame = false; + _got_new_net_frame_inv = false; + _computed_reparent = false; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::do_compute_reparent +// Access: Protected +// Description: Prepares the reparent operation by computing a new +// transform for each frame of each model, designed to +// keep the net transform the same when the joint is +// moved to its new parent. Returns true on success, +// false on failure. +//////////////////////////////////////////////////////////////////// +bool EggJointData:: +do_compute_reparent(int model_index, int n) { + if (_computed_reparent) { + // We've already done this joint. This is possible because we + // have to recursively compute joints upwards, so we might visit + // the same joint more than once. + return _computed_ok; + } + _computed_reparent = true; + + if (_parent == _new_parent) { + // Trivial (and most common) case: we are not moving the joint. + // No recomputation necessary. + _computed_ok = true; + return true; + } + + EggBackPointer *back = get_model(model_index); + if (back == (EggBackPointer *)NULL) { + // This joint doesn't have any data to modify. + _computed_ok = true; + return true; + } + + EggJointPointer *joint; + DCAST_INTO_R(joint, back, false); + + LMatrix4d transform; + if (_parent == (EggJointData *)NULL) { + // We are moving from outside the joint hierarchy to within it. + transform = _new_parent->get_new_net_frame_inv(model_index, n); + + } else if (_new_parent == (EggJointData *)NULL) { + // We are moving from within the hierarchy to outside it. + transform = _parent->get_new_net_frame(model_index, n); + + } else { + // We are changing parents within the hierarchy. + transform = + _parent->get_new_net_frame(model_index, n) * + _new_parent->get_new_net_frame_inv(model_index, n); + } + + nassertr(n == joint->get_num_rebuild_frames(), false); + + _computed_ok = joint->add_rebuild_frame(joint->get_frame(n) * transform); + return _computed_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::do_finish_reparent +// Access: Protected +// Description: Performs the actual reparenting operation +// by removing all of the old children and replacing +// them with the set of new children. Returns true on +// success, false on failure. +//////////////////////////////////////////////////////////////////// +bool EggJointData:: +do_finish_reparent() { + bool all_ok = true; + + int num_models = get_num_models(); + for (int model_index = 0; model_index < num_models; model_index++) { + EggJointPointer *parent_joint = NULL; + if (_new_parent != NULL && _new_parent->has_model(model_index)) { + DCAST_INTO_R(parent_joint, _new_parent->get_model(model_index), false); + } + + if (has_model(model_index)) { + EggJointPointer *joint; + DCAST_INTO_R(joint, get_model(model_index), false); + joint->do_finish_reparent(parent_joint); + if (!joint->do_rebuild()) { + all_ok = false; + } + } + } + + _parent = _new_parent; + if (_parent != (EggJointData *)NULL) { + _parent->_children.push_back(this); + } + + return all_ok; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_new_net_frame +// Access: Private +// Description: Similar to get_net_frame(), but computed for the +// prospective new parentage of the node, before +// do_finish_reparent() is called. This is generally +// useful only when called within do_compute_reparent(). +//////////////////////////////////////////////////////////////////// +const LMatrix4d &EggJointData:: +get_new_net_frame(int model_index, int n) { + if (!_got_new_net_frame) { + _new_net_frame = get_new_frame(model_index, n); + if (_new_parent != (EggJointData *)NULL) { + _new_net_frame = _new_net_frame * _new_parent->get_new_net_frame(model_index, n); + } + _got_new_net_frame = true; + } + return _new_net_frame; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_new_net_frame_inv +// Access: Private +// Description: Returns the inverse of get_new_net_frame(). +//////////////////////////////////////////////////////////////////// +const LMatrix4d &EggJointData:: +get_new_net_frame_inv(int model_index, int n) { + if (!_got_new_net_frame_inv) { + _new_net_frame_inv.invert_from(get_new_frame(model_index, n)); + if (_new_parent != (EggJointData *)NULL) { + _new_net_frame_inv = _new_parent->get_new_net_frame_inv(model_index, n) * _new_net_frame_inv; + } + _got_new_net_frame_inv = true; + } + return _new_net_frame_inv; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_new_frame +// Access: Private +// Description: Returns the local transform matrix corresponding to +// this joint position in the nth frame in the indicated +// model, as it will be when do_finish_reparent() is +// called. +//////////////////////////////////////////////////////////////////// +LMatrix4d EggJointData:: +get_new_frame(int model_index, int n) { + do_compute_reparent(model_index, n); + + EggBackPointer *back = get_model(model_index); + if (back == (EggBackPointer *)NULL) { + return LMatrix4d::ident_mat(); + } + + EggJointPointer *joint; + DCAST_INTO_R(joint, back, LMatrix4d::ident_mat()); + + if (joint->get_num_rebuild_frames() > 0) { + return joint->get_rebuild_frame(n); + } else { + return joint->get_frame(n); + } +} diff --git a/pandatool/src/eggcharbase/eggJointData.h b/pandatool/src/eggcharbase/eggJointData.h index 6342d20c68..f98865f0ab 100644 --- a/pandatool/src/eggcharbase/eggJointData.h +++ b/pandatool/src/eggcharbase/eggJointData.h @@ -43,22 +43,42 @@ public: INLINE EggJointData *get_child(int n) const; EggJointData *find_joint(const string &name); - int get_num_frames(int model_index) const; + virtual int get_num_frames(int model_index) const; LMatrix4d get_frame(int model_index, int n) const; LMatrix4d get_net_frame(int model_index, int n) const; + INLINE void reparent_to(EggJointData *new_parent); + void move_vertices_to(EggJointData *new_owner); + bool do_rebuild(); void optimize(); virtual void add_back_pointer(int model_index, EggObject *egg_object); virtual void write(ostream &out, int indent_level = 0) const; +protected: + void do_begin_reparent(); + void do_begin_compute_reparent(); + bool do_compute_reparent(int model_index, int n); + bool do_finish_reparent(); + +private: + const LMatrix4d &get_new_net_frame(int model_index, int n); + const LMatrix4d &get_new_net_frame_inv(int model_index, int n); + LMatrix4d get_new_frame(int model_index, int n); + + // These are used to cache the above results for optimizing + // do_compute_reparent(). + LMatrix4d _new_net_frame, _new_net_frame_inv; + bool _got_new_net_frame, _got_new_net_frame_inv; + bool _computed_reparent; + bool _computed_ok; + protected: typedef pvector Children; Children _children; EggJointData *_parent; - - friend class EggCharacterCollection; + EggJointData *_new_parent; public: @@ -77,6 +97,9 @@ public: private: static TypeHandle _type_handle; + + friend class EggCharacterCollection; + friend class EggCharacterData; }; #include "eggJointData.I" diff --git a/pandatool/src/eggcharbase/eggJointNodePointer.cxx b/pandatool/src/eggcharbase/eggJointNodePointer.cxx index e633e5169c..f8c3e9ca98 100644 --- a/pandatool/src/eggcharbase/eggJointNodePointer.cxx +++ b/pandatool/src/eggcharbase/eggJointNodePointer.cxx @@ -35,7 +35,7 @@ EggJointNodePointer:: EggJointNodePointer(EggObject *object) { _joint = DCAST(EggGroup, object); - if (_joint != (EggGroup *)NULL) { + if (_joint != (EggGroup *)NULL && _joint->is_joint()) { // Quietly insist that the joint has a transform, for neatness. If // it does not, give it the identity transform. if (!_joint->has_transform()) { @@ -94,6 +94,46 @@ set_frame(int n, const LMatrix4d &mat) { _joint->set_transform(mat); } +//////////////////////////////////////////////////////////////////// +// Function: EggJointNodePointer::do_finish_reparent +// Access: Protected +// Description: Performs the actual reparenting operation +// by removing the node from its old parent and +// associating it with its new parent, if any. +//////////////////////////////////////////////////////////////////// +void EggJointNodePointer:: +do_finish_reparent(EggJointPointer *new_parent) { + if (new_parent == (EggJointPointer *)NULL) { + // No new parent; unparent the joint. + EggGroupNode *egg_parent = _joint->get_parent(); + if (egg_parent != (EggGroupNode *)NULL) { + egg_parent->remove_child(_joint.p()); + } + + } else { + // Reparent the joint to its new parent (implicitly unparenting it + // from its previous parent). + EggJointNodePointer *new_node = DCAST(EggJointNodePointer, new_parent); + if (new_node->_joint != _joint->get_parent()) { + new_node->_joint->add_child(_joint.p()); + } + } +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointNodePointer::move_vertices_to +// Access: Public, Virtual +// Description: Moves the vertices assigned to this joint into the +// other joint (which should be of the same type). +//////////////////////////////////////////////////////////////////// +void EggJointNodePointer:: +move_vertices_to(EggJointPointer *new_joint) { + EggJointNodePointer *new_node; + DCAST_INTO_V(new_node, new_joint); + + new_node->_joint->steal_vrefs(_joint); +} + //////////////////////////////////////////////////////////////////// // Function: EggJointNodePointer::add_rebuild_frame // Access: Public, Virtual diff --git a/pandatool/src/eggcharbase/eggJointNodePointer.h b/pandatool/src/eggcharbase/eggJointNodePointer.h index 44c0259f62..d7eba74daf 100644 --- a/pandatool/src/eggcharbase/eggJointNodePointer.h +++ b/pandatool/src/eggcharbase/eggJointNodePointer.h @@ -38,6 +38,9 @@ public: virtual LMatrix4d get_frame(int n) const; virtual void set_frame(int n, const LMatrix4d &mat); + virtual void do_finish_reparent(EggJointPointer *new_parent); + virtual void move_vertices_to(EggJointPointer *new_joint); + virtual bool add_rebuild_frame(const LMatrix4d &mat); virtual bool do_rebuild(); diff --git a/pandatool/src/eggcharbase/eggJointPointer.I b/pandatool/src/eggcharbase/eggJointPointer.I new file mode 100644 index 0000000000..d55e1c430f --- /dev/null +++ b/pandatool/src/eggcharbase/eggJointPointer.I @@ -0,0 +1,41 @@ +// Filename: eggJointPointer.I +// Created by: drose (20Jul03) +// +//////////////////////////////////////////////////////////////////// +// +// PANDA 3D SOFTWARE +// Copyright (c) 2001, 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://www.panda3d.org/license.txt . +// +// To contact the maintainers of this program write to +// panda3d@yahoogroups.com . +// +//////////////////////////////////////////////////////////////////// + + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::get_num_rebuild_frames +// Access: Public +// Description: Returns the number of rebuild frames that have been +// added so far. +//////////////////////////////////////////////////////////////////// +INLINE int EggJointPointer:: +get_num_rebuild_frames() const { + return _rebuild_frames.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::get_rebuild_frame +// Access: Public +// Description: Returns the nth matrix that has been added to the set +// of rebuild frames. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggJointPointer:: +get_rebuild_frame(int n) const { + nassertr(n >= 0 && n < (int)_rebuild_frames.size(), LMatrix4d::ident_mat()); + return _rebuild_frames[n]; +} diff --git a/pandatool/src/eggcharbase/eggJointPointer.cxx b/pandatool/src/eggcharbase/eggJointPointer.cxx index 49b94fb1ea..2005b05713 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.cxx +++ b/pandatool/src/eggcharbase/eggJointPointer.cxx @@ -34,6 +34,16 @@ add_frame(const LMatrix4d &) { return false; } +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::move_vertices_to +// Access: Public, Virtual +// Description: Moves the vertices assigned to this joint into the +// other joint (which should be of the same type). +//////////////////////////////////////////////////////////////////// +void EggJointPointer:: +move_vertices_to(EggJointPointer *) { +} + //////////////////////////////////////////////////////////////////// // Function: EggJointPointer::begin_rebuild // Access: Public diff --git a/pandatool/src/eggcharbase/eggJointPointer.h b/pandatool/src/eggcharbase/eggJointPointer.h index 53e654d9c2..966d3dcb44 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.h +++ b/pandatool/src/eggcharbase/eggJointPointer.h @@ -41,8 +41,13 @@ public: virtual void set_frame(int n, const LMatrix4d &mat)=0; virtual bool add_frame(const LMatrix4d &mat); + virtual void do_finish_reparent(EggJointPointer *new_parent)=0; + virtual void move_vertices_to(EggJointPointer *new_joint); + void begin_rebuild(); virtual bool add_rebuild_frame(const LMatrix4d &mat); + INLINE int get_num_rebuild_frames() const; + INLINE const LMatrix4d &get_rebuild_frame(int n) const; virtual bool do_rebuild(); virtual void optimize(); @@ -69,6 +74,8 @@ private: static TypeHandle _type_handle; }; +#include "eggJointPointer.I" + #endif diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx index 84504f1432..5c078902df 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.cxx @@ -86,6 +86,10 @@ get_frame(int n) const { // If we have exactly one frame, then we have as many frames as we // want; just repeat the first frame. n = 0; + + } else if (get_num_frames() == 0) { + // If we have no frames, we really have the identity matrix. + return LMatrix4d::ident_mat(); } nassertr(n >= 0 && n < get_num_frames(), LMatrix4d::ident_mat()); @@ -122,6 +126,32 @@ add_frame(const LMatrix4d &mat) { return _xform->add_data(mat); } +//////////////////////////////////////////////////////////////////// +// Function: EggMatrixTablePointer::do_finish_reparent +// Access: Protected +// Description: Performs the actual reparenting operation +// by removing the node from its old parent and +// associating it with its new parent, if any. +//////////////////////////////////////////////////////////////////// +void EggMatrixTablePointer:: +do_finish_reparent(EggJointPointer *new_parent) { + if (new_parent == (EggJointPointer *)NULL) { + // No new parent; unparent the joint. + EggGroupNode *egg_parent = _table->get_parent(); + if (egg_parent != (EggGroupNode *)NULL) { + egg_parent->remove_child(_table.p()); + } + + } else { + // Reparent the joint to its new parent (implicitly unparenting it + // from its previous parent). + EggMatrixTablePointer *new_node = DCAST(EggMatrixTablePointer, new_parent); + if (new_node->_table != _table->get_parent()) { + new_node->_table->add_child(_table.p()); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggMatrixTablePointer::do_rebuild // Access: Public, Virtual diff --git a/pandatool/src/eggcharbase/eggMatrixTablePointer.h b/pandatool/src/eggcharbase/eggMatrixTablePointer.h index c903d17c3d..01afab1a9a 100644 --- a/pandatool/src/eggcharbase/eggMatrixTablePointer.h +++ b/pandatool/src/eggcharbase/eggMatrixTablePointer.h @@ -43,6 +43,8 @@ public: virtual void set_frame(int n, const LMatrix4d &mat); virtual bool add_frame(const LMatrix4d &mat); + virtual void do_finish_reparent(EggJointPointer *new_parent); + virtual bool do_rebuild(); virtual void optimize(); diff --git a/pandatool/src/eggcharbase/eggSliderData.cxx b/pandatool/src/eggcharbase/eggSliderData.cxx index 4114fde6f8..56146b468e 100644 --- a/pandatool/src/eggcharbase/eggSliderData.cxx +++ b/pandatool/src/eggcharbase/eggSliderData.cxx @@ -42,7 +42,7 @@ EggSliderData(EggCharacterCollection *collection, //////////////////////////////////////////////////////////////////// // Function: EggSliderData::get_num_frames -// Access: Public +// Access: Public, Virtual // Description: Returns the number of frames of animation for this // particular slider in the indicated model. //////////////////////////////////////////////////////////////////// diff --git a/pandatool/src/eggcharbase/eggSliderData.h b/pandatool/src/eggcharbase/eggSliderData.h index 33d6e7a9f0..f6edec7d78 100644 --- a/pandatool/src/eggcharbase/eggSliderData.h +++ b/pandatool/src/eggcharbase/eggSliderData.h @@ -37,7 +37,7 @@ public: EggSliderData(EggCharacterCollection *collection, EggCharacterData *char_data); - int get_num_frames(int model_index) const; + virtual int get_num_frames(int model_index) const; double get_frame(int model_index, int n) const; virtual void add_back_pointer(int model_index, EggObject *egg_object);