From 0c2a4f1550d35c1d6ce6513e5099b1367be08fdf Mon Sep 17 00:00:00 2001 From: David Rose Date: Sun, 31 Aug 2003 01:40:38 +0000 Subject: [PATCH] add -optimal to egg-optchar --- pandatool/src/egg-optchar/eggOptchar.cxx | 77 +++++- pandatool/src/egg-optchar/eggOptchar.h | 3 +- pandatool/src/eggcharbase/Sources.pp | 1 + .../src/eggcharbase/eggCharacterData.cxx | 55 +++++ pandatool/src/eggcharbase/eggCharacterData.h | 1 + pandatool/src/eggcharbase/eggJointData.cxx | 222 +++++++++++++++++- pandatool/src/eggcharbase/eggJointData.h | 3 + .../src/eggcharbase/eggJointNodePointer.cxx | 11 +- pandatool/src/eggcharbase/eggJointPointer.I | 90 +++++++ pandatool/src/eggcharbase/eggJointPointer.h | 12 + 10 files changed, 448 insertions(+), 27 deletions(-) diff --git a/pandatool/src/egg-optchar/eggOptchar.cxx b/pandatool/src/egg-optchar/eggOptchar.cxx index 4e2376061a..07ab788f73 100644 --- a/pandatool/src/egg-optchar/eggOptchar.cxx +++ b/pandatool/src/egg-optchar/eggOptchar.cxx @@ -32,6 +32,7 @@ #include "dcast.h" #include "pset.h" #include "compose_matrix.h" +#include "fftCompressor.h" #include @@ -119,6 +120,21 @@ EggOptchar() { "is not affected (the effect is similar to NodePath::wrt_reparent_to).", &EggOptchar::dispatch_vector_string_pair, NULL, &_reparent_joints); + if (FFTCompressor::is_compression_available()) { + add_option + ("optimal", "", 0, + "Computes the optimal joint hierarchy for the character by analyzing " + "all of the joint animation and reparenting joints to minimize " + "transformations. This can repair skeletons that have been flattened " + "or whose hierarchy was otherwise damaged in conversion; it can also " + "detect joints that are constrained to follow other joints and should " + "therefore be parented to the master joints. The result is a file " + "from which more joints may be successfully removed, that generally " + "compresses better and with fewer artifacts. However, this is a " + "fairly expensive operation.", + &EggOptchar::dispatch_none, &_optimal_hierarchy); + } + add_option ("q", "quantum", 0, "Quantize joint membership values to the given unit. This is " @@ -127,6 +143,7 @@ EggOptchar() { "values.", &EggOptchar::dispatch_double, NULL, &_vref_quantum); + _optimal_hierarchy = false; _vref_quantum = 0.01; } @@ -174,7 +191,8 @@ run() { 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()); + int col = 0; + list_joints_p(char_data->get_root_joint(), col); // A newline to cout is needed after the above call. cout << "\n"; nout << char_data->get_num_joints() << " joints.\n"; @@ -390,10 +408,14 @@ determine_removed_components() { for (int ci = 0; ci < num_characters; ci++) { EggCharacterData *char_data = _collection->get_character(ci); int num_components = char_data->get_num_components(); + cerr << char_data->get_name() << " has " << num_components << " components.\n"; for (int i = 0; i < num_components; i++) { EggComponentData *comp_data = char_data->get_component(i); + nassertv(comp_data != (EggComponentData *)NULL); + EggOptcharUserData *user_data = DCAST(EggOptcharUserData, comp_data->get_user_data()); + nassertv(user_data != (EggOptcharUserData *)NULL); const string &name = comp_data->get_name(); if (_keep_all || keep_names.find(name) != keep_names.end()) { @@ -469,9 +491,11 @@ move_vertices() { joint_data->move_vertices_to(best_joint); // Now we can't remove the joint. - EggOptcharUserData *best_user_data = - DCAST(EggOptcharUserData, best_joint->get_user_data()); - best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove); + if (best_joint != (EggJointData *)NULL) { + EggOptcharUserData *best_user_data = + DCAST(EggOptcharUserData, best_joint->get_user_data()); + best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove); + } } } } @@ -488,6 +512,7 @@ move_vertices() { //////////////////////////////////////////////////////////////////// bool EggOptchar:: process_joints() { + cerr << "process_joints\n"; bool removed_any = false; int num_characters = _collection->get_num_characters(); for (int ci = 0; ci < num_characters; ci++) { @@ -594,14 +619,16 @@ find_best_parent(EggJointData *joint_data) const { //////////////////////////////////////////////////////////////////// EggJointData *EggOptchar:: find_best_vertex_joint(EggJointData *joint_data) const { + if (joint_data == (EggJointData *)NULL) { + return NULL; + } + EggOptcharUserData *user_data = DCAST(EggOptcharUserData, joint_data->get_user_data()); if ((user_data->_flags & EggOptcharUserData::F_static) != 0) { // Keep going. - if (joint_data->get_parent() != (EggJointData *)NULL) { - return find_best_vertex_joint(joint_data->get_parent()); - } + return find_best_vertex_joint(joint_data->get_parent()); } // This is the one! @@ -618,6 +645,7 @@ find_best_vertex_joint(EggJointData *joint_data) const { bool EggOptchar:: apply_user_reparents() { bool did_anything = false; + int num_characters = _collection->get_num_characters(); StringPairs::const_iterator spi; @@ -645,6 +673,18 @@ apply_user_reparents() { } } + if (_optimal_hierarchy) { + did_anything = true; + for (int ci = 0; ci < num_characters; ci++) { + EggCharacterData *char_data = _collection->get_character(ci); + nout << "Computing optimal hierarchy for " + << char_data->get_name() << ".\n"; + char_data->choose_optimal_hierarchy(); + nout << "Done computing optimal hierarchy for " + << char_data->get_name() << ".\n"; + } + } + return did_anything; } @@ -849,19 +889,34 @@ list_joints(EggJointData *joint_data, int indent_level, bool verbose) { // -p joint,parent commands. //////////////////////////////////////////////////////////////////// void EggOptchar:: -list_joints_p(EggJointData *joint_data) { +list_joints_p(EggJointData *joint_data, int &col) { // As above, don't list the root joint. int num_children = joint_data->get_num_children(); + static const int max_col = 72; + 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); + string text = string(" -p ") + child_data->get_name() + + string(",") + joint_data->get_name(); + if (col == 0) { + cout << " " << text; + col = 4 + text.length(); + } else { + col += text.length(); + if (col >= max_col) { + cout << " \\\n " << text; + col = 4 + text.length(); + } else { + cout << text; + } + } + + list_joints_p(child_data, col); } } diff --git a/pandatool/src/egg-optchar/eggOptchar.h b/pandatool/src/egg-optchar/eggOptchar.h index 7bf5494b34..00e0bdd697 100644 --- a/pandatool/src/egg-optchar/eggOptchar.h +++ b/pandatool/src/egg-optchar/eggOptchar.h @@ -67,7 +67,7 @@ private: void analyze_joints(EggJointData *joint_data); void analyze_sliders(EggCharacterData *char_data); void list_joints(EggJointData *joint_data, int indent_level, bool verbose); - void list_joints_p(EggJointData *joint_data); + void list_joints_p(EggJointData *joint_data, int &col); void list_scalars(EggCharacterData *char_data, bool verbose); void describe_component(EggComponentData *comp_data, int indent_level, bool verbose); @@ -108,6 +108,7 @@ private: typedef pvector FlagGroups; FlagGroups _flag_groups; + bool _optimal_hierarchy; double _vref_quantum; }; diff --git a/pandatool/src/eggcharbase/Sources.pp b/pandatool/src/eggcharbase/Sources.pp index 90478dcddb..dac8480d7f 100644 --- a/pandatool/src/eggcharbase/Sources.pp +++ b/pandatool/src/eggcharbase/Sources.pp @@ -4,6 +4,7 @@ eggbase progbase #define OTHER_LIBS \ egg:c panda:m + #define USE_PACKAGES zlib #define COMBINED_SOURCES $[TARGET]_composite1.cxx diff --git a/pandatool/src/eggcharbase/eggCharacterData.cxx b/pandatool/src/eggcharbase/eggCharacterData.cxx index 54bd999409..777df5ad16 100644 --- a/pandatool/src/eggcharbase/eggCharacterData.cxx +++ b/pandatool/src/eggcharbase/eggCharacterData.cxx @@ -238,6 +238,61 @@ do_reparent() { return invalid_set.empty(); } +//////////////////////////////////////////////////////////////////// +// Function: EggCharacterData::choose_optimal_hierarchy +// Access: Public +// Description: Chooses the best possible parent joint for each of +// the joints in the hierarchy, based on the score +// computed by EggJointData::score_reparent_to(). This +// is a fairly expensive operation that involves lots of +// recomputing of transforms across the hierarchy. +// +// The joints are not actually reparented yet, but the +// new_parent of each joint is set. Call do_reparent() +// to actually perform the suggested reparenting +// operation. +//////////////////////////////////////////////////////////////////// +void EggCharacterData:: +choose_optimal_hierarchy() { + Joints::const_iterator ji, jj; + for (ji = _joints.begin(); ji != _joints.end(); ++ji) { + EggJointData *joint_data = (*ji); + + EggJointData *best_parent = joint_data->get_parent(); + int best_score = joint_data->score_reparent_to(best_parent); + + for (jj = _joints.begin(); jj != _joints.end(); ++jj) { + EggJointData *possible_parent = (*jj); + if (possible_parent != joint_data && possible_parent != best_parent && + !joint_data->is_new_ancestor(possible_parent)) { + + int score = joint_data->score_reparent_to(possible_parent); + if (score >= 0 && (best_score < 0 || score < best_score)) { + best_parent = possible_parent; + best_score = score; + } + } + } + + // Also consider reparenting the node to the root. + EggJointData *possible_parent = get_root_joint(); + if (possible_parent != best_parent) { + int score = joint_data->score_reparent_to(possible_parent); + if (score >= 0 && (best_score < 0 || score < best_score)) { + best_parent = possible_parent; + best_score = score; + } + } + + if (best_parent != (EggJointData *)NULL && + best_parent != joint_data->_parent) { + nout << "best parent for " << joint_data->get_name() << " is " + << best_parent->get_name() << "\n"; + joint_data->reparent_to(best_parent); + } + } +} + //////////////////////////////////////////////////////////////////// // Function: EggCharacterData::find_slider // Access: Public diff --git a/pandatool/src/eggcharbase/eggCharacterData.h b/pandatool/src/eggcharbase/eggCharacterData.h index b411bb5f0d..a84c4353c2 100644 --- a/pandatool/src/eggcharbase/eggCharacterData.h +++ b/pandatool/src/eggcharbase/eggCharacterData.h @@ -79,6 +79,7 @@ public: INLINE int get_num_joints() const; INLINE EggJointData *get_joint(int n) const; bool do_reparent(); + void choose_optimal_hierarchy(); INLINE int get_num_sliders() const; INLINE EggSliderData *get_slider(int n) const; diff --git a/pandatool/src/eggcharbase/eggJointData.cxx b/pandatool/src/eggcharbase/eggJointData.cxx index 39d40a06b0..2120988233 100644 --- a/pandatool/src/eggcharbase/eggJointData.cxx +++ b/pandatool/src/eggcharbase/eggJointData.cxx @@ -24,6 +24,8 @@ #include "eggGroup.h" #include "eggTable.h" #include "indent.h" +#include "fftCompressor.h" +#include "zStream.h" TypeHandle EggJointData::_type_handle; @@ -73,11 +75,61 @@ get_frame(int model_index, int n) const { //////////////////////////////////////////////////////////////////// LMatrix4d EggJointData:: get_net_frame(int model_index, int n) const { - LMatrix4d mat = get_frame(model_index, n); - if (_parent != (EggJointData *)NULL) { - mat = mat * _parent->get_net_frame(model_index, n); + EggBackPointer *back = get_model(model_index); + if (back == (EggBackPointer *)NULL) { + return LMatrix4d::ident_mat(); } - return mat; + + EggJointPointer *joint; + DCAST_INTO_R(joint, back, LMatrix4d::ident_mat()); + + if (joint->get_num_net_frames() < n) { + // Recursively get the previous frame's net, so we have a place to + // stuff this frame's value. + get_net_frame(model_index, n - 1); + } + + if (joint->get_num_net_frames() == n) { + // Compute this frame's net, and stuff it in. + LMatrix4d mat = get_frame(model_index, n); + if (_parent != (EggJointData *)NULL) { + mat = mat * _parent->get_net_frame(model_index, n); + } + joint->add_net_frame(mat); + } + + return joint->get_net_frame(n); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::get_net_frame_inv +// Access: Public +// Description: Returns the inverse of get_net_frame(). +//////////////////////////////////////////////////////////////////// +LMatrix4d EggJointData:: +get_net_frame_inv(int model_index, int n) const { + 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_net_frame_invs() < n) { + // Recursively get the previous frame's net, so we have a place to + // stuff this frame's value. + get_net_frame_inv(model_index, n - 1); + } + + if (joint->get_num_net_frame_invs() == n) { + // Compute this frame's net inverse, and stuff it in. + LMatrix4d mat = get_net_frame(model_index, n); + mat.invert_in_place(); + joint->add_net_frame_inv(mat); + } + + return joint->get_net_frame_inv(n); } //////////////////////////////////////////////////////////////////// @@ -117,16 +169,140 @@ force_initial_rest_frame() { 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); + + if (new_owner == (EggJointData *)NULL) { + 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->move_vertices_to((EggJointPointer *)NULL); + } + } + } else { + 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::score_reparent +// Access: Public +// Description: Computes a score >= 0 reflecting the similarity of +// the current joint's animation (in world space) to +// that of the indicated potential parent joint (in +// world space). The lower the number, the more similar +// the motion, and the more suitable is the proposed +// parent-child relationship. Returns -1 if there is an +// error. +//////////////////////////////////////////////////////////////////// +int EggJointData:: +score_reparent_to(EggJointData *new_parent) { + if (!FFTCompressor::is_compression_available()) { + // If we don't have compression compiled in, we can't meaningfully + // score the joints. + return -1; + } + + // First, build up a big array of the new transforms this joint + // would receive in all frames of all models, were it reparented to + // the indicated joint. + vector_float i, j, k, a, b, c, x, y, z; + vector_LVecBase3f hprs; + int num_rows = 0; + + int num_models = get_num_models(); + for (int model_index = 0; model_index < num_models; model_index++) { + EggBackPointer *back = get_model(model_index); + if (back != (EggBackPointer *)NULL) { + EggJointPointer *joint; + DCAST_INTO_R(joint, back, false); + + int num_frames = get_num_frames(model_index); + for (int n = 0; n < num_frames; n++) { + LMatrix4d transform; + if (_parent == new_parent) { + // We already have this parent. + transform = LMatrix4d::ident_mat(); + + } else if (_parent == (EggJointData *)NULL) { + // We are moving from outside the joint hierarchy to within it. + transform = new_parent->get_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_net_frame(model_index, n); + + } else { + // We are changing parents within the hierarchy. + transform = + _parent->get_net_frame(model_index, n) * + new_parent->get_net_frame_inv(model_index, n); + } + + transform = joint->get_frame(n) * transform; + LVecBase3d scale, shear, hpr, translate; + if (!decompose_matrix(transform, scale, shear, hpr, translate)) { + // Invalid transform. + return -1; + } + i.push_back(scale[0]); + j.push_back(scale[1]); + k.push_back(scale[2]); + a.push_back(shear[0]); + b.push_back(shear[1]); + c.push_back(shear[2]); + hprs.push_back(LCAST(float, hpr)); + x.push_back(translate[0]); + y.push_back(translate[1]); + z.push_back(translate[2]); + num_rows++; + } + } + } + + if (num_rows == 0) { + // No data, no score. + return -1; + } + + // Now, we derive a score, by the simple expedient of using the + // FFTCompressor to compress the generated transforms, and measuring + // the length of the resulting bitstream. + FFTCompressor compressor; + Datagram dg; + compressor.write_reals(dg, &i[0], num_rows); + compressor.write_reals(dg, &j[0], num_rows); + compressor.write_reals(dg, &k[0], num_rows); + compressor.write_reals(dg, &a[0], num_rows); + compressor.write_reals(dg, &b[0], num_rows); + compressor.write_reals(dg, &c[0], num_rows); + compressor.write_hprs(dg, &hprs[0], num_rows); + compressor.write_reals(dg, &x[0], num_rows); + compressor.write_reals(dg, &y[0], num_rows); + compressor.write_reals(dg, &z[0], num_rows); + + +#ifndef HAVE_ZLIB + return dg.get_length(); + +#else + // The FFTCompressor does minimal run-length encoding, but to really + // get an accurate measure we should zlib-compress the resulting + // stream. + ostringstream sstr; + OCompressStream zstr(&sstr, false); + zstr.write((const char *)dg.get_data(), dg.get_length()); + zstr.flush(); + return sstr.str().length(); +#endif +} + //////////////////////////////////////////////////////////////////// // Function: EggJointData::do_rebuild // Access: Public @@ -363,12 +539,12 @@ do_compute_reparent(int model_index, int 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); + transform = _parent->get_net_frame(model_index, n); } else { // We are changing parents within the hierarchy. transform = - _parent->get_new_net_frame(model_index, n) * + _parent->get_net_frame(model_index, n) * _new_parent->get_new_net_frame_inv(model_index, n); } @@ -401,6 +577,7 @@ do_finish_reparent() { EggJointPointer *joint; DCAST_INTO_R(joint, get_model(model_index), false); joint->do_finish_reparent(parent_joint); + joint->clear_net_frames(); if (!joint->do_rebuild()) { all_ok = false; } @@ -462,6 +639,27 @@ find_joint_matches(const string &name) { return (EggJointData *)NULL; } +//////////////////////////////////////////////////////////////////// +// Function: EggJointData::is_new_ancestor +// Access: Protected +// Description: Returns true if this joint is an ancestor of the +// indicated joint, in the "new" hierarchy (that is, the +// one defined by _new_parent, as set by reparent_to() +// before do_finish_reparent() is called). +//////////////////////////////////////////////////////////////////// +bool EggJointData:: +is_new_ancestor(EggJointData *child) const { + if (child == this) { + return true; + } + + if (child->_new_parent == (EggJointData *)NULL) { + return false; + } + + return is_new_ancestor(child->_new_parent); +} + //////////////////////////////////////////////////////////////////// // Function: EggJointData::get_new_net_frame // Access: Private diff --git a/pandatool/src/eggcharbase/eggJointData.h b/pandatool/src/eggcharbase/eggJointData.h index 5f1778153d..f909995282 100644 --- a/pandatool/src/eggcharbase/eggJointData.h +++ b/pandatool/src/eggcharbase/eggJointData.h @@ -45,6 +45,7 @@ public: LMatrix4d get_frame(int model_index, int n) const; LMatrix4d get_net_frame(int model_index, int n) const; + LMatrix4d get_net_frame_inv(int model_index, int n) const; INLINE bool has_rest_frame() const; INLINE bool rest_frames_differ() const; @@ -53,6 +54,7 @@ public: INLINE void reparent_to(EggJointData *new_parent); void move_vertices_to(EggJointData *new_owner); + int score_reparent_to(EggJointData *new_parent); bool do_rebuild(); void optimize(); @@ -72,6 +74,7 @@ private: EggJointData *find_joint_exact(const string &name); EggJointData *find_joint_matches(const string &name); + bool is_new_ancestor(EggJointData *child) const; 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); diff --git a/pandatool/src/eggcharbase/eggJointNodePointer.cxx b/pandatool/src/eggcharbase/eggJointNodePointer.cxx index 7038040f00..1b0c499985 100644 --- a/pandatool/src/eggcharbase/eggJointNodePointer.cxx +++ b/pandatool/src/eggcharbase/eggJointNodePointer.cxx @@ -128,10 +128,15 @@ do_finish_reparent(EggJointPointer *new_parent) { //////////////////////////////////////////////////////////////////// void EggJointNodePointer:: move_vertices_to(EggJointPointer *new_joint) { - EggJointNodePointer *new_node; - DCAST_INTO_V(new_node, new_joint); + if (new_joint == (EggJointPointer *)NULL) { + _joint->unref_all_vertices(); - new_node->_joint->steal_vrefs(_joint); + } else { + EggJointNodePointer *new_node; + DCAST_INTO_V(new_node, new_joint); + + new_node->_joint->steal_vrefs(_joint); + } } //////////////////////////////////////////////////////////////////// diff --git a/pandatool/src/eggcharbase/eggJointPointer.I b/pandatool/src/eggcharbase/eggJointPointer.I index d55e1c430f..d210388875 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.I +++ b/pandatool/src/eggcharbase/eggJointPointer.I @@ -39,3 +39,93 @@ get_rebuild_frame(int n) const { nassertr(n >= 0 && n < (int)_rebuild_frames.size(), LMatrix4d::ident_mat()); return _rebuild_frames[n]; } + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::clear_net_frames +// Access: Public +// Description: Resets the cache of net frames for this joint. +//////////////////////////////////////////////////////////////////// +INLINE void EggJointPointer:: +clear_net_frames() { + _net_frames.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::add_net_frame +// Access: Public, Virtual +// Description: Adds a new frame to the set of net frames. This is +// used to cache the net transform from the root for +// this particular joint. +//////////////////////////////////////////////////////////////////// +INLINE void EggJointPointer:: +add_net_frame(const LMatrix4d &mat) { + _net_frames.push_back(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::get_num_net_frames +// Access: Public +// Description: Returns the number of net frames that have been +// added so far. +//////////////////////////////////////////////////////////////////// +INLINE int EggJointPointer:: +get_num_net_frames() const { + return _net_frames.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::get_net_frame +// Access: Public +// Description: Returns the nth matrix that has been added to the set +// of net frames. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggJointPointer:: +get_net_frame(int n) const { + nassertr(n >= 0 && n < (int)_net_frames.size(), LMatrix4d::ident_mat()); + return _net_frames[n]; +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::clear_net_frame_invs +// Access: Public +// Description: Resets the cache of net_inv frames for this joint. +//////////////////////////////////////////////////////////////////// +INLINE void EggJointPointer:: +clear_net_frame_invs() { + _net_frame_invs.clear(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::add_net_frame_inv +// Access: Public, Virtual +// Description: Adds a new frame to the set of net_inv frames. This is +// used to cache the inverse net transform from the root +// for this particular joint. +//////////////////////////////////////////////////////////////////// +INLINE void EggJointPointer:: +add_net_frame_inv(const LMatrix4d &mat) { + _net_frame_invs.push_back(mat); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::get_num_net_frame_invs +// Access: Public +// Description: Returns the number of net_inv frames that have been +// added so far. +//////////////////////////////////////////////////////////////////// +INLINE int EggJointPointer:: +get_num_net_frame_invs() const { + return _net_frame_invs.size(); +} + +//////////////////////////////////////////////////////////////////// +// Function: EggJointPointer::get_net_frame_inv +// Access: Public +// Description: Returns the nth matrix that has been added to the set +// of net_inv frames. +//////////////////////////////////////////////////////////////////// +INLINE const LMatrix4d &EggJointPointer:: +get_net_frame_inv(int n) const { + nassertr(n >= 0 && n < (int)_net_frame_invs.size(), LMatrix4d::ident_mat()); + return _net_frame_invs[n]; +} diff --git a/pandatool/src/eggcharbase/eggJointPointer.h b/pandatool/src/eggcharbase/eggJointPointer.h index 6454d686e7..4297eca835 100644 --- a/pandatool/src/eggcharbase/eggJointPointer.h +++ b/pandatool/src/eggcharbase/eggJointPointer.h @@ -50,6 +50,16 @@ public: INLINE const LMatrix4d &get_rebuild_frame(int n) const; virtual bool do_rebuild(); + INLINE void clear_net_frames(); + INLINE void add_net_frame(const LMatrix4d &mat); + INLINE int get_num_net_frames() const; + INLINE const LMatrix4d &get_net_frame(int n) const; + + INLINE void clear_net_frame_invs(); + INLINE void add_net_frame_inv(const LMatrix4d &mat); + INLINE int get_num_net_frame_invs() const; + INLINE const LMatrix4d &get_net_frame_inv(int n) const; + virtual void optimize(); virtual void expose(EggGroup::DCSType dcs_type); virtual void zero_channels(const string &components); @@ -57,6 +67,8 @@ public: protected: typedef pvector RebuildFrames; RebuildFrames _rebuild_frames; + RebuildFrames _net_frames; + RebuildFrames _net_frame_invs; public: static TypeHandle get_class_type() {