15 #include "eggOptchar.h"
16 #include "eggOptcharUserData.h"
17 #include "vertexMembership.h"
19 #include "eggJointData.h"
20 #include "eggSliderData.h"
21 #include "eggCharacterCollection.h"
22 #include "eggCharacterData.h"
23 #include "eggBackPointer.h"
24 #include "eggGroupNode.h"
25 #include "eggPrimitive.h"
26 #include "eggVertexPool.h"
29 #include "eggAnimPreload.h"
30 #include "string_utils.h"
33 #include "compose_matrix.h"
34 #include "fftCompressor.h"
46 add_path_replace_options();
47 add_path_store_options();
52 set_program_brief(
"optimizes character models and animations in .egg files");
53 set_program_description
54 (
"egg-optchar performs basic optimizations of a character model "
55 "and its associated animations, primarily by analyzing the "
56 "animation tables and removing unneeded joints and/or morphs. "
57 "It can also perform basic restructuring operations on the "
58 "character hierarchy.");
62 "List the joint hierarchy instead of performing any operations.",
63 &EggOptchar::dispatch_none, &_list_hierarchy);
67 "List the joint hierarchy along with an indication of the properties "
69 &EggOptchar::dispatch_none, &_list_hierarchy_v);
73 "List the existing joint hierarchy as a series of -p joint,parent "
74 "commands, suitable for pasting into an egg-optchar command line.",
75 &EggOptchar::dispatch_none, &_list_hierarchy_p);
78 (
"keep",
"joint[,joint...]", 0,
79 "Keep the named joints (or sliders) in the character, even if they do "
80 "not appear to be needed by the animation.",
81 &EggOptchar::dispatch_vector_string_comma, NULL, &_keep_components);
84 (
"drop",
"joint[,joint...]", 0,
85 "Removes the named joints or sliders, even if they appear to be needed.",
86 &EggOptchar::dispatch_vector_string_comma, NULL, &_drop_components);
89 (
"expose",
"joint[,joint...]", 0,
90 "Expose the named joints by flagging them with a DCS attribute, so "
91 "each one can be found in the scene graph when the character is loaded, "
92 "and objects can be parented to it. This implies -keep.",
93 &EggOptchar::dispatch_vector_string_comma, NULL, &_expose_components);
96 (
"suppress",
"joint[,joint...]", 0,
97 "The opposite of suppress, this prevents the named joints from "
98 "being created with an implicit DCS attribute, even if they contain "
99 "rigid geometry. The default is to create an implicit node for any "
100 "joint that contains rigid geometry, to take advantage of display "
101 "list and/or vertex buffer caching. This does not imply -keep.",
102 &EggOptchar::dispatch_vector_string_comma, NULL, &_suppress_components);
105 (
"flag",
"node[,node...][=name]", 0,
106 "Assign the indicated name to the geometry within the given nodes. "
107 "This will make the geometry visible as a node in the resulting "
108 "character model when it is loaded in the scene graph (normally, "
109 "the node hierarchy is suppressed when loading characters). This "
110 "is different from -expose in that it reveals geometry rather than "
111 "joints; the revealed node can be hidden or its attributes changed "
112 "at runtime, but it will be animated by its vertices, not the node, so "
113 "objects parented to this node will not inherit its animation.",
114 &EggOptchar::dispatch_flag_groups, NULL, &_flag_groups);
117 (
"defpose",
"anim.egg,frame", 0,
118 "Specify the model's default pose. The pose is taken "
119 "from the indicated frame of the named animation file (which must "
120 "also be named separately on the command line). The "
121 "pose will be held by the model in "
122 "the absence of any animation, and need not be the same "
123 "pose in which the model was originally skinned.",
124 &EggOptchar::dispatch_string, NULL, &_defpose);
128 "Add an <AnimPreload> entry for each animation to the model file(s). "
129 "This can be used at runtime to support asynchronous "
130 "loading and binding of animation channels.",
131 &EggOptchar::dispatch_none, &_preload);
134 (
"zero",
"joint[,hprxyzijkabc]", 0,
135 "Zeroes out the animation channels for the named joint. If "
136 "a subset of the component letters hprxyzijkabc is included, the "
137 "operation is restricted to just those components; otherwise the "
138 "entire transform is cleared.",
139 &EggOptchar::dispatch_name_components, NULL, &_zero_channels);
143 "Keep all joints and sliders in the character, except those named "
144 "explicitly by -drop.",
145 &EggOptchar::dispatch_none, &_keep_all);
148 (
"p",
"joint,parent", 0,
149 "Moves the named joint under the named parent joint. Use "
150 "\"-p joint,\" to reparent a joint to the root. The joint transform "
151 "is recomputed appropriately under its new parent so that the animation "
152 "is not affected (the effect is similar to NodePath::wrt_reparent_to).",
153 &EggOptchar::dispatch_vector_string_pair, NULL, &_reparent_joints);
156 (
"new",
"joint,source", 0,
157 "Creates a new joint under the named parent joint. The new "
158 "joint will inherit the same net transform as its parent.",
159 &EggOptchar::dispatch_vector_string_pair, NULL, &_new_joints);
162 (
"rename",
"joint,newjoint", 0,
163 "Renames the indicated joint, if present, to the given name.",
164 &EggOptchar::dispatch_vector_string_pair, NULL, &_rename_joints);
169 "Computes the optimal joint hierarchy for the character by analyzing "
170 "all of the joint animation and reparenting joints to minimize "
171 "transformations. This can repair skeletons that have been flattened "
172 "or whose hierarchy was otherwise damaged in conversion; it can also "
173 "detect joints that are constrained to follow other joints and should "
174 "therefore be parented to the master joints. The result is a file "
175 "from which more joints may be successfully removed, that generally "
176 "compresses better and with fewer artifacts. However, this is a "
177 "fairly expensive operation.",
178 &EggOptchar::dispatch_none, &_optimal_hierarchy);
183 "Quantize joint membership values to the given unit. This is "
184 "the smallest significant change in joint membership. There can "
185 "be a significant performance (and memory utilization) runtime "
186 "benefit for eliminating small differences in joint memberships "
187 "between neighboring vertices. The default is 0.01; specifying "
188 "0 means to preserve the original values.",
189 &EggOptchar::dispatch_double, NULL, &_vref_quantum);
192 (
"qa",
"quantum[,hprxyzijkabc]", 0,
193 "Quantizes animation channels to the given unit. This rounds each "
194 "of the named components of all joints to the nearest multiple of unit. "
195 "There is no performance benefit, and little compression benefit, "
196 "for doing this; and this may introduce visible artifacts to the "
197 "animation. However, sometimes it is a useful tool for animation "
198 "analysis and comparison. This option may be repeated several times "
199 "to quantize different channels by a different amount.",
200 &EggOptchar::dispatch_double_components, NULL, &_quantize_anims);
203 (
"dart",
"[default, sync, nosync, or structured]", 0,
204 "change the dart value in the given eggs",
205 &EggOptchar::dispatch_string, NULL, &_dart_type);
208 _optimal_hierarchy =
false;
209 _vref_quantum = 0.01;
222 if (apply_user_reparents()) {
223 nout <<
"Reparenting hierarchy.\n";
229 if (!_zero_channels.empty()) {
233 int num_characters = _collection->get_num_characters();
237 for (ci = 0; ci < num_characters; ci++) {
240 analyze_sliders(char_data);
243 if (_list_hierarchy || _list_hierarchy_v) {
244 for (ci = 0; ci < num_characters; ci++) {
246 nout <<
"Character: " << char_data->get_name() <<
"\n";
248 list_scalars(char_data, _list_hierarchy_v);
252 }
else if (_list_hierarchy_p) {
253 for (ci = 0; ci < num_characters; ci++) {
255 nout <<
"Character: " << char_data->get_name() <<
"\n";
266 determine_removed_components();
268 if (process_joints()) {
288 if (!_flag_groups.empty()) {
290 for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
304 if (!_defpose.empty()) {
308 if (!_dart_type.empty()) {
310 for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
311 change_dart_type(*ei, _dart_type);
329 if (_list_hierarchy || _list_hierarchy_v || _list_hierarchy_p) {
333 return EggCharacterFilter::handle_args(args);
345 dispatch_vector_string_pair(
const string &opt,
const string &arg,
void *var) {
346 StringPairs *ip = (StringPairs *)var;
349 tokenize(arg, words,
",");
351 if (words.size() == 2) {
359 <<
" requires a pair of strings separated by a comma.\n";
376 dispatch_name_components(
const string &opt,
const string &arg,
void *var) {
377 StringPairs *ip = (StringPairs *)var;
380 tokenize(arg, words,
",");
383 if (words.size() == 1) {
386 }
else if (words.size() == 2) {
392 <<
" requires a pair of strings separated by a comma.\n";
397 sp._b = matrix_component_letters;
399 for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
400 if (strchr(matrix_component_letters, *si) == NULL) {
401 nout <<
"Not a standard matrix component: \"" << *si <<
"\"\n"
402 <<
"-" << opt <<
" requires a joint name followed by a set "
403 <<
"of component names. The standard component names are \""
404 << matrix_component_letters <<
"\".\n";
425 dispatch_double_components(
const string &opt,
const string &arg,
void *var) {
426 DoubleStrings *ip = (DoubleStrings *)var;
429 tokenize(arg, words,
",");
431 bool valid_double =
false;
434 if (words.size() == 1) {
435 valid_double = string_to_double(words[0], sp._a);
437 }
else if (words.size() == 2) {
438 valid_double = string_to_double(words[0], sp._a);
443 <<
" requires a numeric value followed by a string.\n";
449 <<
" requires a numeric value followed by a string.\n";
454 sp._b = matrix_component_letters;
456 for (string::const_iterator si = sp._b.begin(); si != sp._b.end(); ++si) {
457 if (strchr(matrix_component_letters, *si) == NULL) {
458 nout <<
"Not a standard matrix component: \"" << *si <<
"\"\n"
459 <<
"-" << opt <<
" requires a joint name followed by a set "
460 <<
"of component names. The standard component names are \""
461 << matrix_component_letters <<
"\".\n";
481 dispatch_flag_groups(
const string &opt,
const string &arg,
void *var) {
482 FlagGroups *ip = (FlagGroups *)var;
486 tokenize(arg, words,
",");
490 <<
" requires a series of words separated by a comma.\n";
494 FlagGroupsEntry entry;
498 string &last_word = words.back();
499 size_t equals = last_word.rfind(
'=');
500 if (equals != string::npos) {
501 entry._name = last_word.substr(equals + 1);
502 last_word = last_word.substr(0, equals);
511 vector_string::const_iterator si;
512 for (si = words.begin(); si != words.end(); ++si) {
513 const string &word = (*si);
517 ip->push_back(entry);
529 determine_removed_components() {
534 Names suppress_names;
537 vector_string::const_iterator si;
538 for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
539 keep_names.insert(*si);
541 for (si = _drop_components.begin(); si != _drop_components.end(); ++si) {
542 drop_names.insert(*si);
544 for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
545 keep_names.insert(*si);
546 expose_names.insert(*si);
548 for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) {
549 suppress_names.insert(*si);
553 keep_names.insert(
"");
555 int num_characters = _collection->get_num_characters();
556 for (
int ci = 0; ci < num_characters; ci++) {
559 nout << char_data->get_name() <<
" has " << num_components <<
" components.\n";
560 for (
int i = 0; i < num_components; i++) {
568 const string &name = comp_data->get_name();
569 if (suppress_names.find(name) != suppress_names.end()) {
572 names_used.insert(name);
573 user_data->_flags |= EggOptcharUserData::F_suppress;
576 if (drop_names.find(name) != drop_names.end()) {
578 names_used.insert(name);
579 user_data->_flags |= EggOptcharUserData::F_remove;
581 }
else if (_keep_all || keep_names.find(name) != keep_names.end()) {
583 names_used.insert(name);
585 if (expose_names.find(name) != expose_names.end()) {
587 user_data->_flags |= EggOptcharUserData::F_expose;
592 if ((user_data->_flags & (EggOptcharUserData::F_static | EggOptcharUserData::F_empty)) != 0) {
593 if ((user_data->_flags & (EggOptcharUserData::F_top | EggOptcharUserData::F_empty)) == EggOptcharUserData::F_top) {
604 user_data->_flags |= EggOptcharUserData::F_remove;
613 for (si = _keep_components.begin(); si != _keep_components.end(); ++si) {
614 const string &name = (*si);
615 if (names_used.find(name) == names_used.end()) {
616 nout <<
"No such component: " << name <<
"\n";
619 for (si = _drop_components.begin(); si != _drop_components.end(); ++si) {
620 const string &name = (*si);
621 if (names_used.find(name) == names_used.end()) {
622 nout <<
"No such component: " << name <<
"\n";
625 for (si = _expose_components.begin(); si != _expose_components.end(); ++si) {
626 const string &name = (*si);
627 if (names_used.find(name) == names_used.end()) {
628 nout <<
"No such component: " << name <<
"\n";
631 for (si = _suppress_components.begin(); si != _suppress_components.end(); ++si) {
632 const string &name = (*si);
633 if (names_used.find(name) == names_used.end()) {
634 nout <<
"No such component: " << name <<
"\n";
649 int num_characters = _collection->get_num_characters();
650 for (
int ci = 0; ci < num_characters; ci++) {
654 for (
int i = 0; i < num_joints; i++) {
659 if ((user_data->_flags & EggOptcharUserData::F_empty) == 0 &&
660 (user_data->_flags & EggOptcharUserData::F_remove) != 0) {
663 EggJointData *best_joint = find_best_vertex_joint(joint_data->get_parent());
670 best_user_data->_flags &= ~(EggOptcharUserData::F_empty | EggOptcharUserData::F_remove);
688 bool removed_any =
false;
689 int num_characters = _collection->get_num_characters();
690 for (
int ci = 0; ci < num_characters; ci++) {
696 int num_identity = 0;
700 for (
int i = 0; i < num_joints; i++) {
705 if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
711 if ((user_data->_flags & EggOptcharUserData::F_identity) != 0) {
713 }
else if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
715 }
else if ((user_data->_flags & EggOptcharUserData::F_empty) != 0) {
725 EggJointData *best_parent = find_best_parent(joint_data->get_parent());
727 if ((user_data->_flags & EggOptcharUserData::F_expose) != 0) {
729 }
else if ((user_data->_flags & EggOptcharUserData::F_suppress) != 0) {
730 joint_data->
expose(EggGroup::DC_none);
736 if (num_joints == num_kept) {
737 nout << char_data->get_name() <<
": keeping " << num_joints
740 nout << setw(5) << num_joints
741 <<
" original joints in " << char_data->get_name()
743 if (num_identity != 0) {
744 nout << setw(5) << num_identity <<
" identity joints\n";
746 if (num_static != 0) {
747 nout << setw(5) << num_static <<
" unanimated joints\n";
749 if (num_empty != 0) {
750 nout << setw(5) << num_empty <<
" empty joints\n";
752 if (num_other != 0) {
753 nout << setw(5) << num_other <<
" other joints\n";
756 << setw(5) << num_kept <<
" joints remaining\n\n";
776 if ((user_data->_flags & EggOptcharUserData::F_remove) != 0) {
779 return find_best_parent(joint_data->get_parent());
795 find_best_vertex_joint(
EggJointData *joint_data)
const {
803 if ((user_data->_flags & EggOptcharUserData::F_static) != 0) {
805 return find_best_vertex_joint(joint_data->get_parent());
820 apply_user_reparents() {
821 bool did_anything =
false;
823 int num_characters = _collection->get_num_characters();
826 StringPairs::const_iterator spi;
827 for (spi = _new_joints.begin(); spi != _new_joints.end(); ++spi) {
828 const StringPair &p = (*spi);
830 for (
int ci = 0; ci < num_characters; ci++) {
839 nout <<
"No joint named " << p._b <<
" in " << char_data->get_name()
843 nout <<
"Joint " << p._a <<
" already exists in "
844 << char_data->get_name() <<
".\n";
847 nout <<
"Creating new joint " << p._a <<
" in "
848 << char_data->get_name() <<
".\n";
856 for (spi = _reparent_joints.begin(); spi != _reparent_joints.end(); ++spi) {
857 const StringPair &p = (*spi);
859 for (
int ci = 0; ci < num_characters; ci++) {
868 nout <<
"No joint named " << p._b <<
" in " << char_data->get_name()
871 nout <<
"No joint named " << p._a <<
" in " << char_data->get_name()
880 if (_optimal_hierarchy) {
882 for (
int ci = 0; ci < num_characters; ci++) {
884 nout <<
"Computing optimal hierarchy for "
885 << char_data->get_name() <<
".\n";
887 nout <<
"Done computing optimal hierarchy for "
888 << char_data->get_name() <<
".\n";
906 bool did_anything =
false;
907 int num_characters = _collection->get_num_characters();
909 StringPairs::const_iterator spi;
910 for (spi = _zero_channels.begin(); spi != _zero_channels.end(); ++spi) {
911 const StringPair &p = (*spi);
913 for (
int ci = 0; ci < num_characters; ci++) {
918 nout <<
"No joint named " << p._a <<
" in " << char_data->get_name()
940 quantize_channels() {
941 bool did_anything =
false;
942 int num_characters = _collection->get_num_characters();
944 DoubleStrings::const_iterator spi;
945 for (spi = _quantize_anims.begin(); spi != _quantize_anims.end(); ++spi) {
946 const DoubleString &p = (*spi);
948 for (
int ci = 0; ci < num_characters; ci++) {
972 joint_data->set_user_data(user_data);
978 user_data->_flags |= EggOptcharUserData::F_top;
985 bool different_mat =
false;
986 bool has_vertices =
false;
990 for (i = 0; i < num_models; i++) {
1000 for (f = 0; f < num_frames && !different_mat; f++) {
1003 if (num_mats == 1) {
1005 user_data->_static_mat = mat;
1009 if (!mat.
almost_equal(user_data->_static_mat, 0.0001)) {
1011 different_mat =
true;
1018 if (!different_mat) {
1020 user_data->_flags |= EggOptcharUserData::F_static;
1022 if (num_mats == 0 ||
1025 user_data->_flags |= EggOptcharUserData::F_identity;
1029 if (!has_vertices) {
1031 user_data->_flags |= EggOptcharUserData::F_empty;
1034 int num_children = joint_data->get_num_children();
1035 for (i = 0; i < num_children; i++) {
1036 analyze_joints(joint_data->get_child(i), level + 1);
1049 for (
int si = 0; si < num_sliders; si++) {
1053 slider_data->set_user_data(user_data);
1059 bool different_value = false;
1060 bool has_vertices = false;
1062 int num_models = slider_data->get_num_models();
1063 for (
int i = 0; i < num_models; i++) {
1067 has_vertices =
true;
1073 for (f = 0; f < num_frames && !different_value; f++) {
1074 double value = slider_data->
get_frame(i, f);
1076 if (num_values == 1) {
1078 user_data->_static_value = value;
1082 if (!IS_THRESHOLD_EQUAL(value, user_data->_static_value, 0.0001)) {
1084 different_value =
true;
1091 if (!different_value) {
1093 user_data->_flags |= EggOptcharUserData::F_static;
1095 if (num_values == 0 || IS_THRESHOLD_ZERO(user_data->_static_value, 0.0001)) {
1097 user_data->_flags |= EggOptcharUserData::F_identity;
1101 if (!has_vertices) {
1103 user_data->_flags |= EggOptcharUserData::F_empty;
1114 list_joints(
EggJointData *joint_data,
int indent_level,
bool verbose) {
1119 int num_children = joint_data->get_num_children();
1120 for (
int i = 0; i < num_children; i++) {
1122 describe_component(child_data, indent_level, verbose);
1124 list_joints(child_data, indent_level + 2, verbose);
1138 int num_children = joint_data->get_num_children();
1139 static const int max_col = 72;
1141 for (
int i = 0; i < num_children; i++) {
1147 string text = string(
" -p ") + child_data->get_name() +
1148 string(
",") + joint_data->get_name();
1150 cout <<
" " << text;
1151 col = 4 + text.length();
1153 col += text.length();
1154 if (col >= max_col) {
1155 cout <<
" \\\n " << text;
1156 col = 4 + text.length();
1162 list_joints_p(child_data, col);
1174 for (
int si = 0; si < num_sliders; si++) {
1176 describe_component(slider_data, 0, verbose);
1190 indent(cout, indent_level)
1191 << comp_data->get_name();
1194 EggOptcharUserData *user_data =
1196 if (user_data->is_identity()) {
1197 cout <<
" (identity)";
1198 }
else if (user_data->is_static()) {
1199 cout <<
" (static)";
1201 if (user_data->is_empty()) {
1204 if (user_data->is_top()) {
1220 int num_characters = _collection->get_num_characters();
1221 for (
int ci = 0; ci < num_characters; ci++) {
1241 quantize_vertices() {
1243 for (ei = _eggs.begin(); ei != _eggs.end(); ++ei) {
1244 quantize_vertices(*ei);
1257 quantize_vertices(
EggNode *egg_node) {
1258 if (egg_node->
is_of_type(EggVertexPool::get_class_type())) {
1261 for (vi = vpool->
begin(); vi != vpool->
end(); ++vi) {
1262 quantize_vertex(*vi);
1265 }
else if (egg_node->
is_of_type(EggGroupNode::get_class_type())) {
1267 EggGroupNode::iterator ci;
1268 for (ci = group->begin(); ci != group->end(); ++ci) {
1269 quantize_vertices(*ci);
1280 quantize_vertex(
EggVertex *egg_vertex) {
1288 EggVertex::GroupRef::const_iterator gi;
1289 double net_membership = 0.0;
1294 net_membership += membership;
1296 nassertv(net_membership != 0.0);
1300 double factor = 1.0 / net_membership;
1301 net_membership = 0.0;
1302 VertexMemberships::iterator mi;
1303 VertexMemberships::iterator largest = memberships.begin();
1305 for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
1306 if ((*largest) < (*mi)) {
1312 double value = (*mi)._membership * factor;
1313 if (_vref_quantum != 0.0) {
1314 value = floor(value / _vref_quantum + 0.5) * _vref_quantum;
1316 (*mi)._membership = value;
1318 net_membership += value;
1323 (*largest)._membership += 1.0 - net_membership;
1327 for (mi = memberships.begin(); mi != memberships.end(); ++mi) {
1328 (*mi)._group->set_vertex_membership(egg_vertex, (*mi)._membership);
1341 bool matched =
false;
1343 FlagGroups::const_iterator fi;
1344 for (fi = _flag_groups.begin();
1345 fi != _flag_groups.end() && !matched;
1347 const FlagGroupsEntry &entry = (*fi);
1348 Globs::const_iterator si;
1349 for (si = entry._groups.begin();
1350 si != entry._groups.end() && !matched;
1352 if ((*si).matches(egg_group->get_name())) {
1354 if (!entry._name.empty()) {
1357 name = egg_group->get_name();
1368 rename_primitives(egg_group, name);
1372 EggGroupNode::iterator gi;
1373 for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
1375 if (child->
is_of_type(EggGroupNode::get_class_type())) {
1377 do_flag_groups(group);
1390 for (StringPairs::iterator spi = _rename_joints.begin();
1391 spi != _rename_joints.end();
1393 const StringPair &sp = (*spi);
1394 int num_characters = _collection->get_num_characters();
1396 for (ci = 0; ci < num_characters; ++ci) {
1400 nout <<
"Renaming joint " << sp._a <<
" to " << sp._b <<
"\n";
1401 joint->set_name(sp._b);
1404 for (
int mn = 0; mn < num_models; ++mn) {
1412 nout <<
"Couldn't find joint " << sp._a <<
"\n";
1425 change_dart_type(
EggGroupNode *egg_group,
const string &new_dart_type) {
1426 EggGroupNode::iterator gi;
1427 for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
1429 if (child->
is_of_type(EggGroupNode::get_class_type())) {
1431 if (child->
is_of_type(EggGroup::get_class_type())) {
1433 EggGroup::DartType dt = gr->get_dart_type();
1434 if(dt != EggGroup::DT_none) {
1436 gr->set_dart_type(newDt);
1439 change_dart_type(group, new_dart_type);
1452 rename_primitives(
EggGroupNode *egg_group,
const string &name) {
1453 EggGroupNode::iterator gi;
1454 for (gi = egg_group->begin(); gi != egg_group->end(); ++gi) {
1457 if (child->
is_of_type(EggGroupNode::get_class_type())) {
1459 rename_primitives(group, name);
1461 }
else if (child->
is_of_type(EggPrimitive::get_class_type())) {
1462 child->set_name(name);
1478 int num_characters = _collection->get_num_characters();
1480 for (ci = 0; ci < num_characters; ++ci) {
1484 for (
int mn = 0; mn < num_models; ++mn) {
1486 if (root->
is_of_type(EggTable::get_class_type())) {
1492 int mi = char_data->get_model_index(mn);
1493 anim_preload->set_num_frames(char_data->get_num_frames(mi));
1494 double frame_rate = char_data->get_frame_rate(mi);
1495 if (frame_rate != 0.0) {
1496 anim_preload->set_fps(frame_rate);
1499 anim_group->add_child(anim_preload);
1506 for (ci = 0; ci < num_characters; ++ci) {
1510 for (
int mn = 0; mn < num_models; ++mn) {
1512 if (root->
is_of_type(EggGroup::get_class_type())) {
1515 EggGroup::const_iterator ci;
1516 for (ci = anim_group->begin(); ci != anim_group->end(); ++ci) {
1519 model_root->add_child(new_anim_preload);
1535 size_t comma = _defpose.find(
',');
1536 egg_filename = _defpose.substr(0, comma);
1539 if (comma != string::npos) {
1540 frame_str = _defpose.substr(comma + 1);
1542 frame_str = trim(frame_str);
1544 if (!frame_str.empty()) {
1545 if (!string_to_int(frame_str, frame)) {
1546 nout <<
"Invalid integer in -defpose: " << frame_str <<
"\n";
1553 int num_eggs = _collection->get_num_eggs();
1557 for (i = 0; i < num_eggs && egg_index == -1; ++i) {
1558 if (_collection->get_egg(i)->get_egg_filename() == egg_filename) {
1565 for (i = 0; i < num_eggs && egg_index == -1; ++i) {
1566 if (_collection->get_egg(i)->get_egg_filename().get_basename_wo_extension() == egg_basename) {
1571 if (egg_index == -1) {
1573 nout <<
"Egg file " << egg_filename <<
" named in -defpose, but does not appear on command line.\n";
1577 EggData *egg_data = _collection->get_egg(egg_index);
1579 if (_collection->get_num_models(egg_index) == 0) {
1580 nout <<
"Egg file " << egg_filename <<
" does not include any model or animation.\n";
1585 int mi = _collection->get_first_model_index(egg_index);
1589 int anim_index = -1;
1597 nassertv(anim_index != -1);
1603 int main(
int argc,
char *argv[]) {
void reparent_to(EggJointData *new_parent)
Indicates an intention to change the parent of this joint to the indicated joint, or NULL to remove i...
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
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...
EggUserData * get_user_data() const
Returns the user data pointer most recently stored on this object, or NULL if nothing was previously ...
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 a 4-by-4 transform matrix.
bool do_reparent()
Begins the process of restructuring the joint hierarchy according to the previous calls to reparent_t...
static DartType string_dart_type(const string &strval)
Returns the DartType value associated with the given string representation, or DT_none if the string ...
int get_num_models() const
Returns the total number of models associated with this character.
virtual void set_name(const string &name)
Applies the indicated name change to the egg file.
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_...
A base class for nodes in the hierarchy that are not leaf nodes.
void add_normals_options()
Adds -no, -np, etc.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
void zero_channels(const string &components)
Calls zero_channels() on all models for this joint, but does not recurse downwards.
int get_num_joints() const
Returns the total number of joints in the character joint hierarchy.
GroupRef::const_iterator gref_end() const
Returns an iterator that can, in conjunction with gref_begin(), be used to traverse the entire set of...
int get_num_models() const
Returns the maximum number of back pointers this component may have.
EggSliderData * get_slider(int n) const
Returns the nth slider in the character slider list.
This corresponds to a single morph slider control.
int get_num_frames(int model_index) const
Returns the number of frames of animation for this particular component in the indicated 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...
GroupRef::const_iterator gref_begin() const
Returns an iterator that can, in conjunction with gref_end(), be used to traverse the entire set of g...
This is the primary interface into all the egg data, and the root of the egg file structure...
EggNode * get_model_root(int n) const
Returns the model_root of the nth model associated with this character.
This is the base class of both EggJointData and EggSliderData.
int get_num_components() const
Returns the total number of joints and sliders in the character.
This is our own Panda specialization on the default STL vector.
void quantize_channels(const string &components, double quantum)
Calls quantize_channels() on all models for this joint, and then recurses downwards to all joints bel...
void apply_default_pose(int source_model, int frame)
Applies the pose from the indicated frame of the indicated source model_index as the initial pose for...
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
bool almost_equal(const LMatrix4d &other, double threshold) const
Returns true if two matrices are memberwise equal within a specified tolerance.
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
The name of a file, such as a texture file or an Egg file.
This corresponds to an <AnimPreload> entry.
static const LMatrix4d & ident_mat()
Returns an identity matrix.
EggJointData * get_root_joint() const
Returns the root joint of the character hierarchy.
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal...
EggComponentData * get_component(int n) const
Returns the nth joint or slider in the character.
Performs basic optimizations of a character model and its associated animations, by analyzing the ani...
virtual bool has_vertices() const
Returns true if there are any vertices referenced by the node this points to, false otherwise...
void move_vertices_to(EggJointData *new_owner)
Moves the vertices assigned to this joint into the indicated joint, without changing their weight ass...
This class contains extra user data which is piggybacked onto EggGroup objects for the purpose of the...
double get_vertex_membership(const EggVertex *vert) const
Returns the amount of membership of the indicated vertex in this group.
double get_frame(int model_index, int n) const
Returns the value corresponding to this slider position in the nth frame in the indicated model...
void choose_optimal_hierarchy()
Chooses the best possible parent joint for each of the joints in the hierarchy, based on the score co...
Represents a single character, as read and collected from several models and animation files...
This class is used to help EggOptchar quantize the membership of one vertex among its various groups...
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
This is one node of a hierarchy of EggJointData nodes, each of which represents a single joint of the...
A base class for things that may be directly added into the egg hierarchy.
void add_transform_options()
Adds -TS, -TT, etc.
EggJointData * get_joint(int n) const
Returns the nth joint in the character joint hierarchy.
This stores a pointer from an EggJointData or EggSliderData object back to the referencing data in an...
const Filename & get_egg_filename() const
Returns the directory in which the egg file is considered to reside.
GroupRef::size_type gref_size() const
Returns the number of elements between gref_begin() and gref_end().
void expose(EggGroup::DCSType dcs_type=EggGroup::DC_default)
Calls expose() on all models for this joint, but does not recurse downwards.
EggJointData * find_joint(const string &name) const
Returns the first joint found with the indicated name, or NULL if no joint has that name...
int get_num_sliders() const
Returns the number of sliders in the character slider list.
A collection of vertices.
EggData * get_egg_data(int n) const
Returns the EggData representing the egg file that defined this particular model. ...
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
static bool is_compression_available()
Returns true if the FFTW library is compiled in, so that this class is actually capable of doing usef...
EggJointData * make_new_joint(const string &name, EggJointData *parent)
Creates a new joint as a child of the indicated joint and returns it.
This class can be used to test for string matches against standard Unix-shell filename globbing conve...