Panda3D
eggRetargetAnim.cxx
1 // Filename: eggRetargetAnim.cxx
2 // Created by: drose (05May05)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "eggRetargetAnim.h"
16 
17 #include "dcast.h"
18 #include "eggJointData.h"
19 #include "eggCharacterCollection.h"
20 #include "eggCharacterData.h"
21 #include "eggCharacterDb.h"
22 #include "eggJointPointer.h"
23 #include "eggTable.h"
24 #include "compose_matrix.h"
25 #include "pystub.h"
26 
27 ////////////////////////////////////////////////////////////////////
28 // Function: EggRetargetAnim::Constructor
29 // Access: Public
30 // Description:
31 ////////////////////////////////////////////////////////////////////
32 EggRetargetAnim::
33 EggRetargetAnim() {
34  add_path_replace_options();
35  add_path_store_options();
36 
37  set_program_brief("remove transformations from animation data in .egg files");
38  set_program_description
39  ("egg-retarget-anim reads a character model and its associated animation "
40  "files, and removes the translations and scales from the animation "
41  "files, replacing them with the translations and scales from the "
42  "rest position of the character model.\n\n"
43 
44  "This allows an animation that was generated for a model with one "
45  "skeleton to be played successfully on a model with a different "
46  "skeleton, provided that both skeletons have the same hierarchy and "
47  "differ only in scales and/or translations of the various joints, "
48  "and that scales and translations are not part of the per-frame "
49  "animations.");
50 
51  add_option
52  ("r", "file.egg", 0,
53  "Read the reference model from the indicated egg file. All of the "
54  "animations will be retargeted to match the indicated file.",
55  &EggRetargetAnim::dispatch_filename, NULL, &_reference_filename);
56 
57  add_option
58  ("keep", "joint[,joint...]", 0,
59  "Preserve the full animation on the named joint(s). This is especially "
60  "appropriate for the root joint.",
61  &EggRetargetAnim::dispatch_vector_string_comma, NULL, &_keep_joints);
62 }
63 
64 ////////////////////////////////////////////////////////////////////
65 // Function: EggRetargetAnim::run
66 // Access: Public
67 // Description:
68 ////////////////////////////////////////////////////////////////////
69 void EggRetargetAnim::
70 run() {
71  nassertv(_collection != (EggCharacterCollection *)NULL);
72  nassertv(_collection->get_num_eggs() > 0);
73 
74  if (_reference_filename.empty()) {
75  nout << "No reference filename specified.\n";
76  exit(1);
77  }
78 
79  int num_characters = _collection->get_num_characters();
80  if (num_characters != 1) {
81  nout << "All animations must have the same character name.\n";
82  exit(1);
83  }
84 
85  // Read in the extra egg file that we use for extracting the
86  // references out.
87  PT(EggData) reference_egg = read_egg(_reference_filename);
88  if (reference_egg == (EggData *)NULL) {
89  nout << "Cannot read " << _reference_filename << "\n";
90  exit(1);
91  }
92 
93  // First, we add it to a separate EggCharacterCollection, so we can
94  // figure out its name.
96  if (col.add_egg(reference_egg) < 0) {
97  nout << _reference_filename
98  << " does not contain a character model or animation reference.\n";
99  exit(1);
100  }
101 
102  if (col.get_num_characters() != 1) {
103  nout << "Reference model must contain only one character.\n";
104  exit(1);
105  }
106 
107  string ref_name = col.get_character(0)->get_name();
108 
109  // Now rename all of the animations to the same name as the
110  // reference model, and add the reference animation in to the same
111  // collection to match it up joint-for-joint.
112  _collection->rename_char(0, ref_name);
113  int reference_egg_index = _collection->add_egg(reference_egg);
114  nassertv(reference_egg_index > 0);
115  nassertv(_collection->get_num_characters() == 1);
116 
117  int reference_model = _collection->get_first_model_index(reference_egg_index);
118  EggCharacterData *char_data = _collection->get_character(0);
119  nout << "Processing " << char_data->get_name() << "\n";
120 
121  typedef pset<string> Names;
122  Names keep_names;
123 
124  vector_string::const_iterator si;
125  for (si = _keep_joints.begin(); si != _keep_joints.end(); ++si) {
126  keep_names.insert(*si);
127  }
128 
129  EggCharacterDb db;
130  EggJointData *root_joint = char_data->get_root_joint();
131  retarget_anim(char_data, root_joint, reference_model, keep_names, db);
132  root_joint->do_rebuild_all(db);
133 
134  write_eggs();
135 }
136 
137 ////////////////////////////////////////////////////////////////////
138 // Function: EggRetargetAnim::retarget_anim
139 // Access: Public
140 // Description: Recursively replaces the scale and translate
141 // information on all of the joints in the char_data
142 // hierarchy wiht this from reference_char.
143 ////////////////////////////////////////////////////////////////////
146  int reference_model, const pset<string> &keep_names,
147  EggCharacterDb &db) {
148  if (keep_names.find(joint_data->get_name()) != keep_names.end()) {
149  // Don't retarget this joint; keep the translation and scale and whatever.
150 
151  } else {
152  // Retarget this joint.
153  int num_models = joint_data->get_num_models();
154  for (int i = 0; i < num_models; i++) {
155  if (joint_data->has_model(i)) {
156  int num_frames = char_data->get_num_frames(i);
157 
158  EggBackPointer *back = joint_data->get_model(i);
159  nassertv(back != (EggBackPointer *)NULL);
160  EggJointPointer *joint;
161  DCAST_INTO_V(joint, back);
162 
163  LMatrix4d ref = joint_data->get_frame(reference_model, 0);
164  LVecBase3d ref_scale, ref_shear, ref_hpr, ref_translate;
165  if (!decompose_matrix(ref, ref_scale, ref_shear, ref_hpr, ref_translate)) {
166  nout << "Could not decompose rest frame for "
167  << joint_data->get_name() << "\n";
168  } else {
169  int f;
170  for (f = 0; f < num_frames; f++) {
171  LMatrix4d mat = joint_data->get_frame(i, f);
172 
173  LVecBase3d scale, shear, hpr, translate;
174  if (decompose_matrix(mat, scale, shear, hpr, translate)) {
175  compose_matrix(mat, ref_scale, ref_shear, hpr, ref_translate);
176  } else {
177  nout << "Could not decompose matrix for " << joint_data->get_name()
178  << "\n";
179  }
180 
181  db.set_matrix(joint, EggCharacterDb::TT_rebuild_frame,
182  f, mat);
183  }
184  }
185  }
186  }
187  }
188 
189  int num_children = joint_data->get_num_children();
190  for (int i = 0; i < num_children; i++) {
191  EggJointData *next_joint_data = joint_data->get_child(i);
192  retarget_anim(char_data, next_joint_data, reference_model, keep_names, db);
193  }
194 }
195 
196 
197 int main(int argc, char *argv[]) {
198  // A call to pystub() to force libpystub.so to be linked in.
199  pystub();
200 
201  EggRetargetAnim prog;
202  prog.parse_command_line(argc, argv);
203  prog.run();
204  return 0;
205 }
void retarget_anim(EggCharacterData *char_data, EggJointData *joint_data, int reference_model, const pset< string > &keep_names, EggCharacterDb &db)
Recursively replaces the scale and translate information on all of the joints in the char_data hierar...
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
int get_num_frames(int model_index) const
Returns the number of frames of animation of the indicated model.
virtual void parse_command_line(int argc, char **argv)
Dispatches on each of the options on the command line, and passes the remaining parameters to handle_...
int get_num_models() const
Returns the maximum number of back pointers this component may have.
int add_egg(EggData *egg)
Adds a new egg file to the list of models and animation files for this particular character...
EggBackPointer * get_model(int model_index) const
Returns the back pointer to an egg file for the indicated model if it exists, or NULL if it does not...
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
int get_num_characters() const
Returns the number of separate Characters that have been discovered in the various egg files added to...
This class is used during joint optimization or restructuring to store the table of interim joint com...
Retargets one or more animation files from one particular skeleton to a similar, but differently scal...
void set_matrix(const EggJointPointer *joint, TableType type, int frame, const LMatrix4d &mat)
Stores the matrix for the indicated joint, type, and frame in the database.
This is a base class for EggJointNodePointer and EggMatrixTablePointer.
EggCharacterData * get_character(int i) const
Returns the ith character in the collection.
Represents a set of characters, as read and collected from possibly several model and/or animation eg...
This is the base class for all three-component vectors and points.
Definition: lvecBase3.h:1471
bool has_model(int model_index) const
Returns true if the component has a back pointer to an egg file somewhere for the indicated model...
Represents a single character, as read and collected from several models and animation files...
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
Definition: eggJointData.h:34
LMatrix4d get_frame(int model_index, int n) const
Returns the local transform matrix corresponding to this joint position in the nth frame in the indic...
bool do_rebuild_all(EggCharacterDb &db)
Calls do_rebuild() on all models, and recursively on all joints at this node and below.
This stores a pointer from an EggJointData or EggSliderData object back to the referencing data in an...