Panda3D
Loading...
Searching...
No Matches
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"
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 */
28EggTopstrip::
29EggTopstrip() {
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 */
82void EggTopstrip::
83run() {
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.
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 */
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 */
222strip_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 */
266strip_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 */
287adjust_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
344int main(int argc, char *argv[]) {
345 EggTopstrip prog;
346 prog.parse_command_line(argc, argv);
347 prog.run();
348 return 0;
349}
This stores a pointer from an EggJointData or EggSliderData object back to the referencing data in an...
Represents a single character, as read and collected from several models and animation files.
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
int get_model_index(int n) const
Returns the model_index of the nth model associated with this character.
int get_num_frames(int model_index) const
Returns the number of frames of animation of the indicated model.
EggNode * get_model_root(int n) const
Returns the model_root of the nth model associated with this character.
int get_num_models() const
Returns the total number of models associated with this character.
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.
This class is used during joint optimization or restructuring to store the table of interim joint com...
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.
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.
int get_num_models() const
Returns the maximum number of back pointers this component may have.
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,...
This is the primary interface into all the egg data, and the root of the egg file structure.
Definition eggData.h:37
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
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...
bool do_rebuild_all(EggCharacterDb &db)
Calls do_rebuild() on all models, and recursively on all joints at this node and below.
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...
This is a base class for EggJointNodePointer and EggMatrixTablePointer.
A base class for things that may be directly added into the egg hierarchy.
Definition eggNode.h:36
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
Reads a character model and/or animations and strips out the animation from one of the top joints fro...
Definition eggTopstrip.h:34
void check_transform_channels()
Checks the _transform_channels string to ensure that it contains only the expected nine letters,...
void adjust_transform(LMatrix4d &mat) const
Adjust the transform extracted from the "top" joint according to the -s and -i/-n options,...
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.
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.
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_...
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition typedObject.I:28
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.