Panda3D
eggTopstrip.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file eggTopstrip.cxx
10  * @author drose
11  * @date 2001-02-23
12  */
13 
14 #include "eggTopstrip.h"
15 
16 #include "dcast.h"
17 #include "eggJointData.h"
18 #include "eggCharacterCollection.h"
19 #include "eggCharacterData.h"
20 #include "eggCharacterDb.h"
21 #include "eggJointPointer.h"
22 #include "eggTable.h"
23 #include "compose_matrix.h"
24 
25 /**
26  *
27  */
28 EggTopstrip::
29 EggTopstrip() {
30  add_path_replace_options();
31  add_path_store_options();
32 
33  set_program_brief("unapplies animation from a joint in an .egg file");
34  set_program_description
35  ("egg-topstrip reads a character model and its associated animation "
36  "files, and unapplies the animation from one of the top joints. "
37  "This effectively freezes that particular joint, and makes the rest "
38  "of the character relative to that joint.\n\n"
39 
40  "This is a particularly useful thing to do to generate character "
41  "models that can stack one on top of the other in a sensible way.");
42 
43  add_option
44  ("t", "name", 0,
45  "Specify the name of the 'top' joint, from which to draw the "
46  "animation channels which will be applied to the entire animation.",
47  &EggTopstrip::dispatch_string, nullptr, &_top_joint_name);
48 
49  add_option
50  ("i", "", 0,
51  "Invert the matrix before applying. This causes a subtractive "
52  "effect. This is the default unless -r is specified.",
53  &EggTopstrip::dispatch_true, &_got_invert_transform, &_invert_transform);
54 
55  add_option
56  ("n", "", 0,
57  "Do not invert the matrix before applying. This causes an "
58  "additive effect.",
59  &EggTopstrip::dispatch_false, &_got_invert_transform, &_invert_transform);
60 
61  add_option
62  ("s", "[ijkphrxyz]", 0,
63  "Specify the components of the transform that are to be applied. Use "
64  "any combination of the nine token letters: i, j, k represent the "
65  "three scale axes; h, p, r represent rotation; and x, y, z represent "
66  "translation. The default is everything: -s ijkphrxyz.",
67  &EggTopstrip::dispatch_string, nullptr, &_transform_channels);
68 
69  add_option
70  ("r", "file.egg", 0,
71  "Read the animation channel from the indicated egg file. If this "
72  "is not specified, each egg file will supply its own animation channel.",
73  &EggTopstrip::dispatch_filename, nullptr, &_channel_filename);
74 
75  _invert_transform = true;
76  _transform_channels = "ijkphrxyz";
77 }
78 
79 /**
80  *
81  */
82 void EggTopstrip::
83 run() {
84  nassertv(_collection != nullptr);
85  nassertv(_collection->get_num_eggs() > 0);
86 
88 
89  // Get the number of characters first, in case adding the _channel_egg
90  // changes this.
91  int num_characters = _collection->get_num_characters();
92 
93  // Determine which model and character we'll be pulling the animation
94  // channels from.
95  int from_model = -1;
96 
97  if (!_channel_filename.empty()) {
98  // Read in the extra egg file that we use for extracting the channels out.
99  PT(EggData) channel_egg = read_egg(_channel_filename);
100  if (channel_egg == nullptr) {
101  nout << "Cannot read " << _channel_filename << "\n";
102  exit(1);
103  }
104  int channel_egg_index = _collection->add_egg(channel_egg);
105  if (channel_egg_index < 0) {
106  nout << _channel_filename
107  << " does not contain a character model or animation channel.\n";
108  exit(1);
109  }
110 
111  from_model = _collection->get_first_model_index(channel_egg_index);
112 
113  if (!_got_invert_transform) {
114  // With -r, the default is not to invert the transform.
115  _invert_transform = false;
116  }
117  }
118 
119  // Now process each character.
120  EggCharacterDb db;
121 
122  int ci;
123  for (ci = 0; ci < num_characters; ci++) {
124  EggCharacterData *char_data = _collection->get_character(ci);
125  nout << "Processing " << char_data->get_name() << "\n";
126 
127  EggJointData *root_joint = char_data->get_root_joint();
128 
129  // We'll read the transform to apply from this character, which will be
130  // the same character unless -r was specified.
131  EggCharacterData *from_char = char_data;
132  if (from_model != -1) {
133  from_char = _collection->get_character_by_model_index(from_model);
134  }
135 
136  // Determine which joint we'll use to extract the transform to apply.
137  EggJointData *top_joint = nullptr;
138  if (_top_joint_name.empty()) {
139  // The default top joint name is the alphabetically first joint in the
140  // top level.
141  if (root_joint->get_num_children() == 0) {
142  nout << "Character " << from_char->get_name() << " has no joints.\n";
143  exit(1);
144  }
145  top_joint = root_joint->get_child(0);
146  } else {
147  top_joint = from_char->find_joint(_top_joint_name);
148  if (top_joint == nullptr) {
149  nout << "Character " << from_char->get_name()
150  << " has no joint named " << _top_joint_name << "\n";
151  exit(1);
152  }
153  }
154 
155  // First, transform all the joints.
156  int num_children = root_joint->get_num_children();
157  for (int i = 0; i < num_children; i++) {
158  EggJointData *joint_data = root_joint->get_child(i);
159  strip_anim(char_data, joint_data, from_model, from_char, top_joint, db);
160  }
161 
162  // We also need to transform the vertices for any models involved here.
163  int num_models = char_data->get_num_models();
164  for (int m = 0; m < num_models; m++) {
165  EggNode *node = char_data->get_model_root(m);
166  if (!node->is_of_type(EggTable::get_class_type())) {
167  strip_anim_vertices(node, char_data->get_model_index(m),
168  from_model, top_joint, db);
169  }
170  }
171  }
172 
173  // Now, trigger the actual rebuilding of all the joint data.
174  for (ci = 0; ci < num_characters; ci++) {
175  EggCharacterData *char_data = _collection->get_character(ci);
176  char_data->get_root_joint()->do_rebuild_all(db);
177  }
178 
179  write_eggs();
180 }
181 
182 /**
183  * Checks the _transform_channels string to ensure that it contains only the
184  * expected nine letters, or a subset.
185  */
186 void EggTopstrip::
188  static std::string expected = "ijkphrxyz";
189  static const int num_channels = 9;
190  bool has_each[num_channels];
191  memset(has_each, 0, num_channels * sizeof(bool));
192 
193  for (size_t p = 0; p < _transform_channels.size(); p++) {
194  int i = expected.find(_transform_channels[p]);
195  if (i == (int)std::string::npos) {
196  nout << "Invalid letter for -s: " << _transform_channels[p] << "\n";
197  exit(1);
198  }
199  nassertv(i < num_channels);
200  has_each[i] = true;
201  }
202 
203  _transform_channels = "";
204  for (int i = 0; i < num_channels; i++) {
205  if (has_each[i]) {
206  _transform_channels += expected[i];
207  }
208  }
209 
210  if (_transform_channels.empty()) {
211  nout << "No transform specified for -s.\n";
212  exit(1);
213  }
214 }
215 
216 
217 /**
218  * Applies the channels from joint _top_joint in model from_model to the joint
219  * referenced by joint_data.
220  */
221 void EggTopstrip::
222 strip_anim(EggCharacterData *char_data, EggJointData *joint_data,
223  int from_model, EggCharacterData *from_char,
224  EggJointData *top_joint, EggCharacterDb &db) {
225  int num_models = joint_data->get_num_models();
226  for (int i = 0; i < num_models; i++) {
227  int model = (from_model < 0) ? i : from_model;
228  if (joint_data->has_model(i)) {
229  if (!top_joint->has_model(model)) {
230  nout << "Warning: Joint " << top_joint->get_name()
231  << " is not defined in all models.\n";
232  return;
233  }
234 
235  int num_into_frames = char_data->get_num_frames(i);
236  int num_from_frames = from_char->get_num_frames(model);
237 
238  int num_frames = std::max(num_into_frames, num_from_frames);
239 
240  EggBackPointer *back = joint_data->get_model(i);
241  nassertv(back != nullptr);
242  EggJointPointer *joint;
243  DCAST_INTO_V(joint, back);
244 
245  // Compute and apply the new transforms.
246 
247  int f;
248  for (f = 0; f < num_frames; f++) {
249  LMatrix4d into = joint_data->get_frame(i, f % num_into_frames);
250  LMatrix4d from = top_joint->get_net_frame(model, f % num_from_frames, db);
251 
252  adjust_transform(from);
253 
254  db.set_matrix(joint, EggCharacterDb::TT_rebuild_frame,
255  f, into * from);
256  }
257  }
258  }
259 }
260 
261 /**
262  * Applies the channels from joint _top_joint in model from_model to the
263  * vertices at egg_node.
264  */
265 void EggTopstrip::
266 strip_anim_vertices(EggNode *egg_node, int into_model, int from_model,
267  EggJointData *top_joint, EggCharacterDb &db) {
268  int model = (from_model < 0) ? into_model : from_model;
269  if (!top_joint->has_model(model)) {
270  nout << "Warning: Joint " << top_joint->get_name()
271  << " is not defined in all models.\n";
272  return;
273  }
274 
275  LMatrix4d from = top_joint->get_net_frame(model, 0, db);
276  adjust_transform(from);
277 
278  egg_node->transform_vertices_only(from);
279 }
280 
281 
282 /**
283  * Adjust the transform extracted from the "top" joint according to the -s and
284  * -i/-n options, prior to applying it to the skeleton.
285  */
286 void EggTopstrip::
287 adjust_transform(LMatrix4d &mat) const {
288  if (_transform_channels.length() != 9) {
289  // Decompose and recompose the matrix, so we can eliminate the parts the
290  // user doesn't want.
291 
292  LVecBase3d scale, hpr, translate;
293  bool result = decompose_matrix(mat, scale, hpr, translate, _coordinate_system);
294  if (!result) {
295  nout << "Warning: skew transform in animation.\n";
296  } else {
297  LVecBase3d new_scale(1.0, 1.0, 1.0);
298  LVecBase3d new_hpr(0.0, 0.0, 0.0);
299  LVecBase3d new_translate(0.0, 0.0, 0.0);
300 
301  for (size_t i = 0; i < _transform_channels.size(); i++) {
302  switch (_transform_channels[i]) {
303  case 'i':
304  new_scale[0] = scale[0];
305  break;
306  case 'j':
307  new_scale[1] = scale[1];
308  break;
309  case 'k':
310  new_scale[2] = scale[2];
311  break;
312 
313  case 'h':
314  new_hpr[0] = hpr[0];
315  break;
316  case 'p':
317  new_hpr[1] = hpr[1];
318  break;
319  case 'r':
320  new_hpr[2] = hpr[2];
321  break;
322 
323  case 'x':
324  new_translate[0] = translate[0];
325  break;
326  case 'y':
327  new_translate[1] = translate[1];
328  break;
329  case 'z':
330  new_translate[2] = translate[2];
331  break;
332  }
333  }
334 
335  compose_matrix(mat, new_scale, new_hpr, new_translate, _coordinate_system);
336  }
337  }
338  if (_invert_transform) {
339  mat.invert_in_place();
340  }
341 }
342 
343 
344 int main(int argc, char *argv[]) {
345  EggTopstrip prog;
346  prog.parse_command_line(argc, argv);
347  prog.run();
348  return 0;
349 }
EggTopstrip
Reads a character model and/or animations and strips out the animation from one of the top joints fro...
Definition: eggTopstrip.h:34
eggTable.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggJointData::do_rebuild_all
bool do_rebuild_all(EggCharacterDb &db)
Calls do_rebuild() on all models, and recursively on all joints at this node and below.
Definition: eggJointData.cxx:279
eggCharacterCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggCharacterDb.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggComponentData::has_model
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,...
Definition: eggComponentData.I:30
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggCharacterData
Represents a single character, as read and collected from several models and animation files.
Definition: eggCharacterData.h:52
EggCharacterData::find_joint
EggJointData * find_joint(const std::string &name) const
Returns the first joint found with the indicated name, or NULL if no joint has that name.
Definition: eggCharacterData.I:80
EggCharacterData::get_model_root
EggNode * get_model_root(int n) const
Returns the model_root of the nth model associated with this character.
Definition: eggCharacterData.I:49
EggBackPointer
This stores a pointer from an EggJointData or EggSliderData object back to the referencing data in an...
Definition: eggBackPointer.h:31
EggJointData::get_net_frame
LMatrix4d get_net_frame(int model_index, int n, EggCharacterDb &db) const
Returns the complete transform from the root corresponding to this joint position in the nth frame in...
Definition: eggJointData.cxx:68
compose_matrix.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggJointData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode::transform_vertices_only
void transform_vertices_only(const LMatrix4d &mat)
Applies the indicated transformation only to vertices that appear in global space within vertex pools...
Definition: eggNode.I:270
ProgramBase::parse_command_line
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_...
Definition: programBase.cxx:274
EggJointPointer
This is a base class for EggJointNodePointer and EggMatrixTablePointer.
Definition: eggJointPointer.h:31
EggData
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition: eggData.h:37
EggTopstrip::strip_anim_vertices
void strip_anim_vertices(EggNode *egg_node, int into_model, int from_model, EggJointData *top_joint, EggCharacterDb &db)
Applies the channels from joint _top_joint in model from_model to the vertices at egg_node.
Definition: eggTopstrip.cxx:266
EggJointData
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
Definition: eggJointData.h:31
EggJointData::get_frame
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...
Definition: eggJointData.cxx:51
EggCharacterData::get_num_frames
int get_num_frames(int model_index) const
Returns the number of frames of animation of the indicated model.
Definition: eggCharacterData.cxx:100
EggTopstrip::check_transform_channels
void check_transform_channels()
Checks the _transform_channels string to ensure that it contains only the expected nine letters,...
Definition: eggTopstrip.cxx:187
eggCharacterData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggTopstrip.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggCharacterData::get_model_index
int get_model_index(int n) const
Returns the model_index of the nth model associated with this character.
Definition: eggCharacterData.I:37
EggCharacterDb::set_matrix
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.
Definition: eggCharacterDb.cxx:106
EggComponentData::get_num_models
int get_num_models() const
Returns the maximum number of back pointers this component may have.
Definition: eggComponentData.I:21
EggCharacterData::get_root_joint
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
Definition: eggCharacterData.I:71
eggJointPointer.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggComponentData::get_model
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.
Definition: eggComponentData.I:42
EggCharacterData::get_num_models
int get_num_models() const
Returns the total number of models associated with this character.
Definition: eggCharacterData.I:22
EggTopstrip::strip_anim
void strip_anim(EggCharacterData *char_data, EggJointData *joint_data, int from_model, EggCharacterData *from_char, EggJointData *top_joint, EggCharacterDb &db)
Applies the channels from joint _top_joint in model from_model to the joint referenced by joint_data.
Definition: eggTopstrip.cxx:222
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
EggTopstrip::adjust_transform
void adjust_transform(LMatrix4d &mat) const
Adjust the transform extracted from the "top" joint according to the -s and -i/-n options,...
Definition: eggTopstrip.cxx:287
TypedObject::is_of_type
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:28
EggCharacterDb
This class is used during joint optimization or restructuring to store the table of interim joint com...
Definition: eggCharacterDb.h:41