Panda3D
mayaToEggConverter.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 mayaToEggConverter.cxx
10  * @author drose
11  * @date 1999-11-10
12  * Modified 19Mar10 by ETC PandaSE team
13  * Added set_vertex_color_modern to fix Phong shader bug; also see
14  * header comment for mayaToEgg.cxx for more details
15  */
16 
17 #include "mayaToEggConverter.h"
18 #include "mayaShader.h"
19 #include "maya_funcs.h"
20 #include "config_mayaegg.h"
21 #include "mayaEggGroupUserData.h"
22 
23 #include "eggData.h"
24 #include "eggGroup.h"
25 #include "eggTable.h"
26 #include "eggVertex.h"
27 #include "eggVertexPool.h"
28 #include "eggNurbsSurface.h"
29 #include "eggNurbsCurve.h"
30 #include "eggPolygon.h"
31 #include "eggPrimitive.h"
32 #include "eggTexture.h"
33 #include "eggTextureCollection.h"
34 #include "eggXfmSAnim.h"
35 #include "eggSAnimData.h"
36 #include "string_utils.h"
37 #include "dcast.h"
38 
39 #include "pre_maya_include.h"
40 #include <maya/MArgList.h>
41 #include <maya/MColor.h>
42 #include <maya/MDagPath.h>
43 #include <maya/MFnCamera.h>
44 #include <maya/MFnDagNode.h>
45 #include <maya/MFnTransform.h>
46 #include <maya/MFnLight.h>
47 #include <maya/MFnNurbsSurface.h>
48 #include <maya/MFnNurbsCurve.h>
49 #include <maya/MFnMesh.h>
50 #include <maya/MFnMeshData.h>
51 #include <maya/MFnPlugin.h>
52 #include <maya/MItDag.h>
53 #include <maya/MMatrix.h>
54 #include <maya/MObject.h>
55 #include <maya/MPoint.h>
56 #include <maya/MPointArray.h>
57 #include <maya/MDoubleArray.h>
58 #include <maya/MIntArray.h>
59 #include <maya/MPxCommand.h>
60 #include <maya/MStatus.h>
61 #include <maya/MString.h>
62 #include <maya/MTransformationMatrix.h>
63 #include <maya/MVector.h>
64 #include <maya/MTesselationParams.h>
65 #include <maya/MAnimControl.h>
66 #include <maya/MGlobal.h>
67 #include <maya/MAnimUtil.h>
68 #include <maya/MFnSkinCluster.h>
69 #include <maya/MFnWeightGeometryFilter.h>
70 #include <maya/MFnIkJoint.h>
71 #include <maya/MFnSingleIndexedComponent.h>
72 #include <maya/MFnDoubleIndexedComponent.h>
73 #include <maya/MFnBlendShapeDeformer.h>
74 #include <maya/MItDependencyGraph.h>
75 #include <maya/MDagPathArray.h>
76 #include <maya/MSelectionList.h>
77 #include "post_maya_include.h"
78 
79 using std::endl;
80 using std::string;
81 
82 
83 /**
84  *
85  */
86 MayaToEggConverter::
87 MayaToEggConverter(const string &program_name) :
88  _program_name(program_name),
89  _tree(this)
90 {
91  // Make sure the library is properly initialized.
93 
94  _from_selection = false;
95 
96  _polygon_output = false;
97  _polygon_tolerance = 0.01;
98  _respect_maya_double_sided = maya_default_double_sided;
99  _always_show_vertex_color = maya_default_vertex_color;
100  _keep_all_uvsets = false;
101  _round_uvs = false;
102  _legacy_shader = false;
103  _convert_cameras = false;
104  _convert_lights = false;
105 
106  _transform_type = TT_model;
107 }
108 
109 /**
110  *
111  */
112 MayaToEggConverter::
113 MayaToEggConverter(const MayaToEggConverter &copy) :
114  _program_name(copy._program_name),
115  _from_selection(copy._from_selection),
116  _subsets(copy._subsets),
117  _subroots(copy._subroots),
118  _excludes(copy._excludes),
119  _ignore_sliders(copy._ignore_sliders),
120  _force_joints(copy._force_joints),
121  _tree(this),
122  _maya(copy._maya),
123  _polygon_output(copy._polygon_output),
124  _polygon_tolerance(copy._polygon_tolerance),
125  _respect_maya_double_sided(copy._respect_maya_double_sided),
126  _always_show_vertex_color(copy._always_show_vertex_color),
127  _keep_all_uvsets(copy._keep_all_uvsets),
128  _convert_cameras(copy._convert_cameras),
129  _convert_lights(copy._convert_lights),
130  _round_uvs(copy._round_uvs),
131  _legacy_shader(copy._legacy_shader),
132  _transform_type(copy._transform_type)
133 {
134 }
135 
136 /**
137  *
138  */
139 MayaToEggConverter::
140 ~MayaToEggConverter() {
141  close_api();
142 }
143 
144 /**
145  * Allocates and returns a new copy of the converter.
146  */
148 make_copy() {
149  return new MayaToEggConverter(*this);
150 }
151 
152 /**
153  * Returns the English name of the file type this converter supports.
154  */
156 get_name() const {
157  return "Maya";
158 }
159 
160 /**
161  * Returns the common extension of the file type this converter supports.
162  */
164 get_extension() const {
165  return "mb";
166 }
167 
168 /**
169  * Returns a space-separated list of extension, in addition to the one
170  * returned by get_extension(), that are recognized by this converter.
171  */
174  return "ma";
175 }
176 
177 /**
178  * Handles the reading of the input file and converting it to egg. Returns
179  * true if successful, false otherwise.
180  *
181  * This is designed to be as generic as possible, generally in support of run-
182  * time loading. Also see convert_maya().
183  */
185 convert_file(const Filename &filename) {
186  if (!open_api()) {
187  mayaegg_cat.error()
188  << "Maya is not available.\n";
189  return false;
190  }
191 
192  // We must ensure our Maya pointers are cleared before we reset the Maya
193  // scene, because resetting the Maya scene will invalidate all the Maya
194  // pointers we are holding and cause a crash if we try to free them later.
195  clear();
196 
197  if (!_maya->read(filename)) {
198  mayaegg_cat.error()
199  << "Unable to read " << filename << "\n";
200  return false;
201  }
202 
203  if (_character_name.empty()) {
204  _character_name = filename.get_basename_wo_extension();
205  }
206 
207  return convert_maya();
208 }
209 
210 /**
211  * Empties the list of subroot nodes added via add_subroot(). The entire file
212  * will once again be converted.
213  */
215 clear_subroots() {
216  _subroots.clear();
217 }
218 
219 /**
220  * Adds a name pattern to the list of subroot nodes. If the list of subroot
221  * nodes is not empty, then only a subroot of the nodes in the maya file will
222  * be converted: those whose names match one of the patterns given on this
223  * list.
224  */
226 add_subroot(const GlobPattern &glob) {
227  _subroots.push_back(glob);
228 }
229 
230 /**
231  * Empties the list of subset nodes added via add_subset(). The entire file
232  * will once again be converted.
233  */
235 clear_subsets() {
236  _subsets.clear();
237 }
238 
239 /**
240  * Adds a name pattern to the list of subset nodes. If the list of subset
241  * nodes is not empty, then only a subset of the nodes in the maya file will
242  * be converted: those whose names match one of the patterns given on this
243  * list.
244  */
246 add_subset(const GlobPattern &glob) {
247  _subsets.push_back(glob);
248 }
249 
250 /**
251  * Empties the list of excluded nodes added via add_exclude().
252  */
254 clear_excludes() {
255  _excludes.clear();
256 }
257 
258 /**
259  * Adds a name pattern to the list of excluded nodes.
260  */
262 add_exclude(const GlobPattern &glob) {
263  _excludes.push_back(glob);
264 }
265 
266 /**
267  * Empties the list of ignore_sliders added via add_ignore_slider(). No
268  * sliders will be ignored.
269  */
272  _ignore_sliders.clear();
273 }
274 
275 /**
276  * Adds a name pattern to the list of ignore_sliders. Any slider (blend shape
277  * deformer) that matches a name on the list will not be converted or
278  * otherwise molested by the converter. This is occasionally necessary to
279  * filter out automatically-created sliders that are not intended to be used
280  * directly, but instead have an indirect effect on other sliders.
281  */
283 add_ignore_slider(const GlobPattern &glob) {
284  _ignore_sliders.push_back(glob);
285 }
286 
287 /**
288  * Returns true if the indicated name is on the list of sliders to ignore,
289  * false otherwise.
290  */
292 ignore_slider(const string &name) const {
293  Globs::const_iterator gi;
294  for (gi = _ignore_sliders.begin(); gi != _ignore_sliders.end(); ++gi) {
295  if ((*gi).matches(name)) {
296  return true;
297  }
298  }
299 
300  return false;
301 }
302 
303 /**
304  * Empties the list of force_joints added via add_force_joint(). No joints
305  * will be forced.
306  */
309  _force_joints.clear();
310 }
311 
312 /**
313  * Adds a name pattern to the list of force_joints.
314  *
315  * Any DAG node that matches a name on the list will be treated as if it were
316  * a joint during the conversion process; it will receive animation and
317  * position information. Normally, a true Maya joint, as well as any DAG
318  * nodes whose transforms are animated, will automatically be flagged as a
319  * Panda joint.
320  */
322 add_force_joint(const GlobPattern &glob) {
323  _force_joints.push_back(glob);
324 }
325 
326 /**
327  * Returns true if the indicated name is on the list of DAG nodes to treat as
328  * a joint, false otherwise.
329  */
331 force_joint(const string &name) const {
332  Globs::const_iterator gi;
333  for (gi = _force_joints.begin(); gi != _force_joints.end(); ++gi) {
334  if ((*gi).matches(name)) {
335  return true;
336  }
337  }
338 
339  return false;
340 }
341 
342 /**
343  * Sets the flag that indicates whether the currently selected Maya geometry
344  * will be converted. If this is true, and the selection is nonempty, then
345  * only the selected geometry will be converted. If this is false, the entire
346  * file will be converted.
347  */
349 set_from_selection(bool from_selection) {
350  _from_selection = from_selection;
351 }
352 
353 /**
354  * This may be called after convert_file() has been called and returned true,
355  * indicating a successful conversion. It will return the distance units
356  * represented by the converted egg file, if known, or DU_invalid if not
357  * known.
358  */
360 get_input_units() {
361  return _maya->get_units();
362 }
363 
364 /**
365  * Fills up the egg_data structure according to the global maya model data.
366  * Returns true if successful, false if there is an error.
367  */
369 convert_maya() {
370  clear();
371  clear_error();
372 
373  if (!open_api()) {
374  mayaegg_cat.error()
375  << "Maya is not available.\n";
376  return false;
377  }
378 
379  if (_egg_data->get_coordinate_system() == CS_default) {
380  _egg_data->set_coordinate_system(_maya->get_coordinate_system());
381  }
382 
383  mayaegg_cat.info()
384  << "Converting from Maya.\n";
385 
386  // Figure out the animation parameters.
387  double start_frame, end_frame, frame_inc, input_frame_rate, output_frame_rate;
388  if (has_start_frame()) {
389  start_frame = get_start_frame();
390  } else {
391  start_frame = MAnimControl::minTime().value();
392  }
393  if (has_end_frame()) {
394  end_frame = get_end_frame();
395  } else {
396  end_frame = MAnimControl::maxTime().value();
397  // end_frame = MAnimControl::animationEndTime().value(); masad: we could
398  // use this
399  }
400  if (has_frame_inc()) {
401  frame_inc = get_frame_inc();
402  } else {
403  frame_inc = 1.0;
404  }
405  if (has_input_frame_rate()) {
406  input_frame_rate = get_input_frame_rate();
407  } else {
408  MTime time(1.0, MTime::kSeconds);
409  input_frame_rate = time.as(MTime::uiUnit());
410  }
411  if (has_output_frame_rate()) {
412  output_frame_rate = get_output_frame_rate();
413  } else {
414  output_frame_rate = input_frame_rate;
415  }
416 
417  frame_inc = frame_inc * input_frame_rate / output_frame_rate;
418 
419  bool all_ok = _tree.build_hierarchy();
420 
421  if (all_ok) {
422  if (!_subroots.empty()) {
423  Globs::const_iterator gi;
424  for (gi = _subroots.begin(); gi != _subroots.end(); ++gi) {
425  if (!_tree.tag_joint_named(*gi)) {
426  mayaegg_cat.info()
427  << "No node matching " << *gi << " found.\n";
428  }
429  }
430 
431  } else {
432  // This call makes every node a potential joint; but it does not
433  // necessarily force nodes to be joints.
434  _tree.tag_joint_all();
435  }
436  }
437 
438  if (all_ok) {
439  if (_from_selection) {
440  all_ok = _tree.tag_selected();
441  } else if (!_subsets.empty()) {
442  Globs::const_iterator gi;
443  for (gi = _subsets.begin(); gi != _subsets.end(); ++gi) {
444  if (!_tree.tag_named(*gi)) {
445  mayaegg_cat.info()
446  << "No node matching " << *gi << " found.\n";
447  }
448  }
449 
450  } else {
451  _tree.tag_all();
452  }
453  }
454 
455  if (all_ok) {
456  if (!_excludes.empty()) {
457  Globs::const_iterator gi;
458  for (gi = _excludes.begin(); gi != _excludes.end(); ++gi) {
459  if (!_tree.untag_named(*gi)) {
460  mayaegg_cat.info()
461  << "No node matching " << *gi << " found.\n";
462  }
463  }
464  }
465  }
466 
467  if (all_ok) {
468  switch (get_animation_convert()) {
469  case AC_pose:
470  // pose: set to a specific frame, then get out the static geometry.
471  mayaegg_cat.info(false)
472  << "frame " << start_frame << "\n";
473  MGlobal::viewFrame(MTime(start_frame, MTime::uiUnit()));
474  // fall through
475 
476  case AC_none:
477  // none: just get out a static model, no animation.
478  mayaegg_cat.info() << "ac_none" << endl;
479  all_ok = convert_hierarchy(get_egg_data());
480  break;
481 
482  case AC_flip:
483  case AC_strobe:
484  // flip or strobe: get out a series of static models, one per frame,
485  // under a sequence node for AC_flip.
486  all_ok = convert_flip(start_frame, end_frame, frame_inc,
487  output_frame_rate);
488  break;
489 
490  case AC_model:
491  // model: get out an animatable model with joints and vertex membership.
492  all_ok = convert_char_model();
493  break;
494 
495  case AC_chan:
496  // chan: get out a series of animation tables.
497  all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
498  output_frame_rate);
499  break;
500 
501  case AC_both:
502  // both: Put a model and its animation into the same egg file.
503  _animation_convert = AC_model;
504  if (!convert_char_model()) {
505  all_ok = false;
506  }
507  _animation_convert = AC_chan;
508  if (!convert_char_chan(start_frame, end_frame, frame_inc,
509  output_frame_rate)) {
510  all_ok = false;
511  }
512  break;
513 
514  case AC_invalid:
515  break;
516  };
517 
518  reparent_decals(get_egg_data());
519  }
520 
521  if (had_error()) {
522  all_ok = false;
523  }
524 
525  if (all_ok) {
526  mayaegg_cat.info()
527  << "Converted, no errors.\n";
528  } else {
529  mayaegg_cat.info()
530  << "Errors encountered in conversion.\n";
531  }
532 
533  return all_ok;
534 }
535 
536 /**
537  * Attempts to open the Maya API if it was not already open, and returns true
538  * if successful, or false if there is an error.
539  */
541 open_api(bool revert_directory) {
542 
543  if (_maya == nullptr || !_maya->is_valid()) {
544  // maya to egg converter only needs a read license. only egg2maya need
545  // write lisences.
546  _maya = MayaApi::open_api(_program_name, true, revert_directory);
547  }
548  return _maya->is_valid();
549 }
550 
551 /**
552  * Closes the Maya API, if it was previously opened. Caution! Maya appears
553  * to call exit() when its API is closed.
554  */
556 close_api() {
557  // We have to clear the shaders, at least, before we release the Maya API.
558  clear();
559  _maya.clear();
560 }
561 
562 /**
563  * Frees all of the Maya pointers kept within this object, in preparation for
564  * loading a new scene or releasing the Maya API.
565  */
567 clear() {
568  _tree.clear();
569  _textures.clear();
570  _shaders.clear();
571 }
572 
573 /**
574  * Converts the animation as a series of models that cycle (flip) from one to
575  * the next at the appropriate frame rate. This is the most likely to convert
576  * precisely (since we ask Maya to tell us the vertex position each time) but
577  * it is the most wasteful in terms of memory utilization (since a complete of
578  * the model is stored for each frame).
579  */
580 bool MayaToEggConverter::
581 convert_flip(double start_frame, double end_frame, double frame_inc,
582  double output_frame_rate) {
583  bool all_ok = true;
584 
585  EggGroup *sequence_node = new EggGroup(_character_name);
586  get_egg_data()->add_child(sequence_node);
587  if (_animation_convert == AC_flip) {
588  sequence_node->set_switch_flag(true);
589  sequence_node->set_switch_fps(output_frame_rate);
590  }
591 
592  MTime frame(start_frame, MTime::uiUnit());
593  MTime frame_stop(end_frame, MTime::uiUnit());
594  while (frame <= frame_stop) {
595  mayaegg_cat.info(false)
596  << "frame " << frame.value() << "\n";
597  std::ostringstream name_strm;
598  name_strm << "frame" << frame.value();
599  EggGroup *frame_root = new EggGroup(name_strm.str());
600  sequence_node->add_child(frame_root);
601 
602  MGlobal::viewFrame(frame);
603  if (!convert_hierarchy(frame_root)) {
604  all_ok = false;
605  }
606 
607  frame += frame_inc;
608  }
609 
610  return all_ok;
611 }
612 
613 /**
614  * Converts the file as an animatable character model, with joints and vertex
615  * membership.
616  */
617 bool MayaToEggConverter::
618 convert_char_model() {
619  if (has_neutral_frame()) {
620  MTime frame(get_neutral_frame(), MTime::uiUnit());
621  mayaegg_cat.info(false)
622  << "neutral frame " << frame.value() << "\n";
623  MGlobal::viewFrame(frame);
624  }
625 
626  // It's also important for us to reset all the blend shape sliders to 0
627  // before we get out the model. Otherwise, the model we convert will have
628  // the current positions of the sliders baked in.
629  _tree.reset_sliders();
630 
631  EggGroup *char_node = new EggGroup(_character_name);
632  get_egg_data()->add_child(char_node);
633  char_node->set_dart_type(EggGroup::DT_default);
634 
635  return convert_hierarchy(char_node);
636 }
637 
638 /**
639  * Converts the animation as a series of tables to apply to the character
640  * model, as retrieved earlier via AC_model.
641  */
642 bool MayaToEggConverter::
643 convert_char_chan(double start_frame, double end_frame, double frame_inc,
644  double output_frame_rate) {
645  // MStatus status;
646 
647  EggTable *root_table_node = new EggTable();
648  get_egg_data()->add_child(root_table_node);
649  EggTable *bundle_node = new EggTable(_character_name);
650  bundle_node->set_table_type(EggTable::TT_bundle);
651  root_table_node->add_child(bundle_node);
652  EggTable *skeleton_node = new EggTable("<skeleton>");
653  bundle_node->add_child(skeleton_node);
654  EggTable *morph_node = new EggTable("morph");
655  bundle_node->add_child(morph_node);
656 
657  // Set the frame rate before we start asking for anim tables to be created.
658  _tree._fps = output_frame_rate;
659  _tree.clear_egg(get_egg_data(), nullptr, skeleton_node, morph_node);
660 
661  // Now we can get the animation data by walking through all of the frames,
662  // one at a time, and getting the joint angles at each frame.
663 
664  // This is just a temporary EggGroup to receive the transform for each joint
665  // each frame.
666  PT(EggGroup) tgroup = new EggGroup;
667 
668  int num_nodes = _tree.get_num_nodes();
669  int num_sliders = _tree.get_num_blend_descs();
670  int i;
671 
672  MTime frame(start_frame, MTime::uiUnit());
673  MTime frame_stop(end_frame, MTime::uiUnit());
674  while (frame <= frame_stop) {
675  if (mayaegg_cat.is_spam()) {
676  mayaegg_cat.spam(false)
677  << "frame " << frame.value() << "\n";
678  } else {
679  // We have to write to cerr instead of mayaegg_cat to allow flushing
680  // without writing a newline.
681  std::cerr << "." << std::flush;
682  }
683  MGlobal::viewFrame(frame);
684 
685  for (i = 0; i < num_nodes; i++) {
686  MayaNodeDesc *node_desc = _tree.get_node(i);
687  if (node_desc->is_joint()) {
688  if (mayaegg_cat.is_spam()) {
689  mayaegg_cat.spam()
690  << "joint " << node_desc->get_name() << "\n";
691  }
692  get_joint_transform(node_desc->get_dag_path(), tgroup);
693  EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
694  if (!anim->add_data(tgroup->get_transform3d())) {
695  mayaegg_cat.error()
696  << "Invalid transform on " << node_desc->get_name()
697  << " frame " << frame.value() << ".\n";
698  }
699  }
700  }
701 
702  for (i = 0; i < num_sliders; i++) {
703  MayaBlendDesc *blend_desc = _tree.get_blend_desc(i);
704  if (mayaegg_cat.is_spam()) {
705  mayaegg_cat.spam()
706  << "slider " << blend_desc->get_name() << "\n";
707  }
708  EggSAnimData *anim = _tree.get_egg_slider(blend_desc);
709  anim->add_data(blend_desc->get_slider());
710  }
711 
712  frame += frame_inc;
713  }
714 
715  // Now optimize all of the tables we just filled up, for no real good
716  // reason, except that it makes the resulting egg file a little easier to
717  // read.
718  for (i = 0; i < num_nodes; i++) {
719  MayaNodeDesc *node_desc = _tree.get_node(i);
720  if (node_desc->is_joint()) {
721  _tree.get_egg_anim(node_desc)->optimize();
722  }
723  }
724 
725  for (i = 0; i < num_sliders; i++) {
726  MayaBlendDesc *blend_desc = _tree.get_blend_desc(i);
727  EggSAnimData *anim = _tree.get_egg_slider(blend_desc);
728  anim->optimize();
729  }
730 
731  mayaegg_cat.info(false)
732  << "\n";
733 
734  return true;
735 }
736 
737 /**
738  * Generates egg structures for each node in the Maya hierarchy.
739  */
740 bool MayaToEggConverter::
741 convert_hierarchy(EggGroupNode *egg_root) {
742  int num_nodes = _tree.get_num_nodes();
743 
744  if (_round_uvs) {
745  mayaegg_cat.info() << "will round up uv coordinates" << endl;
746  }
747 
748  if (_keep_all_uvsets) {
749  mayaegg_cat.info() << "will keep_all_uvsets" << endl;
750  }
751  if (_polygon_output) {
752  mayaegg_cat.info() << "will convert NURBS to polys" << endl;
753  }
754  if (_convert_cameras) {
755  mayaegg_cat.info() << "will convert camera nodes to locators" << endl;
756  }
757  if (_convert_lights) {
758  mayaegg_cat.info() << "will convert light nodes to locators" << endl;
759  }
760  // give some feedback about whether special options are on
761  if (_legacy_shader) {
762  mayaegg_cat.info() << "will disable modern Phong shader path. using legacy" << endl;
763  }
764  _tree.clear_egg(get_egg_data(), egg_root, nullptr, nullptr);
765  for (int i = 0; i < num_nodes; i++) {
766  MayaNodeDesc *node = _tree.get_node(i);
767  if (!process_model_node(node)) {
768  return false;
769  }
770  }
771  return true;
772 }
773 
774 /**
775  * Converts the indicated Maya node (given a MDagPath, similar in concept to
776  * Panda's NodePath) to the corresponding Egg structure. Returns true if
777  * successful, false if an error was encountered.
778  */
779 bool MayaToEggConverter::
780 process_model_node(MayaNodeDesc *node_desc) {
781  if (!node_desc->has_dag_path()) {
782  // If the node has no Maya equivalent, never mind.
783  return true;
784  }
785 
786  MDagPath dag_path = node_desc->get_dag_path();
787 
788  MStatus status;
789  MFnDagNode dag_node(dag_path, &status);
790  if (!status) {
791  status.perror("MFnDagNode constructor");
792  mayaegg_cat.error() << dag_path.fullPathName().asChar() << "\n";
793  return false;
794  }
795 
796  MObject node = dag_path.transform(&status);
797  if (!status) {
798  status.perror("dag_path.transform()");
799  return false;
800  }
801 
802  string path = dag_path.fullPathName().asChar();
803 
804  if (mayaegg_cat.is_debug()) {
805  mayaegg_cat.debug()
806  << path << ": " << dag_node.typeName().asChar();
807 
808  if (MAnimUtil::isAnimated(dag_path)) {
809  mayaegg_cat.debug(false)
810  << " (animated)";
811  }
812 
813  mayaegg_cat.debug(false) << "\n";
814  }
815 
816  if (dag_node.inUnderWorld()) {
817  if (mayaegg_cat.is_debug()) {
818  mayaegg_cat.debug()
819  << "Ignoring underworld node " << path
820  << "\n";
821  }
822 
823  } else if (dag_node.isIntermediateObject()) {
824  if (mayaegg_cat.is_debug()) {
825  mayaegg_cat.debug()
826  << "Ignoring intermediate object " << path
827  << "\n";
828  }
829 
830  } else if (dag_path.hasFn(MFn::kCamera)) {
831  if (_convert_cameras) {
832  MFnCamera camera (dag_path, &status);
833  if ( !status ) {
834  status.perror("MFnCamera constructor");
835  return false;
836  }
837 
838  // Extract some interesting Camera data
839  if (mayaegg_cat.is_spam()) {
840  MPoint eyePoint = camera.eyePoint(MSpace::kWorld);
841  MVector upDirection = camera.upDirection(MSpace::kWorld);
842  MVector viewDirection = camera.viewDirection(MSpace::kWorld);
843  mayaegg_cat.spam() << " eyePoint: " << eyePoint.x << " "
844  << eyePoint.y << " " << eyePoint.z << endl;
845  mayaegg_cat.spam() << " upDirection: " << upDirection.x << " "
846  << upDirection.y << " " << upDirection.z << endl;
847  mayaegg_cat.spam() << " viewDirection: " << viewDirection.x << " "
848  << viewDirection.y << " " << viewDirection.z << endl;
849  mayaegg_cat.spam() << " aspectRatio: " << camera.aspectRatio() << endl;
850  mayaegg_cat.spam() << " horizontalFilmAperture: "
851  << camera.horizontalFilmAperture() << endl;
852  mayaegg_cat.spam() << " verticalFilmAperture: "
853  << camera.verticalFilmAperture() << endl;
854  }
855 
856  EggGroup *egg_group = _tree.get_egg_group(node_desc);
857 
858  if (mayaegg_cat.is_debug()) {
859  mayaegg_cat.warning()
860  << "Saving camera nodes as a locator: " << path << "\n";
861  }
862 
863  if (node_desc->is_tagged()) {
864  // Presumably, the camera's position has some meaning to the end-user,
865  // so we will implicitly tag it with the DCS flag so it won't get
866  // flattened out.
867  if (_animation_convert != AC_model) {
868  // For now, don't set the DCS flag on cameras within character
869  // models, since egg-optchar doesn't understand this. Perhaps
870  // there's no reason to ever change this, since cameras within
871  // character models may not be meaningful.
872  egg_group->set_dcs_type(EggGroup::DC_net);
873  }
874  get_transform(node_desc, dag_path, egg_group);
875  make_camera_locator(dag_path, dag_node, egg_group);
876  } else {
877  if (mayaegg_cat.is_debug()) {
878  mayaegg_cat.debug()
879  << "Ignoring camera node " << path
880  << "\n";
881  }
882  }
883  }
884 
885  } else if (dag_path.hasFn(MFn::kLight)) {
886  if (_convert_lights) {
887  MFnLight light (dag_path, &status);
888  if ( !status ) {
889  status.perror("MFnLight constructor");
890  return false;
891  }
892 
893  EggGroup *egg_group = _tree.get_egg_group(node_desc);
894 
895  if (mayaegg_cat.is_debug()) {
896  mayaegg_cat.warning() << "Saving light node as a locator: " << path << endl;
897  }
898 
899  if (node_desc->is_tagged()) {
900  // Presumably, the lighht's position has some meaning to the end-user,
901  // so we will implicitly tag it with the DCS flag so it won't get
902  // flattened out.
903  if (_animation_convert != AC_model) {
904  // For now, don't set the DCS flag on lights within character
905  // models, since egg-optchar doesn't understand this. Perhaps
906  // there's no reason to ever change this, since lights within
907  // character models may not be meaningful.
908  egg_group->set_dcs_type(EggGroup::DC_net);
909  }
910  get_transform(node_desc, dag_path, egg_group);
911  make_light_locator(dag_path, dag_node, egg_group);
912  } else {
913  if (mayaegg_cat.is_debug()) {
914  mayaegg_cat.debug()
915  << "Ignoring light node " << path
916  << "\n";
917  }
918  }
919  }
920 
921  MFnLight light (dag_path, &status);
922  if ( !status ) {
923  status.perror("MFnLight constructor");
924  mayaegg_cat.error() << "light extraction failed" << endl;
925  return false;
926  }
927 
928  if (mayaegg_cat.is_info()) {
929  MString name = dag_path.partialPathName();
930  mayaegg_cat.info() << "-- Light found -- tranlations in cm, rotations in rads\n";
931  mayaegg_cat.info() << "\"" << name.asChar() << "\" : \n";
932  }
933 
934  // Get the translationrotationscale data
935  MObject transformNode = dag_path.transform(&status);
936  // This node has no transform - i.e., it's the world node
937  if (!status && status.statusCode () == MStatus::kInvalidParameter)
938  return false;
939  MFnDagNode transform (transformNode, &status);
940  if (!status) {
941  status.perror("MFnDagNode constructor");
942  return false;
943  }
944  MTransformationMatrix matrix (transform.transformationMatrix());
945  MVector tl = matrix.translation(MSpace::kWorld);
946  // Stop rediculously small values like -4.43287e-013
947  if (tl.x < 0.0001) {
948  tl.x = 0;
949  }
950  if (tl.y < 0.0001) {
951  tl.y = 0;
952  }
953  if (tl.z < 0.0001) {
954  tl.z = 0;
955  }
956  // We swap Y and Z in the next few bits cuz Panda is Z-up by default and
957  // Maya is Y-up
958  mayaegg_cat.info() << " \"translation\" : (" << tl.x << ", " << tl.z << ", " << tl.y << ")"
959  << endl;
960  double threeDoubles[3];
961  MTransformationMatrix::RotationOrder rOrder;
962 
963  matrix.getRotation (threeDoubles, rOrder, MSpace::kWorld);
964  mayaegg_cat.info() << " \"rotation\": ("
965  << threeDoubles[0] << ", "
966  << threeDoubles[2] << ", "
967  << threeDoubles[1] << ")\n";
968  matrix.getScale (threeDoubles, MSpace::kWorld);
969  mayaegg_cat.info() << " \"scale\" : ("
970  << threeDoubles[0] << ", "
971  << threeDoubles[2] << ", "
972  << threeDoubles[1] << ")\n";
973 
974  // Extract some interesting Light data
975  MColor color;
976  color = light.color();
977  mayaegg_cat.info() << " \"color\" : ("
978  << color.r << ", "
979  << color.g << ", "
980  << color.b << ")\n";
981  color = light.shadowColor();
982  mayaegg_cat.info() << " \"intensity\" : " << light.intensity() << endl;
983 
984  } else if (dag_path.hasFn(MFn::kNurbsSurface)) {
985  EggGroup *egg_group = _tree.get_egg_group(node_desc);
986  get_transform(node_desc, dag_path, egg_group);
987 
988  if (node_desc->is_tagged()) {
989  MFnNurbsSurface surface(dag_path, &status);
990  if (!status) {
991  mayaegg_cat.info()
992  << "Error in node " << path
993  << ":\n"
994  << " it appears to have a NURBS surface, but does not.\n";
995  } else {
996  make_nurbs_surface(node_desc, dag_path, surface, egg_group);
997  }
998  }
999  } else if (dag_path.hasFn(MFn::kNurbsCurve)) {
1000  // Only convert NurbsCurves if we aren't making an animated model.
1001  // Animated models, as a general rule, don't want these sorts of things in
1002  // them.
1003  if (_animation_convert != AC_model) {
1004  EggGroup *egg_group = _tree.get_egg_group(node_desc);
1005  get_transform(node_desc, dag_path, egg_group);
1006 
1007  if (node_desc->is_tagged()) {
1008  MFnNurbsCurve curve(dag_path, &status);
1009  if (!status) {
1010  mayaegg_cat.info()
1011  << "Error in node " << path << ":\n"
1012  << " it appears to have a NURBS curve, but does not.\n";
1013  } else {
1014  make_nurbs_curve(dag_path, curve, egg_group);
1015  }
1016  }
1017  }
1018 
1019  } else if (dag_path.hasFn(MFn::kMesh)) {
1020  if (node_desc->is_tagged()) {
1021  EggGroup *egg_group = _tree.get_egg_group(node_desc);
1022  get_transform(node_desc, dag_path, egg_group);
1023  MFnMesh mesh(dag_path, &status);
1024  if (!status) {
1025  mayaegg_cat.info()
1026  << "Error in node " << path << ":\n"
1027  << " it appears to have a polygon mesh, but does not.\n";
1028  } else {
1029  make_polyset(node_desc, dag_path, mesh, egg_group);
1030  }
1031  }
1032  /*
1033  EggGroup *egg_group = _tree.get_egg_group(node_desc);
1034  get_transform(node_desc, dag_path, egg_group);
1035 
1036  if (node_desc->is_tagged()) {
1037  MFnMesh mesh(dag_path, &status);
1038  if (!status) {
1039  mayaegg_cat.info()
1040  << "Error in node " << path << ":\n"
1041  << " it appears to have a polygon mesh, but does not.\n";
1042  } else {
1043  make_polyset(node_desc, dag_path, mesh, egg_group);
1044  }
1045  }
1046  */
1047  } else if (dag_path.hasFn(MFn::kLocator)) {
1048  if (_animation_convert == AC_none) {
1049  if (!node_desc->is_tagged()) {
1050  return true;
1051  }
1052  }
1053  EggGroup *egg_group = _tree.get_egg_group(node_desc);
1054 
1055  if (mayaegg_cat.is_debug()) {
1056  mayaegg_cat.debug()
1057  << "Locator at " << path << "\n";
1058  }
1059 
1060  if (node_desc->is_tagged()) {
1061  // Presumably, the locator's position has some meaning to the end-user,
1062  // so we will implicitly tag it with the DCS flag so it won't get
1063  // flattened out.
1064  if (_animation_convert != AC_model) {
1065  // For now, don't set the DCS flag on locators within character
1066  // models, since egg-optchar doesn't understand this. Perhaps there's
1067  // no reason to ever change this, since locators within character
1068  // models may not be meaningful.
1069  egg_group->set_dcs_type(EggGroup::DC_net);
1070  }
1071  get_transform(node_desc, dag_path, egg_group);
1072  make_locator(dag_path, dag_node, egg_group);
1073  }
1074 
1075  } else {
1076  // Just a generic node.
1077  if (_animation_convert == AC_none) {
1078  if (!node_desc->is_tagged()) {
1079  return true;
1080  }
1081  }
1082  EggGroup *egg_group = _tree.get_egg_group(node_desc);
1083  get_transform(node_desc, dag_path, egg_group);
1084  }
1085 
1086  return true;
1087 }
1088 
1089 /**
1090  * Extracts the transform on the indicated Maya node, and applies it to the
1091  * corresponding Egg node.
1092  */
1093 void MayaToEggConverter::
1094 get_transform(MayaNodeDesc *node_desc, const MDagPath &dag_path,
1095  EggGroup *egg_group) {
1096  if (_animation_convert == AC_model) {
1097  // When we're getting an animated model, we only get transforms for
1098  // joints, and they get converted in a special way.
1099 
1100  if (node_desc->is_joint()) {
1101  if (mayaegg_cat.is_spam()) {
1102  mayaegg_cat.spam()
1103  << "gt: joint " << node_desc->get_name() << "\n";
1104  }
1105  get_joint_transform(dag_path, egg_group);
1106  }
1107  return;
1108  }
1109 
1110  MStatus status;
1111  MObject transformNode = dag_path.transform(&status);
1112  if (!status && status.statusCode() == MStatus::kInvalidParameter) {
1113  // This node has no transform - i.e., it's the world node
1114  return;
1115  }
1116 
1117  // Billboards always get the transform set.
1118  if (egg_group->get_billboard_type() == EggGroup::BT_none) {
1119  switch (_transform_type) {
1120  case TT_all:
1121  break;
1122 
1123  case TT_model:
1124  if (!egg_group->get_model_flag() && !egg_group->has_dcs_type()) {
1125  return;
1126  }
1127  break;
1128 
1129  case TT_dcs:
1130  if (!egg_group->has_dcs_type()) {
1131  return;
1132  }
1133  break;
1134 
1135  case TT_none:
1136  case TT_invalid:
1137  return;
1138  }
1139  }
1140 
1141  // Extract the matrix from the dag path.
1142  MMatrix mat = dag_path.inclusiveMatrix(&status);
1143  if (!status) {
1144  status.perror("Can't get transform matrix");
1145  return;
1146  }
1147  LMatrix4d m4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
1148  mat[1][0], mat[1][1], mat[1][2], mat[1][3],
1149  mat[2][0], mat[2][1], mat[2][2], mat[2][3],
1150  mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
1151 
1152  // Maya has a rotate pivot, separate from its transform. Usually we care
1153  // more about the rotate pivot than we do about the transform, so get the
1154  // rotate pivot too.
1155  MFnTransform transform(transformNode, &status);
1156  if (!status) {
1157  status.perror("MFnTransform constructor");
1158  return;
1159  }
1160  MPoint pivot = transform.rotatePivot(MSpace::kObject, &status);
1161  if (!status) {
1162  status.perror("Can't get rotate pivot");
1163  return;
1164  }
1165 
1166  // We need to convert the pivot to world coordinates. (Maya can only tell
1167  // it to us in local coordinates.)
1168  LPoint3d p3d(pivot[0], pivot[1], pivot[2]);
1169  p3d = p3d * m4d;
1170 
1171  // Now recenter the matrix about the pivot point.
1172  m4d.set_row(3, p3d);
1173 
1174  // Convert the recentered matrix into the group's space and store it.
1175  m4d = m4d * egg_group->get_node_frame_inv();
1176  if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
1177  egg_group->add_matrix4(m4d);
1178  }
1179  return;
1180 }
1181 
1182 /**
1183  * Extracts the transform on the indicated Maya node, as appropriate for a
1184  * joint in an animated character, and applies it to the indicated node. This
1185  * is different from get_transform() in that it does not respect the
1186  * _transform_type flag, and it does not consider the relative transforms
1187  * within the egg file.
1188  */
1189 void MayaToEggConverter::
1190 get_joint_transform(const MDagPath &dag_path, EggGroup *egg_group) {
1191  // First, make sure there's not a transform on the group already.
1192  egg_group->clear_transform();
1193 
1194  MStatus status;
1195  MObject transformNode = dag_path.transform(&status);
1196  // This node has no transform - i.e., it's the world node
1197  if (!status && status.statusCode() == MStatus::kInvalidParameter) {
1198  return;
1199  }
1200 
1201  MFnDagNode transform(transformNode, &status);
1202  if (!status) {
1203  status.perror("MFnDagNode constructor");
1204  return;
1205  }
1206 
1207  MTransformationMatrix matrix(transform.transformationMatrix());
1208 
1209  if (mayaegg_cat.is_spam()) {
1210  MVector t = matrix.translation(MSpace::kWorld);
1211  mayaegg_cat.spam()
1212  << " translation: ["
1213  << t[0] << ", "
1214  << t[1] << ", "
1215  << t[2] << "]\n";
1216  double d[3];
1217  MTransformationMatrix::RotationOrder rOrder;
1218 
1219  matrix.getRotation(d, rOrder, MSpace::kWorld);
1220  mayaegg_cat.spam()
1221  << " rotation: ["
1222  << d[0] << ", "
1223  << d[1] << ", "
1224  << d[2] << "]\n";
1225  matrix.getScale(d, MSpace::kWorld);
1226  mayaegg_cat.spam()
1227  << " scale: ["
1228  << d[0] << ", "
1229  << d[1] << ", "
1230  << d[2] << "]\n";
1231  matrix.getShear(d, MSpace::kWorld);
1232  mayaegg_cat.spam()
1233  << " shear: ["
1234  << d[0] << ", "
1235  << d[1] << ", "
1236  << d[2] << "]\n";
1237  }
1238 
1239  MMatrix mat = matrix.asMatrix();
1240  MMatrix ident_mat;
1241  ident_mat.setToIdentity();
1242 
1243  if (!mat.isEquivalent(ident_mat, 0.0001)) {
1244  egg_group->set_transform3d
1245  (LMatrix4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
1246  mat[1][0], mat[1][1], mat[1][2], mat[1][3],
1247  mat[2][0], mat[2][1], mat[2][2], mat[2][3],
1248  mat[3][0], mat[3][1], mat[3][2], mat[3][3]));
1249  }
1250 }
1251 
1252 /**
1253  * Converts the indicated Maya NURBS surface to a corresponding egg structure,
1254  * and attaches it to the indicated egg group.
1255  */
1256 void MayaToEggConverter::
1257 make_nurbs_surface(MayaNodeDesc *node_desc, const MDagPath &dag_path,
1258  MFnNurbsSurface &surface, EggGroup *egg_group) {
1259  MStatus status;
1260  string name = surface.name().asChar();
1261 
1262  if (mayaegg_cat.is_spam()) {
1263  mayaegg_cat.spam()
1264  << " numCVs: "
1265  << surface.numCVsInU()
1266  << " * "
1267  << surface.numCVsInV()
1268  << "\n";
1269  mayaegg_cat.spam()
1270  << " numKnots: "
1271  << surface.numKnotsInU()
1272  << " * "
1273  << surface.numKnotsInV()
1274  << "\n";
1275  mayaegg_cat.spam()
1276  << " numSpans: "
1277  << surface.numSpansInU()
1278  << " * "
1279  << surface.numSpansInV()
1280  << "\n";
1281  }
1282  MayaShader *shader = _shaders.find_shader_for_node(surface.object(), _legacy_shader);
1283 
1284  if (_polygon_output) {
1285  // If we want polygon output only, tesselate the NURBS and output that.
1286  MTesselationParams params;
1287  params.setFormatType(MTesselationParams::kStandardFitFormat);
1288  params.setOutputType(MTesselationParams::kQuads);
1289  params.setStdFractionalTolerance(_polygon_tolerance);
1290 
1291  // We'll create the tesselation as a sibling of the NURBS surface. That
1292  // way we inherit all of the transformations.
1293  MDagPath polyset_path = dag_path;
1294  MObject polyset_parent = polyset_path.node();
1295  MObject polyset =
1296  surface.tesselate(params, polyset_parent, &status);
1297  if (!status) {
1298  status.perror("MFnNurbsSurface::tesselate");
1299  return;
1300  }
1301 
1302  status = polyset_path.push(polyset);
1303  if (!status) {
1304  status.perror("MDagPath::push");
1305  }
1306 
1307  MFnMesh polyset_fn(polyset, &status);
1308  if (!status) {
1309  status.perror("MFnMesh constructor");
1310  return;
1311  }
1312  make_polyset(node_desc, polyset_path, polyset_fn, egg_group, shader);
1313 
1314  // Now remove the polyset we created.
1315  MFnDagNode parent_node(polyset_parent, &status);
1316  if (!status) {
1317  status.perror("MFnDagNode constructor");
1318  return;
1319  }
1320  status = parent_node.removeChild(polyset);
1321  if (!status) {
1322  status.perror("MFnDagNode::removeChild");
1323  }
1324 
1325  return;
1326  }
1327 
1328  MPointArray cv_array;
1329  status = surface.getCVs(cv_array, MSpace::kWorld);
1330  if (!status) {
1331  status.perror("MFnNurbsSurface::getCVs");
1332  return;
1333  }
1334 
1335  // Also get out all the alternate blend shapes for the surface by applying
1336  // each morph slider one at a time.
1337  pvector<MPointArray> morph_cvs;
1338  if (_animation_convert == AC_model) {
1339  int num_sliders = node_desc->get_num_blend_descs();
1340  morph_cvs.reserve(num_sliders);
1341  for (int i = 0; i < num_sliders; i++) {
1342  MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i);
1343 
1344  // Temporarily push the slider up to 1.0 so we can see what the surface
1345  // looks like at that value.
1346  blend_desc->set_slider(1.0);
1347  MPointArray cv_array;
1348  status = surface.getCVs(cv_array, MSpace::kWorld);
1349  blend_desc->set_slider(0.0);
1350 
1351  if (!status) {
1352  status.perror("MFnNurbsSurface::getCVs");
1353  return;
1354  }
1355  morph_cvs.push_back(cv_array);
1356  }
1357  }
1358 
1359  MDoubleArray u_knot_array, v_knot_array;
1360  status = surface.getKnotsInU(u_knot_array);
1361  if (!status) {
1362  status.perror("MFnNurbsSurface::getKnotsInU");
1363  return;
1364  }
1365  status = surface.getKnotsInV(v_knot_array);
1366  if (!status) {
1367  status.perror("MFnNurbsSurface::getKnotsInV");
1368  return;
1369  }
1370 
1371  MFnNurbsSurface::Form u_form = surface.formInU();
1372  MFnNurbsSurface::Form v_form = surface.formInV();
1373 
1374  int u_degree = surface.degreeU();
1375  int v_degree = surface.degreeV();
1376 
1377  int u_cvs = surface.numCVsInU();
1378  int v_cvs = surface.numCVsInV();
1379 
1380  // Maya repeats CVS at the end for a periodic surface, and doesn't count
1381  // them in the joint weight array, below.
1382  int maya_u_cvs = (u_form == MFnNurbsSurface::kPeriodic) ? u_cvs - u_degree : u_cvs;
1383  int maya_v_cvs = (v_form == MFnNurbsSurface::kPeriodic) ? v_cvs - v_degree : v_cvs;
1384 
1385  int u_knots = surface.numKnotsInU();
1386  int v_knots = surface.numKnotsInV();
1387 
1388  assert(u_knots == u_cvs + u_degree - 1);
1389  assert(v_knots == v_cvs + v_degree - 1);
1390 
1391  string vpool_name = name + ".cvs";
1392  EggVertexPool *vpool = new EggVertexPool(vpool_name);
1393  egg_group->add_child(vpool);
1394 
1395  EggNurbsSurface *egg_nurbs = new EggNurbsSurface(name);
1396  egg_nurbs->setup(u_degree + 1, v_degree + 1,
1397  u_knots + 2, v_knots + 2);
1398 
1399  int i;
1400 
1401  egg_nurbs->set_u_knot(0, u_knot_array[0]);
1402  for (i = 0; i < u_knots; i++) {
1403  egg_nurbs->set_u_knot(i + 1, u_knot_array[i]);
1404  }
1405  egg_nurbs->set_u_knot(u_knots + 1, u_knot_array[u_knots - 1]);
1406 
1407  egg_nurbs->set_v_knot(0, v_knot_array[0]);
1408  for (i = 0; i < v_knots; i++) {
1409  egg_nurbs->set_v_knot(i + 1, v_knot_array[i]);
1410  }
1411  egg_nurbs->set_v_knot(v_knots + 1, v_knot_array[v_knots - 1]);
1412 
1413  LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
1414 
1415  for (i = 0; i < egg_nurbs->get_num_cvs(); i++) {
1416  int ui = egg_nurbs->get_u_index(i);
1417  int vi = egg_nurbs->get_v_index(i);
1418  int maya_vi = v_cvs * ui + vi;
1419 
1420  double v[4];
1421  status = cv_array[maya_vi].get(v);
1422  if (!status) {
1423  status.perror("MPoint::get");
1424  } else {
1425  EggVertex *vert = vpool->add_vertex(new EggVertex, i);
1426  LPoint4d p4d(v[0], v[1], v[2], v[3]);
1427  p4d = p4d * vertex_frame_inv;
1428  vert->set_pos(p4d);
1429 
1430  // Now generate the morph targets for the vertex.
1431  if (!morph_cvs.empty()) {
1432  // Morph deltas are given in 3-d space, not in 4-d homogenous space.
1433  LPoint3d p3d(v[0] / v[3], v[1] / v[3], v[2] / v[3]);
1434 
1435  for (unsigned int si = 0; si < morph_cvs.size(); si++) {
1436  MayaBlendDesc *blend_desc = node_desc->get_blend_desc(si);
1437  status = morph_cvs[si][maya_vi].get(v);
1438  if (!status) {
1439  status.perror("MPoint::get");
1440  } else {
1441  LPoint3d m3d(v[0] / v[3], v[1] / v[3], v[2] / v[3]);
1442  LVector3d delta = m3d - p3d;
1443  if (!delta.almost_equal(LVector3d::zero())) {
1444  EggMorphVertex dxyz(blend_desc->get_name(), delta);
1445  vert->_dxyzs.insert(dxyz);
1446  }
1447  }
1448  }
1449  }
1450 
1451  egg_nurbs->add_vertex(vert);
1452  }
1453  }
1454 
1455  // Now consider the trim curves, if any.
1456  unsigned num_trims = surface.numRegions();
1457  int trim_curve_index = 0;
1458  for (unsigned ti = 0; ti < num_trims; ti++) {
1459  unsigned num_loops = surface.numBoundaries(ti);
1460 
1461  if (num_loops > 0) {
1462  egg_nurbs->_trims.push_back(EggNurbsSurface::Trim());
1463  EggNurbsSurface::Trim &egg_trim = egg_nurbs->_trims.back();
1464 
1465  for (unsigned li = 0; li < num_loops; li++) {
1466  egg_trim.push_back(EggNurbsSurface::Loop());
1467  EggNurbsSurface::Loop &egg_loop = egg_trim.back();
1468 
1469  MFnNurbsSurface::BoundaryType type =
1470  surface.boundaryType(ti, li, &status);
1471  bool keep_loop = false;
1472 
1473  if (!status) {
1474  status.perror("MFnNurbsSurface::BoundaryType");
1475  } else {
1476  keep_loop = (type == MFnNurbsSurface::kInner ||
1477  type == MFnNurbsSurface::kOuter);
1478  }
1479 
1480  if (keep_loop) {
1481  unsigned num_edges = surface.numEdges(ti, li);
1482  for (unsigned ei = 0; ei < num_edges; ei++) {
1483  MObjectArray edge = surface.edge(ti, li, ei, true, &status);
1484  if (!status) {
1485  status.perror("MFnNurbsSurface::edge");
1486  } else {
1487  unsigned num_segs = edge.length();
1488  for (unsigned si = 0; si < num_segs; si++) {
1489  MObject segment = edge[si];
1490  if (segment.hasFn(MFn::kNurbsCurve)) {
1491  MFnNurbsCurve curve(segment, &status);
1492  if (!status) {
1493  mayaegg_cat.error()
1494  << "Trim curve appears to be a nurbs curve, but isn't.\n";
1495  } else {
1496  // Finally, we have a valid curve!
1497  EggNurbsCurve *egg_curve =
1498  make_trim_curve(curve, name, egg_group, trim_curve_index);
1499  trim_curve_index++;
1500  if (egg_curve != nullptr) {
1501  egg_loop.push_back(egg_curve);
1502  }
1503  }
1504  } else {
1505  mayaegg_cat.error()
1506  << "Trim curve segment is not a nurbs curve.\n";
1507  }
1508  }
1509  }
1510  }
1511  }
1512  }
1513  }
1514  }
1515 
1516  // We add the NURBS to the group down here, after all of the vpools for the
1517  // trim curves have been added.
1518  egg_group->add_child(egg_nurbs);
1519 
1520  if (shader != nullptr) {
1521  set_shader_attributes(*egg_nurbs, *shader);
1522  }
1523 
1524  // Now try to find the skinning information for the surface.
1525  bool got_weights = false;
1526 
1527  pvector<EggGroup *> joints;
1528  MFloatArray weights;
1529  if (_animation_convert == AC_model) {
1530  got_weights =
1531  get_vertex_weights(dag_path, surface, joints, weights);
1532  }
1533 
1534  if (got_weights && !joints.empty()) {
1535  int num_joints = joints.size();
1536  int num_weights = (int)weights.length();
1537  int num_verts = num_weights / num_joints;
1538  // The number of weights should be an even multiple of verts * joints.
1539  nassertv(num_weights == num_verts * num_joints);
1540 
1541  for (i = 0; i < egg_nurbs->get_num_cvs(); i++) {
1542  int ui = egg_nurbs->get_u_index(i) % maya_u_cvs;
1543  int vi = egg_nurbs->get_v_index(i) % maya_v_cvs;
1544 
1545  int maya_vi = maya_v_cvs * ui + vi;
1546  nassertv(maya_vi < num_verts);
1547  EggVertex *vert = vpool->get_vertex(i);
1548 
1549  for (int ji = 0; ji < num_joints; ++ji) {
1550  PN_stdfloat weight = weights[maya_vi * num_joints + ji];
1551  if (weight != 0.0f) {
1552  EggGroup *joint = joints[ji];
1553  if (joint != nullptr) {
1554  joint->ref_vertex(vert, weight);
1555  }
1556  }
1557  }
1558  }
1559  }
1560 }
1561 
1562 /**
1563  * Converts the indicated Maya NURBS trim curve to a corresponding egg
1564  * structure, and returns it, or NULL if there is a problem.
1565  */
1566 EggNurbsCurve *MayaToEggConverter::
1567 make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name,
1568  EggGroupNode *egg_group, int trim_curve_index) {
1569  if (mayaegg_cat.is_spam()) {
1570  mayaegg_cat.spam()
1571  << "Trim curve:\n";
1572  mayaegg_cat.spam()
1573  << " numCVs: "
1574  << curve.numCVs()
1575  << "\n";
1576  mayaegg_cat.spam()
1577  << " numKnots: "
1578  << curve.numKnots()
1579  << "\n";
1580  mayaegg_cat.spam()
1581  << " numSpans: "
1582  << curve.numSpans()
1583  << "\n";
1584  }
1585 
1586  MStatus status;
1587 
1588  MPointArray cv_array;
1589  status = curve.getCVs(cv_array, MSpace::kWorld);
1590  if (!status) {
1591  status.perror("MFnNurbsCurve::getCVs");
1592  return nullptr;
1593  }
1594  MDoubleArray knot_array;
1595  status = curve.getKnots(knot_array);
1596  if (!status) {
1597  status.perror("MFnNurbsCurve::getKnots");
1598  return nullptr;
1599  }
1600 
1601  /*
1602  MFnNurbsCurve::Form form = curve.form();
1603  */
1604 
1605  int degree = curve.degree();
1606  int cvs = curve.numCVs();
1607  int knots = curve.numKnots();
1608 
1609  assert(knots == cvs + degree - 1);
1610 
1611  string trim_name = "trim" + format_string(trim_curve_index);
1612 
1613  string vpool_name = nurbs_name + "." + trim_name;
1614  EggVertexPool *vpool = new EggVertexPool(vpool_name);
1615  egg_group->add_child(vpool);
1616 
1617  EggNurbsCurve *egg_curve = new EggNurbsCurve(trim_name);
1618  egg_curve->setup(degree + 1, knots + 2);
1619 
1620  int i;
1621 
1622  egg_curve->set_knot(0, knot_array[0]);
1623  for (i = 0; i < knots; i++) {
1624  egg_curve->set_knot(i + 1, knot_array[i]);
1625  }
1626  egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
1627 
1628  for (i = 0; i < egg_curve->get_num_cvs(); i++) {
1629  double v[4];
1630  MStatus status = cv_array[i].get(v);
1631  if (!status) {
1632  status.perror("MPoint::get");
1633  } else {
1634  EggVertex vert;
1635  vert.set_pos(LPoint3d(v[0], v[1], v[3]));
1636  egg_curve->add_vertex(vpool->create_unique_vertex(vert));
1637  }
1638  }
1639 
1640  return egg_curve;
1641 }
1642 
1643 /**
1644  * Converts the indicated Maya NURBS curve (a standalone curve, not a trim
1645  * curve) to a corresponding egg structure and attaches it to the indicated
1646  * egg group.
1647  */
1648 void MayaToEggConverter::
1649 make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve,
1650  EggGroup *egg_group) {
1651  MStatus status;
1652  string name = curve.name().asChar();
1653 
1654  if (mayaegg_cat.is_spam()) {
1655  mayaegg_cat.spam()
1656  << " numCVs: "
1657  << curve.numCVs()
1658  << "\n";
1659  mayaegg_cat.spam()
1660  << " numKnots: "
1661  << curve.numKnots()
1662  << "\n";
1663  mayaegg_cat.spam()
1664  << " numSpans: "
1665  << curve.numSpans()
1666  << "\n";
1667  }
1668 
1669  MPointArray cv_array;
1670  status = curve.getCVs(cv_array, MSpace::kWorld);
1671  if (!status) {
1672  status.perror("MFnNurbsCurve::getCVs");
1673  return;
1674  }
1675  MDoubleArray knot_array;
1676  status = curve.getKnots(knot_array);
1677  if (!status) {
1678  status.perror("MFnNurbsCurve::getKnots");
1679  return;
1680  }
1681 
1682  /*
1683  MFnNurbsCurve::Form form = curve.form();
1684  */
1685 
1686  int degree = curve.degree();
1687  int cvs = curve.numCVs();
1688  int knots = curve.numKnots();
1689 
1690  assert(knots == cvs + degree - 1);
1691 
1692  string vpool_name = name + ".cvs";
1693  EggVertexPool *vpool = new EggVertexPool(vpool_name);
1694  egg_group->add_child(vpool);
1695 
1696  EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
1697  egg_group->add_child(egg_curve);
1698  egg_curve->setup(degree + 1, knots + 2);
1699 
1700  int i;
1701 
1702  egg_curve->set_knot(0, knot_array[0]);
1703  for (i = 0; i < knots; i++) {
1704  egg_curve->set_knot(i + 1, knot_array[i]);
1705  }
1706  egg_curve->set_knot(knots + 1, knot_array[knots - 1]);
1707 
1708  LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
1709 
1710  for (i = 0; i < egg_curve->get_num_cvs(); i++) {
1711  double v[4];
1712  MStatus status = cv_array[i].get(v);
1713  if (!status) {
1714  status.perror("MPoint::get");
1715  } else {
1716  EggVertex vert;
1717  LPoint4d p4d(v[0], v[1], v[2], v[3]);
1718  p4d = p4d * vertex_frame_inv;
1719  vert.set_pos(p4d);
1720  egg_curve->add_vertex(vpool->create_unique_vertex(vert));
1721  }
1722  }
1723  MayaShader *shader = _shaders.find_shader_for_node(curve.object(), _legacy_shader);
1724  if (shader != nullptr) {
1725  set_shader_attributes(*egg_curve, *shader);
1726  }
1727 }
1728 
1729 /**
1730  * given uvsets, round them up or down
1731  */
1732 int MayaToEggConverter::
1733 round(double value) {
1734  if (value < 0)
1735  return -(floor(-value + 0.5));
1736  // or as an alternate use: return ceil ( value - 0.5);
1737  else
1738  return floor( value + 0.5);
1739 }
1740 
1741 /**
1742  * Converts the indicated Maya polyset to a bunch of EggPolygons and parents
1743  * them to the indicated egg group.
1744  */
1745 void MayaToEggConverter::
1746 make_polyset(MayaNodeDesc *node_desc, const MDagPath &dag_path,
1747  const MFnMesh &mesh, EggGroup *egg_group,
1748  MayaShader *default_shader) {
1749  MStatus status;
1750  string name = mesh.name().asChar();
1751 
1752  MObject mesh_object = mesh.object();
1753  bool maya_double_sided = false;
1754  get_bool_attribute(mesh_object, "doubleSided", maya_double_sided);
1755 
1756  if (mayaegg_cat.is_spam()) {
1757  mayaegg_cat.spam()
1758  << " numPolygons: "
1759  << mesh.numPolygons()
1760  << "\n";
1761  mayaegg_cat.spam()
1762  << " numVertices: "
1763  << mesh.numVertices()
1764  << "\n";
1765  }
1766 
1767  if (mesh.numPolygons() == 0) {
1768  if (mayaegg_cat.is_debug()) {
1769  mayaegg_cat.debug()
1770  << "Ignoring empty mesh " << name << "\n";
1771  }
1772  return;
1773  }
1774 
1775  string vpool_name = name + ".verts";
1776  EggVertexPool *vpool = new EggVertexPool(vpool_name);
1777  egg_group->add_child(vpool);
1778 
1779  // One way to convert the mesh would be to first get out all the vertices in
1780  // the mesh and add them into the vpool, then when we traverse the polygons
1781  // we would only have to index them into the vpool according to their Maya
1782  // vertex index.
1783 
1784  // Unfortunately, since Maya may store multiple normals andor colors for
1785  // each vertex according to which polygon it is in, that approach won't
1786  // necessarily work. In egg, those split-property vertices have to become
1787  // separate vertices. So instead of adding all the vertices up front, we'll
1788  // start with an empty vpool, and add vertices to it on the fly.
1789 
1790  MObject component_obj;
1791  MItMeshPolygon pi(dag_path, component_obj, &status);
1792  if (!status) {
1793  status.perror("MItMeshPolygon constructor");
1794  return;
1795  }
1796 
1797  MObjectArray shaders;
1798  MIntArray poly_shader_indices;
1799 
1800  status = mesh.getConnectedShaders(dag_path.instanceNumber(),
1801  shaders, poly_shader_indices);
1802  if (!status) {
1803  status.perror("MFnMesh::getConnectedShaders");
1804  }
1805 
1806  // We will need to transform all vertices from world coordinate space into
1807  // the vertex space appropriate to this node. Usually, this is the same
1808  // thing as world coordinate space, and this matrix will be identity; but if
1809  // the node is under an instance (particularly, for instance, a billboard)
1810  // then the vertex space will be different from world space.
1811  LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
1812 
1813  // Save these modeling flags for the check below.
1814  bool egg_vertex_color = false;
1815  bool egg_double_sided = false;
1816  if (egg_group->has_user_data(MayaEggGroupUserData::get_class_type())) {
1817  MayaEggGroupUserData *user_data =
1818  DCAST(MayaEggGroupUserData, egg_group->get_user_data());
1819  egg_vertex_color = user_data->_vertex_color;
1820  egg_double_sided = user_data->_double_sided;
1821  }
1822 
1823  bool double_sided = maya_double_sided;
1824  if (!_respect_maya_double_sided) {
1825  // If this flag is false, we respect the maya double-sided settings only
1826  // if the egg "double-sided" flag is also set.
1827  if (!egg_double_sided) {
1828  double_sided = false;
1829  }
1830  }
1831 
1832  bool keep_all_uvsets = _keep_all_uvsets || node_desc->has_object_type("keep-all-uvsets");
1833  if (node_desc->has_object_type("keep-all-uvsets")) {
1834  mayaegg_cat.info() << "will keep_all_uvsets" << endl;
1835  }
1836 
1837  _shaders.bind_uvsets(mesh.object());
1838 
1839  while (!pi.isDone()) {
1840  EggPolygon *egg_poly = new EggPolygon;
1841  egg_group->add_child(egg_poly);
1842 
1843  egg_poly->set_bface_flag(double_sided);
1844 
1845  // Determine the MayaShader for this particular polygon. There appears to
1846  // be two diverging paths for any Maya node with a Material (MayaShader)
1847  // on it This next bit kicks us out into mayaShader et al. to pull
1848  // textures and everything else.
1849  MayaShader *shader = nullptr;
1850  int index = pi.index();
1851  nassertv(index >= 0 && index < (int)poly_shader_indices.length());
1852  int shader_index = poly_shader_indices[index];
1853 
1854  if (shader_index != -1) {
1855  nassertv(shader_index >= 0 && shader_index < (int)shaders.length());
1856  MObject engine = shaders[shader_index];
1857  shader =
1858  _shaders.find_shader_for_shading_engine(engine, _legacy_shader); //head out to the other classes
1859  // does this mean if we didn't find a Maya shader give it a default
1860  // value anyway?
1861  } else if (default_shader != nullptr) {
1862  shader = default_shader;
1863  }
1864 
1865  const MayaShaderColorDef *default_color_def = nullptr;
1866 
1867  // And apply the shader properties to the polygon.
1868  if (shader != nullptr) {
1869  set_shader_attributes(*egg_poly, *shader, true);
1870  default_color_def = shader->get_color_def();
1871  }
1872 
1873  // Should we extract the color from the vertices? Normally, in Maya a
1874  // texture completely replaces the vertex color, so we should ignore the
1875  // vertex color if we have a texture.
1876 
1877  // However, this is an inconvenient property of Maya; sometimes we really
1878  // do want both vertex color and texture applied to the same object. To
1879  // allow this, we define the special egg flag "vertex-color", which when
1880  // set indicates that we should respect the vertex color anyway.
1881 
1882  // Furthermore, if _always_show_vertex_color is true, we pretend that the
1883  // "vertex-color" flag is always set.
1884  bool ignore_vertex_color = false;
1885  if ( default_color_def != nullptr) {
1886  ignore_vertex_color = default_color_def->_has_texture && !(egg_vertex_color || _always_show_vertex_color);
1887  }
1888 
1889  LColor poly_color(1.0f, 1.0f, 1.0f, 1.0f);
1890  if (!ignore_vertex_color) {
1891  // If we're respecting the vertex color, then remove the color
1892  // specification from the polygon (so we can apply it to the vertices).
1893  poly_color = egg_poly->get_color();
1894  egg_poly->clear_color();
1895  }
1896 
1897  // Get the vertices for the polygon.
1898  long num_verts = pi.polygonVertexCount();
1899  long i;
1900  LPoint3d centroid(0.0, 0.0, 0.0);
1901 
1902  if (default_color_def != nullptr && default_color_def->has_projection()) {
1903  // If the shader has a projection, we may need to compute the polygon's
1904  // centroid to avoid seams at the edges.
1905  for (i = 0; i < num_verts; i++) {
1906  MPoint p = pi.point(i, MSpace::kWorld);
1907  LPoint3d p3d(p[0], p[1], p[2]);
1908  p3d = p3d * vertex_frame_inv;
1909  centroid += p3d;
1910  }
1911  centroid /= (double)num_verts;
1912  }
1913  for (i = 0; i < num_verts; i++) {
1914  EggVertex vert;
1915 
1916  MPoint p = pi.point(i, MSpace::kWorld);
1917  LPoint3d p3d(p[0] / p[3], p[1] / p[3], p[2] / p[3]);
1918  p3d = p3d * vertex_frame_inv;
1919  vert.set_pos(p3d);
1920 
1921  MVector n;
1922  status = pi.getNormal(i, n, MSpace::kWorld);
1923  if (!status) {
1924  status.perror("MItMeshPolygon::getNormal");
1925  } else {
1926  LNormald n3d(n[0], n[1], n[2]);
1927  n3d = n3d * vertex_frame_inv;
1928  vert.set_normal(n3d);
1929  }
1930 
1931  // Go thru all the texture references for this primitive and set uvs
1932  if (mayaegg_cat.is_spam()) {
1933  if (shader != nullptr) {
1934  mayaegg_cat.spam() << "shader->_color.size is " << shader->_color.size() << endl;
1935  }
1936  mayaegg_cat.spam() << "primitive->tref.size is " << egg_poly->get_num_textures() << endl;
1937  }
1938  for (size_t ti=0; ti< _shaders._uvset_names.size(); ++ti) {
1939  // get the eggTexture pointer
1940  string uvset_name(_shaders._uvset_names[ti]);
1941  string panda_uvset_name = uvset_name;
1942  if (panda_uvset_name == "map1") {
1943  panda_uvset_name = "default";
1944  }
1945  if (mayaegg_cat.is_spam()) {
1946  mayaegg_cat.spam() << "--uvset_name :" << uvset_name << endl;
1947  }
1948 
1949  // get the shader color def that matches this EggTexture Asad:
1950  // optimizing uvset: to discard unused uvsets. This for loop figures
1951  // out which ones are unused.
1952 
1953  bool keep_uv = keep_all_uvsets;
1954  bool project_uv = false;
1955  LTexCoordd uv_projection;
1956 
1957  if (shader != nullptr) {
1958  for (size_t tj = 0; tj < shader->_all_maps.size(); ++tj) {
1959  MayaShaderColorDef *def = shader->_all_maps[tj];
1960  if (def->_uvset_name == uvset_name) {
1961  if (mayaegg_cat.is_spam()) {
1962  mayaegg_cat.spam() << "matched colordef idx: " << tj << endl;
1963  }
1964  keep_uv = true;
1965  if (def->has_projection()) {
1966  project_uv = true;
1967  uv_projection = def->project_uv(p3d, centroid);
1968  }
1969  break;
1970  }
1971  }
1972  }
1973 
1974  // if uvset is not used don't add it to the vertex
1975  if (!keep_uv) {
1976  if (mayaegg_cat.is_spam()) {
1977  mayaegg_cat.spam() << "discarding unused uvset " << uvset_name << endl;
1978  }
1979  continue;
1980  }
1981 
1982  if (project_uv) {
1983  // If the shader has a projection, use it instead of the polygon's
1984  // built-in UV's.
1985  vert.set_uv(panda_uvset_name, uv_projection);
1986  } else {
1987  // Get the UV's from the polygon.
1988  float2 uvs;
1989  MString uv_mstring(uvset_name.c_str());
1990  if (pi.hasUVs(uv_mstring, &status)) {
1991  status = pi.getUV(i, uvs, &uv_mstring);
1992  if (!status) {
1993  status.perror("MItMeshPolygon::getUV");
1994  } else {
1995  if (_round_uvs) {
1996  if (uvs[0] > 1.0 || uvs[0] < -1.0) {
1997  // apply upto 11000th precision, but round up
1998  uvs[0] = (long)(uvs[0]*1000);
1999  mayaegg_cat.debug() << "before rounding uvs[0]: " << uvs[0] << endl;
2000  uvs[0] = (double)(round((double)uvs[0]/10.0)*10.0)/1000.0;
2001  mayaegg_cat.debug() << "after rounding uvs[0]: " << uvs[0] << endl;
2002  }
2003  if (uvs[1] > 1.0 || uvs[1] < -1.0) {
2004  uvs[1] = (long)(uvs[1]*1000);
2005  mayaegg_cat.debug() << "before rounding uvs[1]: " << uvs[1] << endl;
2006  uvs[1] = (double)(round((double)uvs[1]/10.0)*10.0)/1000.0;
2007  mayaegg_cat.debug() << "after rounding uvs[1]: " << uvs[1] << endl;
2008  }
2009  }
2010  vert.set_uv(panda_uvset_name, LTexCoordd(uvs[0], uvs[1]));
2011  }
2012  }
2013  }
2014  }
2015 
2016  if (!ignore_vertex_color) {
2017  if (mayaegg_cat.is_spam()) {
2018  mayaegg_cat.spam() << "poly_color = " << poly_color << endl;
2019  }
2020  set_vertex_color(vert,pi,i,shader,poly_color);
2021  }
2022 
2023  vert.set_external_index(pi.vertexIndex(i, &status));
2024  vert.set_external_index2(pi.normalIndex(i, &status));
2025 
2026  egg_poly->add_vertex(vpool->create_unique_vertex(vert));
2027  }
2028 
2029  // Also get the face normal for the polygon.
2030  LNormald face_normal;
2031  bool got_face_normal = false;
2032 
2033  MVector n;
2034  status = pi.getNormal(n, MSpace::kWorld);
2035  if (!status) {
2036  status.perror("MItMeshPolygon::getNormal face");
2037  } else {
2038  face_normal.set(n[0], n[1], n[2]);
2039  face_normal = face_normal * vertex_frame_inv;
2040  got_face_normal = true;
2041  egg_poly->set_normal(face_normal);
2042  }
2043 
2044  // Now, check that the vertex ordering is consistent with the direction of
2045  // the normals. If not, reverse the vertex ordering (since we have seen
2046  // cases where Maya sets this in contradiction to its normals).
2047  LNormald order_normal;
2048  if (got_face_normal && egg_poly->calculate_normal(order_normal)) {
2049  if (order_normal.dot(face_normal) < 0.0) {
2050  egg_poly->reverse_vertex_ordering();
2051  if (mayaegg_cat.is_debug()) {
2052  mayaegg_cat.debug()
2053  << "reversing polygon\n";
2054  }
2055  }
2056  }
2057 
2058  pi.next();
2059  }
2060  if (mayaegg_cat.is_spam()) {
2061  mayaegg_cat.spam() << "done traversing polys" << endl;
2062  }
2063 
2064  // Now that we've added all the polygons (and created all the vertices), go
2065  // back through the vertex pool and set up the appropriate joint membership
2066  // for each of the vertices.
2067  bool got_weights = false;
2068 
2069  pvector<EggGroup *> joints;
2070  MFloatArray weights;
2071  if (_animation_convert == AC_model) {
2072  got_weights =
2073  get_vertex_weights(dag_path, mesh, joints, weights);
2074  }
2075 
2076  if (got_weights && !joints.empty()) {
2077  int num_joints = joints.size();
2078  int num_weights = (int)weights.length();
2079  int num_verts = num_weights / num_joints;
2080  // The number of weights should be an even multiple of verts * joints.
2081  nassertv(num_weights == num_verts * num_joints);
2082 
2084  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
2085  EggVertex *vert = (*vi);
2086  int maya_vi = vert->get_external_index();
2087  nassertv(maya_vi >= 0 && maya_vi < num_verts);
2088 
2089  for (int ji = 0; ji < num_joints; ++ji) {
2090  PN_stdfloat weight = weights[maya_vi * num_joints + ji];
2091  if (weight != 0.0f) {
2092  EggGroup *joint = joints[ji];
2093  if (joint != nullptr) {
2094  joint->ref_vertex(vert, weight);
2095  }
2096  }
2097  }
2098  }
2099  }
2100 
2101 
2102  // We also need to compute the vertex morphs for the polyset, based on
2103  // whatever blend shapes may be present. This is similar to the code in
2104  // make_nurbs_surface(), except that since we don't have a one-to-one
2105  // relationship of egg vertices to Maya vertices, we have to get the morphs
2106  // down here, after we have added all of the egg vertices.
2107 
2108  if (_animation_convert == AC_model) {
2109  int num_orig_mesh_verts = mesh.numVertices();
2110 
2111  int num_sliders = node_desc->get_num_blend_descs();
2112  for (int i = 0; i < num_sliders; i++) {
2113  MayaBlendDesc *blend_desc = node_desc->get_blend_desc(i);
2114 
2115  // Temporarily push the slider up to 1.0 so we can see what the surface
2116  // looks like at that value.
2117  blend_desc->set_slider(1.0);
2118 
2119  // We have to get the mesh object from the dag again after fiddling with
2120  // the slider.
2121  MFnMesh blend_mesh(dag_path, &status);
2122  if (!status) {
2123  mayaegg_cat.warning()
2124  << name << " no longer has a mesh after applying "
2125  << blend_desc->get_name() << "\n";
2126 
2127  } else {
2128  if (blend_mesh.numVertices() != num_orig_mesh_verts) {
2129  mayaegg_cat.warning()
2130  << "Ignoring " << blend_desc->get_name() << " for "
2131  << name << "; blend shape has " << blend_mesh.numVertices()
2132  << " vertices while original shape has "
2133  << num_orig_mesh_verts << ".\n";
2134 
2135  } else {
2136  MPointArray verts;
2137  status = blend_mesh.getPoints(verts, MSpace::kWorld);
2138  if (!status) {
2139  status.perror("MFnMesh::getPoints");
2140  } else {
2141  int num_verts = (int)verts.length();
2143  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
2144  EggVertex *vert = (*vi);
2145  int maya_vi = vert->get_external_index();
2146  nassertv(maya_vi >= 0 && maya_vi < num_verts);
2147 
2148  const MPoint &m = verts[maya_vi];
2149  LPoint3d m3d(m[0] / m[3], m[1] / m[3], m[2] / m[3]);
2150  m3d = m3d * vertex_frame_inv;
2151 
2152  LVector3d delta = m3d - vert->get_pos3();
2153  if (!delta.almost_equal(LVector3d::zero())) {
2154  EggMorphVertex dxyz(blend_desc->get_name(), delta);
2155  vert->_dxyzs.insert(dxyz);
2156  }
2157  }
2158  }
2159 
2160  MFloatVectorArray norms;
2161  status = blend_mesh.getNormals(norms, MSpace::kWorld);
2162  if (!status) {
2163  status.perror("MFnMesh::getNormals");
2164  } else {
2165  int num_norms = (int)norms.length();
2167  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
2168  EggVertex *vert = (*vi);
2169  int maya_vi = vert->get_external_index2();
2170  nassertv(maya_vi >= 0 && maya_vi < num_norms);
2171 
2172  const MFloatVector &m = norms[maya_vi];
2173  LVector3d m3d(m[0], m[1], m[2]);
2174  m3d = m3d * vertex_frame_inv;
2175 
2176  LNormald delta = m3d - vert->get_normal();
2177  if (!delta.almost_equal(LVector3d::zero())) {
2178  EggMorphNormal dnormal(blend_desc->get_name(), delta);
2179  vert->_dnormals.insert(dnormal);
2180  }
2181  }
2182  }
2183  }
2184  }
2185 
2186  blend_desc->set_slider(0.0);
2187  }
2188  }
2189 }
2190 
2191 /**
2192  * Locators are used in Maya to indicate a particular position in space to the
2193  * user or the modeler. We represent that in egg with an ordinary Group node,
2194  * which we transform by the locator's position, so that the indicated point
2195  * becomes the origin at this node and below.
2196  */
2197 void MayaToEggConverter::
2198 make_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
2199  EggGroup *egg_group) {
2200  MStatus status;
2201 
2202  unsigned int num_children = dag_node.childCount();
2203  MObject locator;
2204  bool found_locator = false;
2205  for (unsigned int ci = 0; ci < num_children && !found_locator; ci++) {
2206  locator = dag_node.child(ci);
2207  found_locator = (locator.apiType() == MFn::kLocator);
2208  }
2209 
2210  if (!found_locator) {
2211  mayaegg_cat.error()
2212  << "Couldn't find locator within locator node "
2213  << dag_path.fullPathName().asChar() << "\n";
2214  return;
2215  }
2216 
2217  LPoint3d p3d;
2218  if (!get_vec3d_attribute(locator, "localPosition", p3d)) {
2219  mayaegg_cat.error()
2220  << "Couldn't get position of locator "
2221  << dag_path.fullPathName().asChar() << "\n";
2222  return;
2223  }
2224 
2225  // We need to convert the position to world coordinates. For some reason,
2226  // Maya can only tell it to us in local coordinates.
2227  MMatrix mat = dag_path.inclusiveMatrix(&status);
2228  if (!status) {
2229  status.perror("Can't get coordinate space for locator");
2230  return;
2231  }
2232  LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
2233  mat[1][0], mat[1][1], mat[1][2], mat[1][3],
2234  mat[2][0], mat[2][1], mat[2][2], mat[2][3],
2235  mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
2236  p3d = p3d * n2w;
2237 
2238  // Now convert the locator point into the group's space.
2239  p3d = p3d * egg_group->get_node_frame_inv();
2240 
2241  egg_group->add_translate3d(p3d);
2242 }
2243 
2244 /**
2245  * Locators are used in Maya to indicate a particular position in space to the
2246  * user or the modeler. We represent that in egg with an ordinary Group node,
2247  * which we transform by the locator's position, so that the indicated point
2248  * becomes the origin at this node and below.
2249  */
2250 void MayaToEggConverter::
2251 make_camera_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
2252  EggGroup *egg_group) {
2253  MStatus status;
2254 
2255  unsigned int num_children = dag_node.childCount();
2256  MObject locator;
2257  bool found_camera = false;
2258  for (unsigned int ci = 0; ci < num_children && !found_camera; ci++) {
2259  locator = dag_node.child(ci);
2260  found_camera = (locator.apiType() == MFn::kCamera);
2261  }
2262 
2263  if (!found_camera) {
2264  mayaegg_cat.error()
2265  << "Couldn't find camera"
2266  << dag_path.fullPathName().asChar() << "\n";
2267  return;
2268  }
2269  MFnCamera camera (dag_path, &status);
2270  if ( !status ) {
2271  status.perror("MFnCamera constructor");
2272  return;
2273  }
2274  MPoint eyePoint = camera.eyePoint(MSpace::kWorld);
2275  LPoint3d p3d (eyePoint.x, eyePoint.y, eyePoint.z);
2276 
2277  // Now convert the locator point into the group's space.
2278  p3d = p3d * egg_group->get_node_frame_inv();
2279 
2280  egg_group->add_translate3d(p3d);
2281 }
2282 
2283 
2284 /**
2285  * Locators are used in Maya to indicate a particular position in space to the
2286  * user or the modeler. We represent that in egg with an ordinary Group node,
2287  * which we transform by the locator's position, so that the indicated point
2288  * becomes the origin at this node and below.
2289  */
2290 void MayaToEggConverter::
2291 make_light_locator(const MDagPath &dag_path, const MFnDagNode &dag_node,
2292  EggGroup *egg_group) {
2293  MStatus status;
2294 
2295  unsigned int num_children = dag_node.childCount();
2296  MObject locator;
2297  bool found_alight = false;
2298  bool found_dlight = false;
2299  bool found_plight = false;
2300  for (unsigned int ci = 0; ci < num_children && !found_alight && !found_dlight && !found_plight; ci++) {
2301  locator = dag_node.child(ci);
2302  found_alight = (locator.apiType() == MFn::kAmbientLight);
2303  found_dlight = (locator.apiType() == MFn::kDirectionalLight);
2304  found_plight = (locator.apiType() == MFn::kPointLight);
2305  }
2306 
2307  if (!found_alight && !found_dlight && !found_plight) {
2308  mayaegg_cat.error()
2309  << "Couldn't find light within locator node "
2310  << dag_path.fullPathName().asChar() << "\n";
2311  return;
2312  }
2313 
2314  LPoint3d p3d;
2315 
2316  // We need to convert the position to world coordinates. For some reason,
2317  // Maya can only tell it to us in local coordinates.
2318  MMatrix mat = dag_path.inclusiveMatrix(&status);
2319  if (!status) {
2320  status.perror("Can't get coordinate space for light");
2321  return;
2322  }
2323  LMatrix4d n2w(mat[0][0], mat[0][1], mat[0][2], mat[0][3],
2324  mat[1][0], mat[1][1], mat[1][2], mat[1][3],
2325  mat[2][0], mat[2][1], mat[2][2], mat[2][3],
2326  mat[3][0], mat[3][1], mat[3][2], mat[3][3]);
2327  p3d = p3d * n2w;
2328 
2329  // Now convert the locator point into the group's space.
2330  p3d = p3d * egg_group->get_node_frame_inv();
2331 
2332  egg_group->add_translate3d(p3d);
2333 }
2334 
2335 
2336 /**
2337  *
2338  */
2339 bool MayaToEggConverter::
2340 get_vertex_weights(const MDagPath &dag_path, const MFnMesh &mesh,
2341  pvector<EggGroup *> &joints, MFloatArray &weights) {
2342  MStatus status;
2343 
2344  // Since we are working with a mesh the input attribute that creates the
2345  // mesh is named "inMesh"
2346  MObject attr = mesh.attribute("inMesh");
2347 
2348  // Create the plug to the "inMesh" attribute then use the DG iterator to
2349  // walk through the DG, at the node level.
2350  MPlug history(mesh.object(), attr);
2351  MItDependencyGraph it(history, MFn::kDependencyNode,
2352  MItDependencyGraph::kUpstream,
2353  MItDependencyGraph::kDepthFirst,
2354  MItDependencyGraph::kNodeLevel);
2355 
2356  while (!it.isDone()) {
2357  // We will walk along the node level of the DG until we spot a skinCluster
2358  // node.
2359  MObject c_node = it.thisNode();
2360  if (c_node.hasFn(MFn::kSkinClusterFilter)) {
2361  // We've found the cluster handle. Try to get the weight data.
2362  MFnSkinCluster cluster(c_node, &status);
2363  if (!status) {
2364  status.perror("MFnSkinCluster constructor");
2365  return false;
2366  }
2367 
2368  // Get the set of objects that influence the vertices of this mesh.
2369  // Hopefully these will all be joints.
2370  MDagPathArray influence_objects;
2371  cluster.influenceObjects(influence_objects, &status);
2372  if (!status) {
2373  status.perror("MFnSkinCluster::influenceObjects");
2374 
2375  } else {
2376  // Fill up the vector with the corresponding table of egg groups for
2377  // each joint.
2378  joints.clear();
2379  for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
2380  MDagPath joint_dag_path = influence_objects[oi];
2381  MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
2382  EggGroup *joint = _tree.get_egg_group(joint_node_desc);
2383  joints.push_back(joint);
2384  }
2385 
2386  // Now use a component object to retrieve all of the weight data in
2387  // one API call.
2388  MFnSingleIndexedComponent sic;
2389  MObject sic_object = sic.create(MFn::kMeshVertComponent);
2390  sic.setCompleteData(mesh.numVertices());
2391  unsigned influence_count;
2392 
2393  status = cluster.getWeights(dag_path, sic_object,
2394  weights, influence_count);
2395  if (!status) {
2396  status.perror("MFnSkinCluster::getWeights");
2397  } else {
2398  if (influence_count != influence_objects.length()) {
2399  mayaegg_cat.error()
2400  << "MFnSkinCluster::influenceObjects() returns "
2401  << influence_objects.length()
2402  << " objects, but MFnSkinCluster::getWeights() reports "
2403  << influence_count << " objects.\n";
2404 
2405  } else {
2406  // We've got the weights and the set of objects. That's all we
2407  // need.
2408  return true;
2409  }
2410  }
2411  }
2412  } else if (c_node.hasFn(MFn::kWeightGeometryFilt)) {
2413  // We've found the joint cluster handle. (rigid Binding)
2414  MFnWeightGeometryFilter cluster(c_node, &status);
2415  if (!status) {
2416  status.perror("MFnWeightGeometryFilter constructor");
2417  return false;
2418  }
2419 
2420  MPlug matrix_plug = cluster.findPlug("matrix");
2421  if (!matrix_plug.isNull()) {
2422  MPlugArray matrix_pa;
2423  matrix_plug.connectedTo(matrix_pa, true, false, &status);
2424  if (!status) {
2425  status.perror("Can't find connected Joint");
2426  } else {
2427  MObject jointObj = matrix_pa[0].node();
2428  MFnIkJoint jointFn(jointObj, &status);
2429  if (!status) {
2430  status.perror("Can't find connected JointDag");
2431  } else {
2432  joints.clear();
2433  MDagPath joint_dag_path = MDagPath();
2434  status = jointFn.getPath(joint_dag_path);
2435  if (!status) {
2436  status.perror("MFnIkJoint::dagPath");
2437  } else {
2438  MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
2439  EggGroup *joint = _tree.get_egg_group(joint_node_desc);
2440  joints.push_back(joint);
2441 
2442  // Now use a component object to retrieve all of the weight data
2443  // in one API call.
2444  MFnSingleIndexedComponent sic;
2445  MObject sic_object = sic.create(MFn::kMeshVertComponent);
2446  sic.setCompleteData(mesh.numVertices());
2447 
2448  status = cluster.getWeights(dag_path, sic_object,
2449  weights);
2450  if (!status) {
2451  status.perror("MFnWeightGeometryFilter::getWeights");
2452  } else {
2453  // We've got the weights and the set of objects. That's all
2454  // we need.
2455  return true;
2456  }
2457  }
2458  }
2459  }
2460  }
2461  }
2462 
2463  it.next();
2464  }
2465 
2466  // The mesh was not soft-skinned.
2467  return false;
2468 }
2469 
2470 /**
2471  * As above, for a NURBS surface instead of a polygon mesh.
2472  */
2473 bool MayaToEggConverter::
2474 get_vertex_weights(const MDagPath &dag_path, const MFnNurbsSurface &surface,
2475  pvector<EggGroup *> &joints, MFloatArray &weights) {
2476  MStatus status;
2477 
2478  // Since we are working with a NURBS surface the input attribute that
2479  // creates the surface is named "create"
2480  MObject attr = surface.attribute("create");
2481 
2482  // Create the plug to the "create" attribute then use the DG iterator to
2483  // walk through the DG, at the node level.
2484  MPlug history(surface.object(), attr);
2485  MItDependencyGraph it(history, MFn::kDependencyNode,
2486  MItDependencyGraph::kUpstream,
2487  MItDependencyGraph::kDepthFirst,
2488  MItDependencyGraph::kNodeLevel);
2489 
2490  while (!it.isDone()) {
2491  // We will walk along the node level of the DG until we spot a skinCluster
2492  // node.
2493  MObject c_node = it.thisNode();
2494  if (c_node.hasFn(MFn::kSkinClusterFilter)) {
2495  // We've found the cluster handle. Try to get the weight data.
2496  MFnSkinCluster cluster(c_node, &status);
2497  if (!status) {
2498  status.perror("MFnSkinCluster constructor");
2499  return false;
2500  }
2501 
2502  // Get the set of objects that influence the vertices of this surface.
2503  // Hopefully these will all be joints.
2504  MDagPathArray influence_objects;
2505  cluster.influenceObjects(influence_objects, &status);
2506  if (!status) {
2507  status.perror("MFnSkinCluster::influenceObjects");
2508 
2509  } else {
2510  // Fill up the vector with the corresponding table of egg groups for
2511  // each joint.
2512  joints.clear();
2513  for (unsigned oi = 0; oi < influence_objects.length(); oi++) {
2514  MDagPath joint_dag_path = influence_objects[oi];
2515  MayaNodeDesc *joint_node_desc = _tree.build_node(joint_dag_path);
2516  EggGroup *joint = _tree.get_egg_group(joint_node_desc);
2517  joints.push_back(joint);
2518  }
2519 
2520  // Now use a component object to retrieve all of the weight data in
2521  // one API call.
2522  MFnDoubleIndexedComponent dic;
2523  MObject dic_object = dic.create(MFn::kSurfaceCVComponent);
2524  dic.setCompleteData(surface.numCVsInU(), surface.numCVsInV());
2525  unsigned influence_count;
2526 
2527  status = cluster.getWeights(dag_path, dic_object,
2528  weights, influence_count);
2529  if (!status) {
2530  status.perror("MFnSkinCluster::getWeights");
2531  } else {
2532  if (influence_count != influence_objects.length()) {
2533  mayaegg_cat.error()
2534  << "MFnSkinCluster::influenceObjects() returns "
2535  << influence_objects.length()
2536  << " objects, but MFnSkinCluster::getWeights() reports "
2537  << influence_count << " objects.\n";
2538 
2539  } else {
2540  // We've got the weights and the set of objects. That's all we
2541  // need.
2542  return true;
2543  }
2544  }
2545  }
2546  }
2547 
2548  it.next();
2549  }
2550 
2551  // The surface was not soft-skinned.
2552  return false;
2553 }
2554 
2555 /**
2556  * Applies the known shader attributes to the indicated egg primitive. Note:
2557  * For multi-textures, Maya lists the top most texture in slot 0. But Panda
2558  * puts the base texture at slot 0. Hence I parse the list of textures from
2559  * last to first.
2560  */
2561 void MayaToEggConverter::
2562 set_shader_attributes(EggPrimitive &primitive, const MayaShader &shader,
2563  bool mesh) {
2564  if (shader._legacy_mode) {
2565  set_shader_legacy(primitive, shader, mesh);
2566  } else {
2567  set_shader_modern(primitive, shader, mesh);
2568  }
2569 }
2570 
2571 /**
2572  * The modern implementation of set_shader_attributes.
2573  *
2574  * In the modern codepath, the MayaShader is a direct, literal representation
2575  * of a list of EggTextures. All this exporter has to do is translate the
2576  * list without interpretation. All the complex interpretation is handled
2577  * elsewhere, in the MayaShader module.
2578  */
2579 void MayaToEggConverter::
2580 set_shader_modern(EggPrimitive &primitive, const MayaShader &shader,
2581  bool mesh) {
2582 
2583  for (size_t idx=0; idx < shader._all_maps.size(); idx++) {
2584  MayaShaderColorDef *def = shader._all_maps[idx];
2585  if ((def->_is_alpha)&&(def->_opposite != 0)) {
2586  // This texture represents an alpha-filename. It doesn't get its own
2587  // <Texture>
2588  continue;
2589  }
2590 
2591  EggTexture tex(shader.get_name(), "");
2592  tex.set_format(def->_is_alpha ? EggTexture::F_alpha : EggTexture::F_rgb);
2593  apply_texture_filename(tex, *def);
2594  if (def->_opposite) {
2595  apply_texture_alpha_filename(tex, *def);
2596  }
2597  apply_texture_uvprops(tex, *def);
2598  apply_texture_blendtype(tex, *def);
2599  tex.set_uv_name(def->get_panda_uvset_name());
2600 
2601  EggTexture *new_tex =
2602  _textures.create_unique_texture(tex, ~0);
2603  primitive.add_texture(new_tex);
2604  }
2605 }
2606 
2607 /**
2608  * The legacy implementation of set_shader_attributes. The old behavior of
2609  * the exporter is just plain weird. It seems to be a result of an
2610  * inexperienced coder who made some core mistakes, and then patched them up
2611  * with kludges. It seems to produce plausible results in certain specific
2612  * cases, but overall, it doesn't make any sense. Unfortunately, this weird
2613  * behavior cannot be discarded - vast numbers of 3D models have been created
2614  * that rely on this behavior. The solution is to compartmentalize the
2615  * weirdness. The legacy codepath, when activated, implements the old weird
2616  * behavior. A brand-new codepath that shares almost nothing with the legacy
2617  * codepath implements a much more straightforward behavior.
2618  */
2619 void MayaToEggConverter::
2620 set_shader_legacy(EggPrimitive &primitive, const MayaShader &shader,
2621  bool mesh) {
2622 
2623  // determine if the base texture or any of the top texture need to be rgb
2624  // only
2625  MayaShaderColorDef *color_def = nullptr;
2626  bool is_rgb = false;
2627  bool is_decal = false;
2628  bool is_interpolate = false;
2629  int i;
2630  // last shader is the base so lets skip it
2631  for (i=0; i<(int)shader._color.size()-1; ++i) {
2632  color_def = shader.get_color_def(i);
2633  if (color_def->_has_texture) {
2634  if ((EggTexture::EnvType)color_def->_interpolate) {
2635  is_interpolate = true;
2636  }
2637  else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_modulate) {
2638  // Maya's multiply is slightly different than panda's. Unless,
2639  // _keep_alpha is set, we are dropping the alpha.
2640  if (!color_def->_keep_alpha)
2641  is_rgb = true; // modulate forces the alpha to be ignored
2642  }
2643  else if ((EggTexture::EnvType)color_def->_blend_type == EggTexture::ET_decal) {
2644  is_decal = true;
2645  }
2646  }
2647  }
2648 
2649  // we don't want an extra light stage for interpolate mode, it takes care of
2650  // automatically
2651  if (is_interpolate)
2652  is_decal = false;
2653 
2654  // new decal mode needs an extra dummy layers of textureStage
2655  EggTexture *dummy_tex = nullptr;
2656  string dummy_uvset_name;
2657 
2658  // In Maya, a polygon is either textured or colored. The texture, if
2659  // present, replaces the color. Also now there could be multiple textures
2660  const MayaShaderColorDef &trans_def = shader._transparency;
2661  for (i=shader._color.size()-1; i>=0; --i) {
2662  color_def = shader.get_color_def(i);
2663  if (mayaegg_cat.is_spam()) {
2664  mayaegg_cat.spam() << "slot " << i << ":got color_def: " << color_def << endl;
2665  }
2666  if (color_def->_has_texture || trans_def._has_texture) {
2667  EggTexture tex(shader.get_name(), "");
2668  if (mayaegg_cat.is_spam()) {
2669  mayaegg_cat.spam() << "got shader name:" << shader.get_name() << endl;
2670  mayaegg_cat.spam() << "ssa:texture name[" << i << "]: " << color_def->_texture_name << endl;
2671  }
2672 
2673  string uvset_name = _shaders.find_uv_link(color_def->_texture_name);
2674  if (mayaegg_cat.is_spam()) {
2675  mayaegg_cat.spam() << "ssa:corresponding uvset name is " << uvset_name << endl;
2676  }
2677 
2678  if (color_def->_has_texture) {
2679  // If we have a texture on color, apply it as the filename. if
2680  // (mayaegg_cat.is_debug()) { mayaegg_cat.debug() << "ssa:got texture
2681  // name" << color_def->_texture_filename << endl; }
2682  Filename filename = Filename::from_os_specific(color_def->_texture_filename);
2683  Filename fullpath, outpath;
2684  _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath);
2685  tex.set_filename(outpath);
2686  tex.set_fullpath(fullpath);
2687  apply_texture_uvprops(tex, *color_def);
2688 
2689  // If we also have a texture on transparency, apply it as the alpha
2690  // filename.
2691  if (trans_def._has_texture) {
2692  if (color_def->_wrap_u != trans_def._wrap_u ||
2693  color_def->_wrap_u != trans_def._wrap_u) {
2694  mayaegg_cat.warning()
2695  << "Shader " << shader.get_name()
2696  << " has contradictory wrap modes on color and texture.\n";
2697  }
2698 
2699  if (!compare_texture_uvprops(tex, trans_def)) {
2700  // Only report each broken shader once.
2701  static pset<string> bad_shaders;
2702  if (bad_shaders.insert(shader.get_name()).second) {
2703  mayaegg_cat.error()
2704  << "Color and transparency texture properties differ on shader "
2705  << shader.get_name() << "\n";
2706  }
2707  }
2708  // tex.set_format(EggTexture::F_rgba);
2709 
2710  // We should try to be smarter about whether the transparency value
2711  // is connected to the texture's alpha channel or to its grayscale
2712  // channel. However, I'm not sure how to detect this at the moment;
2713  // rather than spending days trying to figure out, for now I'll just
2714  // assume that if the same texture image is used for both color and
2715  // transparency, then the artist meant to use the alpha channel for
2716  // transparency.
2717  if (trans_def._texture_filename == color_def->_texture_filename) {
2718  // That means that we don't need to do anything special: use all
2719  // the channels of the texture.
2720 
2721  } else {
2722  // Otherwise, pull the alpha channel from the other image file.
2723  // Ideally, we should figure out which channel from the other
2724  // image supplies alpha (and specify this via
2725  // set_alpha_file_channel()), but for now we assume it comes from
2726  // the grayscale data.
2727  filename = Filename::from_os_specific(trans_def._texture_filename);
2728  _path_replace->full_convert_path(filename, get_model_path(),
2729  fullpath, outpath);
2730  tex.set_alpha_filename(outpath);
2731  tex.set_alpha_fullpath(fullpath);
2732  }
2733  } else {
2734  // If there is no transparency texture specified, we don't have any
2735  // transparency, so tell the egg format to ignore any alpha channel
2736  // that might be on the color texture.
2737  // tex.set_format(EggTexture::F_rgb);
2738  }
2739 
2740  if (shader._color.size() > 1) {
2741  // if multi-textured, first texture in maya is on top, so last
2742  // shader on the list is the base one, which should always pick up
2743  // the alpha from the texture file. But the top textures may have
2744  // to strip the alpha
2745  if ((size_t)i != shader._color.size() - 1) {
2746  if (!i && is_interpolate) {
2747  // this is the grass path mode where alpha on this texture
2748  // determines whether to show layer1 or layer2. Since by now
2749  // other layers are set lets change those to get this effect
2750  tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_interpolate);
2751  tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_previous);
2752  tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
2753  tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_last_saved_result);
2754  tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
2755  tex.set_combine_source(EggTexture::CC_rgb, 2, EggTexture::CS_texture);
2756  tex.set_combine_operand(EggTexture::CC_rgb, 2, EggTexture::CO_src_alpha);
2757 
2758  tex.set_combine_mode(EggTexture::CC_alpha, EggTexture::CM_interpolate);
2759  tex.set_combine_source(EggTexture::CC_alpha, 0, EggTexture::CS_previous);
2760  tex.set_combine_operand(EggTexture::CC_alpha, 0, EggTexture::CO_src_alpha);
2761  tex.set_combine_source(EggTexture::CC_alpha, 1, EggTexture::CS_last_saved_result);
2762  tex.set_combine_operand(EggTexture::CC_alpha, 1, EggTexture::CO_src_alpha);
2763  tex.set_combine_source(EggTexture::CC_alpha, 2, EggTexture::CS_texture);
2764  tex.set_combine_operand(EggTexture::CC_alpha, 2, EggTexture::CO_src_alpha);
2765  }
2766  else {
2767  if (is_interpolate) {
2768  tex.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate);
2769  tex.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color);
2770  tex.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
2771  tex.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_texture);
2772  tex.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
2773  }
2774  else {
2775  tex.set_env_type((EggTexture::EnvType)color_def->_blend_type);
2776  if (tex.get_env_type() == EggTexture::ET_modulate) {
2777  if (color_def->_has_alpha_channel) {
2778  // lets caution the artist that they should not be using a
2779  // alpha channel on this texture.
2780  if (mayaegg_cat.is_spam()) {
2781  maya_cat.spam()
2782  << color_def->_texture_name
2783  << " should not have alpha channel in multiply mode: ignoring\n";
2784  }
2785  }
2786  if (is_rgb) {
2787  // tex.set_alpha_mode(EggRenderMode::AM_off); force
2788  // alpha off
2789  tex.set_format(EggTexture::F_rgb); // Change the format to be rgb only
2790  }
2791  }
2792  }
2793  }
2794  }
2795  else {
2796  if (is_interpolate) {
2797  // base shader need to save result
2798  tex.set_saved_result(true);
2799  }
2800  else if (is_decal) {
2801  // decal in classic time, always overwrote the base color. That
2802  // causes problem when the polygon wants to be lit or wants to
2803  // retain vertexpolygon color In the new decal mode, we achieve
2804  // this with a third dummy layer copy this layer to a new dummy
2805  // layer
2806  EggTexture texDummy(shader.get_name()+".dummy", "");
2807  if (mayaegg_cat.is_debug()) {
2808  mayaegg_cat.debug() << "creating dummy shader: " << texDummy.get_name() << endl;
2809  }
2810  texDummy.set_filename(outpath);
2811  texDummy.set_fullpath(fullpath);
2812  apply_texture_uvprops(texDummy, *color_def);
2813  texDummy.set_combine_mode(EggTexture::CC_rgb, EggTexture::CM_modulate);
2814  texDummy.set_combine_source(EggTexture::CC_rgb, 0, EggTexture::CS_primary_color);
2815  texDummy.set_combine_operand(EggTexture::CC_rgb, 0, EggTexture::CO_src_color);
2816  texDummy.set_combine_source(EggTexture::CC_rgb, 1, EggTexture::CS_previous);
2817  texDummy.set_combine_operand(EggTexture::CC_rgb, 1, EggTexture::CO_src_color);
2818  dummy_tex = _textures.create_unique_texture(texDummy, ~0);
2819 
2820  // make this layer ET_replace
2821  tex.set_env_type(EggTexture::ET_replace);
2822  }
2823  }
2824  }
2825  } else { // trans_def._has_texture
2826  // We have a texture on transparency only. Apply it as the primary
2827  // filename, and set the format accordingly.
2828  Filename filename = Filename::from_os_specific(trans_def._texture_filename);
2829  Filename fullpath,outpath;
2830  _path_replace->full_convert_path(filename, get_model_path(),
2831  fullpath, outpath);
2832  tex.set_filename(outpath);
2833  tex.set_fullpath(fullpath);
2834  tex.set_format(EggTexture::F_alpha);
2835  apply_texture_uvprops(tex, trans_def);
2836  }
2837 
2838  if (mayaegg_cat.is_debug()) {
2839  mayaegg_cat.debug() << "ssa:tref_name:" << tex.get_name() << endl;
2840  }
2841  if (is_rgb && i == (int)shader._color.size()-1) {
2842  // make base layer rgb only
2843  tex.set_format(EggTexture::F_rgb); // Change the format to be rgb only
2844  }
2845  EggTexture *new_tex =
2846  _textures.create_unique_texture(tex, ~0);
2847 
2848  if (mesh) {
2849  if (uvset_name.find("not found") == string::npos) {
2850  primitive.add_texture(new_tex);
2851  color_def->_uvset_name.assign(uvset_name.c_str());
2852  if (uvset_name != "map1") {
2853  new_tex->set_uv_name(uvset_name);
2854  }
2855  if (i == (int)shader._color.size()-1 && is_decal) {
2856  dummy_uvset_name.assign(color_def->_uvset_name);
2857  }
2858  }
2859  } else {
2860  primitive.add_texture(new_tex);
2861  if (color_def->_uvset_name != "map1") {
2862  new_tex->set_uv_name(color_def->_uvset_name);
2863  }
2864  }
2865  }
2866  }
2867  if (dummy_tex != nullptr) {
2868  primitive.add_texture(dummy_tex);
2869  dummy_tex->set_uv_name(dummy_uvset_name);
2870  }
2871  // Also apply an overall color to the primitive.
2872  LColor rgba = shader.get_rgba();
2873  if (mayaegg_cat.is_spam()) {
2874  mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl;
2875  }
2876 
2877  // The existence of a texture on either color channel completely replaces
2878  // the corresponding flat color.
2879  if (color_def && color_def->_has_texture) {
2880  rgba[0] = 1.0f;
2881  rgba[1] = 1.0f;
2882  rgba[2] = 1.0f;
2883  }
2884  if (trans_def._has_texture) {
2885  rgba[3] = 1.0f;
2886  }
2887 
2888  // But the color gain always gets applied.
2889  if (color_def) {
2890  rgba[0] *= color_def->_color_gain[0];
2891  rgba[1] *= color_def->_color_gain[1];
2892  rgba[2] *= color_def->_color_gain[2];
2893  rgba[3] *= color_def->_color_gain[3];
2894  if (mayaegg_cat.is_spam()) {
2895  mayaegg_cat.spam() << "ssa:rgba = " << rgba << endl;
2896  }
2897  }
2898 
2899  primitive.set_color(rgba);
2900 
2901  if (mayaegg_cat.is_spam()) {
2902  mayaegg_cat.spam() << " set_shader_attributes : end\n";
2903  }
2904 }
2905 
2906 /**
2907  * Applies all the appropriate texture properties to the EggTexture object,
2908  * including wrap modes and texture matrix.
2909  */
2910 void MayaToEggConverter::
2911 apply_texture_uvprops(EggTexture &tex, const MayaShaderColorDef &color_def) {
2912  // Let's mipmap all textures by default.
2913  tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
2914  tex.set_magfilter(EggTexture::FT_linear);
2915 
2916  EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
2917  EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
2918 
2919  tex.set_wrap_u(wrap_u);
2920  tex.set_wrap_v(wrap_v);
2921 
2922  LMatrix3d mat = color_def.compute_texture_matrix();
2923  if (!mat.almost_equal(LMatrix3d::ident_mat())) {
2924  tex.set_transform2d(mat);
2925  }
2926 }
2927 
2928 /**
2929  * Applies the blendtype to the EggTexture.
2930  */
2931 void MayaToEggConverter::
2932 apply_texture_blendtype(EggTexture &tex, const MayaShaderColorDef &color_def) {
2933  switch (color_def._blend_type) {
2934  case MayaShaderColorDef::BT_unspecified:
2935  tex.set_env_type(EggTexture::ET_unspecified);
2936  return;
2937  case MayaShaderColorDef::BT_modulate:
2938  tex.set_env_type(EggTexture::ET_modulate);
2939  return;
2940  case MayaShaderColorDef::BT_decal:
2941  tex.set_env_type(EggTexture::ET_decal);
2942  return;
2943  case MayaShaderColorDef::BT_blend:
2944  tex.set_env_type(EggTexture::ET_blend);
2945  return;
2946  case MayaShaderColorDef::BT_replace:
2947  tex.set_env_type(EggTexture::ET_replace);
2948  return;
2949  case MayaShaderColorDef::BT_add:
2950  tex.set_env_type(EggTexture::ET_add);
2951  return;
2952  case MayaShaderColorDef::BT_blend_color_scale:
2953  tex.set_env_type(EggTexture::ET_blend_color_scale);
2954  return;
2955  case MayaShaderColorDef::BT_modulate_glow:
2956  tex.set_env_type(EggTexture::ET_modulate_glow);
2957  return;
2958  case MayaShaderColorDef::BT_modulate_gloss:
2959  tex.set_env_type(EggTexture::ET_modulate_gloss);
2960  return;
2961  case MayaShaderColorDef::BT_normal:
2962  tex.set_env_type(EggTexture::ET_normal);
2963  return;
2964  case MayaShaderColorDef::BT_normal_height:
2965  tex.set_env_type(EggTexture::ET_normal_height);
2966  return;
2967  case MayaShaderColorDef::BT_glow:
2968  tex.set_env_type(EggTexture::ET_glow);
2969  return;
2970  case MayaShaderColorDef::BT_gloss:
2971  tex.set_env_type(EggTexture::ET_gloss);
2972  return;
2973  case MayaShaderColorDef::BT_height:
2974  tex.set_env_type(EggTexture::ET_height);
2975  return;
2976  case MayaShaderColorDef::BT_selector:
2977  tex.set_env_type(EggTexture::ET_selector);
2978  return;
2979  }
2980 }
2981 
2982 /**
2983  * Applies the filename to the EggTexture.
2984  */
2985 void MayaToEggConverter::
2986 apply_texture_filename(EggTexture &tex, const MayaShaderColorDef &def) {
2987  Filename filename = Filename::from_os_specific(def._texture_filename);
2988  Filename fullpath, outpath;
2989  _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath);
2990  tex.set_filename(outpath);
2991  tex.set_fullpath(fullpath);
2992 }
2993 
2994 /**
2995  * Applies the alpha filename to the EggTexture.
2996  */
2997 void MayaToEggConverter::
2998 apply_texture_alpha_filename(EggTexture &tex, const MayaShaderColorDef &def) {
2999  if (def._opposite) {
3000  tex.set_format(EggTexture::F_rgba);
3001  if (def._opposite->_texture_filename != def._texture_filename) {
3002  Filename filename = Filename::from_os_specific(def._opposite->_texture_filename);
3003  Filename fullpath, outpath;
3004  _path_replace->full_convert_path(filename, get_model_path(), fullpath, outpath);
3005  tex.set_alpha_filename(outpath);
3006  tex.set_alpha_fullpath(fullpath);
3007  }
3008  }
3009 }
3010 
3011 /**
3012  * Compares the texture properties already on the texture (presumably set by a
3013  * previous call to apply_texture_uvprops()) and returns false if they differ
3014  * from that specified by the indicated color_def object, or true if they
3015  * match.
3016  */
3017 bool MayaToEggConverter::
3018 compare_texture_uvprops(EggTexture &tex,
3019  const MayaShaderColorDef &color_def) {
3020  bool okflag = true;
3021 
3022  EggTexture::WrapMode wrap_u = color_def._wrap_u ? EggTexture::WM_repeat : EggTexture::WM_clamp;
3023  EggTexture::WrapMode wrap_v = color_def._wrap_v ? EggTexture::WM_repeat : EggTexture::WM_clamp;
3024 
3025  if (wrap_u != tex.determine_wrap_u()) {
3026  // Choose the more general of the two.
3027  if (wrap_u == EggTexture::WM_repeat) {
3028  tex.set_wrap_u(wrap_u);
3029  }
3030  okflag = false;
3031  }
3032  if (wrap_v != tex.determine_wrap_v()) {
3033  if (wrap_v == EggTexture::WM_repeat) {
3034  tex.set_wrap_v(wrap_v);
3035  }
3036  okflag = false;
3037  }
3038 
3039  LMatrix3d m = color_def.compute_texture_matrix();
3040  LMatrix4d mat4(m(0, 0), m(0, 1), 0.0, m(0, 2),
3041  m(1, 0), m(1, 1), 0.0, m(1, 2),
3042  0.0, 0.0, 1.0, 0.0,
3043  m(2, 0), m(2, 1), 0.0, m(2, 2));
3044  if (!mat4.almost_equal(tex.get_transform3d())) {
3045  okflag = false;
3046  }
3047 
3048  return okflag;
3049 }
3050 
3051 /**
3052  * Recursively walks the egg hierarchy, reparenting "decal" type nodes below
3053  * their corresponding "decalbase" type nodes, and setting the flags.
3054  *
3055  * Returns true on success, false if some nodes were incorrect.
3056  */
3057 bool MayaToEggConverter::
3058 reparent_decals(EggGroupNode *egg_parent) {
3059  bool okflag = true;
3060 
3061  // First, walk through all children of this node, looking for the one decal
3062  // base, if any.
3063  EggGroup *decal_base = nullptr;
3064  pvector<EggGroup *> decal_children;
3065 
3066  EggGroupNode::iterator ci;
3067  for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
3068  EggNode *child = (*ci);
3069  if (child->is_of_type(EggGroup::get_class_type())) {
3070  EggGroup *child_group = DCAST(EggGroup, child);
3071  if (child_group->has_object_type("decalbase")) {
3072  if (decal_base != nullptr) {
3073  mayaegg_cat.error()
3074  << "Two children of " << egg_parent->get_name()
3075  << " both have decalbase set: " << decal_base->get_name()
3076  << " and " << child_group->get_name() << "\n";
3077  okflag = false;
3078  }
3079  child_group->remove_object_type("decalbase");
3080  decal_base = child_group;
3081 
3082  } else if (child_group->has_object_type("decal")) {
3083  child_group->remove_object_type("decal");
3084  decal_children.push_back(child_group);
3085  }
3086  }
3087  }
3088 
3089  if (decal_base == nullptr) {
3090  if (!decal_children.empty()) {
3091  mayaegg_cat.warning()
3092  << decal_children.front()->get_name()
3093  << " has decal, but no sibling node has decalbase.\n";
3094  }
3095 
3096  } else {
3097  if (decal_children.empty()) {
3098  mayaegg_cat.warning()
3099  << decal_base->get_name()
3100  << " has decalbase, but no sibling nodes have decal.\n";
3101 
3102  } else {
3103  // All the decal children get moved to be a child of decal base. This
3104  // usually will not affect the vertex positions, but it could if the
3105  // decal base has a transform and the decal child is an instance node.
3106  // So don't do that.
3108  for (di = decal_children.begin(); di != decal_children.end(); ++di) {
3109  EggGroup *child_group = (*di);
3110  decal_base->add_child(child_group);
3111  }
3112 
3113  // Also set the decal state on the base.
3114  decal_base->set_decal_flag(true);
3115  }
3116  }
3117 
3118  // Now recurse on each of the child nodes.
3119  for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
3120  EggNode *child = (*ci);
3121  if (child->is_of_type(EggGroupNode::get_class_type())) {
3122  EggGroupNode *child_group = DCAST(EggGroupNode, child);
3123  if (!reparent_decals(child_group)) {
3124  okflag = false;
3125  }
3126  }
3127  }
3128 
3129  return okflag;
3130 }
3131 
3132 /**
3133  * Returns the TransformType value corresponding to the indicated string, or
3134  * TT_invalid.
3135  */
3136 MayaToEggConverter::TransformType MayaToEggConverter::
3137 string_transform_type(const string &arg) {
3138  if (cmp_nocase(arg, "all") == 0) {
3139  return TT_all;
3140  } else if (cmp_nocase(arg, "model") == 0) {
3141  return TT_model;
3142  } else if (cmp_nocase(arg, "dcs") == 0) {
3143  return TT_dcs;
3144  } else if (cmp_nocase(arg, "none") == 0) {
3145  return TT_none;
3146  } else {
3147  return TT_invalid;
3148  }
3149 }
3150 /**
3151  * Checks to see if we're using legacy or modern shaders and based on the
3152  * result, it passes the vertex color calculations off to either the legacy or
3153  * modern vertex color functions.
3154  */
3155 void MayaToEggConverter::
3156 set_vertex_color(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const LColor &color) {
3157  if (shader == nullptr || shader->_legacy_mode) {
3158  set_vertex_color_legacy(vert, pi, vert_index, shader, color);
3159  } else {
3160  set_vertex_color_modern(vert, pi, vert_index, shader, color);
3161  }
3162 }
3163 /**
3164  * Calls set_color on an EggVertex, determining the correct color values,
3165  * based on the shader, vert_color Maya's vertex & flat color(s). This is the
3166  * original implementation that works only on Lambert shaders/materials in
3167  * Maya.
3168  */
3169 void MayaToEggConverter::
3170 set_vertex_color_legacy(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const LColor &color){
3171  if (pi.hasColor()) {
3172  MColor c;
3173  MStatus status = pi.getColor(c, vert_index);
3174  if (!status) {
3175  status.perror("MItMeshPolygon::getColor");
3176  } else {
3177  // I saw instances where the color components exceeded 1.0 so lets clamp
3178  // the values to 0 to 1
3179  c /= 1.0;
3180  // The vertex color is a color scale that modifies the polygon color,
3181  // not an override that replaces it.
3182  vert.set_color(LColor(c.r * color[0], c.g * color[1], c.b * color[2], c.a * color[3]));
3183 
3184  if (mayaegg_cat.is_spam()) {
3185  mayaegg_cat.spam() << "maya_color = " << c.r << " " << c.g << " " << c.b << " " << c.a << endl;
3186  mayaegg_cat.spam() << "vert_color = " << vert.get_color() << endl;
3187  }
3188  }
3189  } else {
3190  vert.set_color(color);
3191  }
3192 
3193 }
3194 /**
3195  * Calls set_color on an EggVertex, determining the correct color values,
3196  * based on the shader, vert_color Maya's vertex & flat color(s). This
3197  * implementation is designed to work specifically with Phong materials or
3198  * shaders.
3199  */
3200 void MayaToEggConverter::
3201 set_vertex_color_modern(EggVertex &vert, MItMeshPolygon &pi, int vert_index, const MayaShader *shader, const LColor &color) {
3202  // If there's an explicit vertex color, output it.
3203  if (pi.hasColor(vert_index)) {
3204  MColor c;
3205  MStatus status = pi.getColor(c, vert_index);
3206  if (status) {
3207  vert.set_color(LColor(c.r, c.g, c.b, c.a));
3208  return;
3209  }
3210  }
3211 
3212  // If there's no explicit color, use flat color, or white on a textured
3213  // model.
3214  if (shader->_color_maps.empty()) {
3215  const LColord &c = shader->_flat_color;
3216  vert.set_color(LColor((PN_stdfloat)c[0], (PN_stdfloat)c[1], (PN_stdfloat)c[2], (PN_stdfloat)c[3]));
3217  } else {
3218  // there's no explicit color anywhere, must be textured (or blank)
3219  vert.set_color(LColor(1.0f, 1.0f, 1.0f, 1.0f));
3220  }
3221 }
SomethingToEggConverter::has_output_frame_rate
bool has_output_frame_rate() const
Returns true if the frame rate has been explicitly specified via set_output_frame_rate(),...
Definition: somethingToEggConverter.I:316
EggVertexPool::get_vertex
EggVertex * get_vertex(int index) const
Returns the vertex in the pool with the indicated index number, or NULL if no vertices have that inde...
Definition: eggVertexPool.cxx:115
eggData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SomethingToEggConverter::had_error
bool had_error() const
Returns true if an error was detected during the conversion process (unless _allow_errors is true),...
Definition: somethingToEggConverter.I:28
SomethingToEggConverter::get_end_frame
double get_end_frame() const
Returns the value set by a previous call to set_end_frame().
Definition: somethingToEggConverter.I:165
MayaToEggConverter::convert_maya
bool convert_maya()
Fills up the egg_data structure according to the global maya model data.
Definition: mayaToEggConverter.cxx:369
eggTable.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggAnimData::add_data
void add_data(double value)
Adds a single element to the table.
Definition: eggAnimData.I:97
EggSAnimData::optimize
void optimize()
Optimizes the data by collapsing a long table of duplicate values into a single value.
Definition: eggSAnimData.cxx:29
MayaShaderColorDef::get_panda_uvset_name
std::string get_panda_uvset_name()
Maya's default uvset name is "map1".
Definition: mayaShaderColorDef.cxx:235
config_mayaegg.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
eggVertexPool.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShaderColorDef::compute_texture_matrix
LMatrix3d compute_texture_matrix() const
Returns a texture matrix corresponding to the texture transforms indicated by the shader.
Definition: mayaShaderColorDef.cxx:144
EggPrimitive::get_num_textures
get_num_textures
Returns the number of textures applied to the primitive.
Definition: eggPrimitive.h:100
eggXfmSAnim.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShaders::clear
void clear()
Frees all of the previously-defined MayaShader objects associated with this set.
Definition: mayaShaders.cxx:189
eggPrimitive.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeDesc::get_blend_desc
MayaBlendDesc * get_blend_desc(int n) const
Returns the nth MayaBlendDesc object that affects the geometry in this node.
Definition: mayaNodeDesc.cxx:178
pvector
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
mayaEggGroupUserData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SomethingToEggConverter::has_frame_inc
bool has_frame_inc() const
Returns true if the frame increment has been explicitly specified via set_frame_inc(),...
Definition: somethingToEggConverter.I:198
MayaShaders::find_shader_for_node
MayaShader * find_shader_for_node(MObject node, bool legacy_shader)
Extracts the shader assigned to the indicated node.
Definition: mayaShaders.cxx:51
MayaToEggConverter::string_transform_type
static TransformType string_transform_type(const std::string &arg)
Returns the TransformType value corresponding to the indicated string, or TT_invalid.
Definition: mayaToEggConverter.cxx:3137
MayaToEggConverter::add_ignore_slider
void add_ignore_slider(const GlobPattern &glob)
Adds a name pattern to the list of ignore_sliders.
Definition: mayaToEggConverter.cxx:283
Filename::from_os_specific
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328
SomethingToEggConverter::has_neutral_frame
bool has_neutral_frame() const
Returns true if the neutral frame has been explicitly specified via set_neutral_frame(),...
Definition: somethingToEggConverter.I:236
string_utils.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeTree::clear_egg
void clear_egg(EggData *egg_data, EggGroupNode *egg_root, EggGroupNode *skeleton_node, EggGroupNode *morph_node)
Removes all of the references to generated egg structures from the tree, and prepares the tree for ge...
Definition: mayaNodeTree.cxx:288
MayaNodeTree::get_node
MayaNodeDesc * get_node(int n) const
Returns the nth node in the hierarchy, in an arbitrary ordering.
Definition: mayaNodeTree.cxx:263
MayaToEggConverter::add_subset
void add_subset(const GlobPattern &glob)
Adds a name pattern to the list of subset nodes.
Definition: mayaToEggConverter.cxx:246
MayaToEggConverter::get_extension
virtual std::string get_extension() const
Returns the common extension of the file type this converter supports.
Definition: mayaToEggConverter.cxx:164
MayaNodeTree::tag_selected
bool tag_selected()
Tags the just the selected hierarchy for conversion, or the entire hierarchy if nothing is selected.
Definition: mayaNodeTree.cxx:196
dcast.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaNodeDesc::has_object_type
bool has_object_type(std::string object_type) const
Returns true if this node or any of its parent has_object_type of object_type.
Definition: mayaNodeDesc.cxx:289
MayaShaderColorDef
This defines the various attributes that Maya may associate with the "color" channel for a particular...
Definition: mayaShaderColorDef.h:34
post_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShader
Corresponds to a single "shader" in Maya.
Definition: mayaShader.h:30
SomethingToEggConverter::get_egg_data
EggData * get_egg_data()
Returns the EggData structure.
Definition: somethingToEggConverter.I:380
EggTransform::get_transform3d
const LMatrix4d & get_transform3d() const
Returns the overall transform as a 4x4 matrix.
Definition: eggTransform.I:212
eggSAnimData.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggNode::get_vertex_frame_inv
const LMatrix4d & get_vertex_frame_inv() const
Returns the inverse of the matrix returned by get_vertex_frame().
Definition: eggNode.I:135
EggNurbsCurve::set_knot
set_knot
Resets the value of the indicated knot as indicated.
Definition: eggNurbsCurve.h:58
EggTexture::set_uv_name
set_uv_name
Specifies the named set of texture coordinates that this texture will use when it is applied to geome...
Definition: eggTexture.h:341
Filename::get_basename_wo_extension
std::string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:386
EggTable
This corresponds to a.
Definition: eggTable.h:27
mayaShader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaBlendDesc::set_slider
void set_slider(PN_stdfloat value)
Moves the Maya slider associated with this blend shape to the indicated value.
Definition: mayaBlendDesc.cxx:46
MayaNodeTree::tag_all
void tag_all()
Tags the entire hierarchy for conversion.
Definition: mayaNodeTree.cxx:142
MayaNodeDesc::is_joint
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
Definition: mayaNodeDesc.cxx:187
EggNurbsSurface::set_v_knot
void set_v_knot(int k, double value)
Resets the value of the indicated knot as indicated.
Definition: eggNurbsSurface.I:90
MayaNodeTree::untag_named
bool untag_named(const GlobPattern &glob)
Un-tags nodes matching the indicated glob (and all of their children) for conversion.
Definition: mayaNodeTree.cxx:174
MayaToEggConverter::open_api
bool open_api(bool revert_directory=true)
Attempts to open the Maya API if it was not already open, and returns true if successful,...
Definition: mayaToEggConverter.cxx:541
EggMorphList::insert
std::pair< iterator, bool > insert(const MorphType &value)
This is similar to the insert() interface for sets, except it does not guarantee that the resulting l...
Definition: eggMorphList.I:159
EggPrimitive::set_bface_flag
set_bface_flag
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.h:116
MayaNodeTree::reset_sliders
void reset_sliders()
Resets all of the sliders associated with all blend shapes down to 0.
Definition: mayaNodeTree.cxx:566
EggGroupNode
A base class for nodes in the hierarchy that are not leaf nodes.
Definition: eggGroupNode.h:46
eggTexture.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggPrimitive
A base class for any of a number of kinds of geometry primitives: polygons, point lights,...
Definition: eggPrimitive.h:49
EggTexture::determine_wrap_u
WrapMode determine_wrap_u() const
Determines the appropriate wrap in the U direction.
Definition: eggTexture.I:107
SomethingToEggConverter::has_start_frame
bool has_start_frame() const
Returns true if the starting frame has been explicitly specified via set_start_frame(),...
Definition: somethingToEggConverter.I:115
EggFilenameNode::set_fullpath
void set_fullpath(const Filename &fullpath)
Records the full pathname to the file, for the benefit of get_fullpath().
Definition: eggFilenameNode.I:90
EggNurbsSurface::setup
void setup(int u_order, int v_order, int num_u_knots, int num_v_knots)
Prepares a new surface definition with the indicated order and number of knots in each dimension.
Definition: eggNurbsSurface.cxx:36
MayaToEggConverter::get_input_units
virtual DistanceUnit get_input_units()
This may be called after convert_file() has been called and returned true, indicating a successful co...
Definition: mayaToEggConverter.cxx:360
EggTextureCollection::clear
void clear()
Removes all textures from the collection.
Definition: eggTextureCollection.cxx:63
EggNurbsCurve::get_num_cvs
int get_num_cvs() const
Returns the total number of control vertices that *should* be defined for the curve.
Definition: eggNurbsCurve.I:101
EggGroup::remove_object_type
bool remove_object_type(const std::string &object_type)
Removes the first instance of the indicated object type from the group if it is present.
Definition: eggGroup.cxx:161
MayaNodeDesc::has_dag_path
bool has_dag_path() const
Returns true if a Maya dag path has been associated with this node, false otherwise.
Definition: mayaNodeDesc.cxx:150
MayaToEggConverter::clear
void clear()
Frees all of the Maya pointers kept within this object, in preparation for loading a new scene or rel...
Definition: mayaToEggConverter.cxx:567
EggVertex::set_external_index2
void set_external_index2(int external_index2)
Similar to set_external_index(), but this is a different number which may be used for a different pur...
Definition: eggVertex.I:310
EggVertexPool::add_vertex
EggVertex * add_vertex(EggVertex *vertex, int index=-1)
Adds the indicated vertex to the pool.
Definition: eggVertexPool.cxx:421
MayaNodeTree::get_num_blend_descs
int get_num_blend_descs() const
Returns the number of unique MayaBlendDesc objects (and hence the number of morph sliders) discovered...
Definition: mayaNodeTree.cxx:549
MayaShaders::bind_uvsets
void bind_uvsets(MObject mesh)
Causes all shaders in the set to use the given mesh as a file-to-uvset map.
Definition: mayaShaders.cxx:99
EggAttributes::get_color
LColor get_color() const
Returns the color set on this particular attribute.
Definition: eggAttributes.I:91
EggTexture::set_alpha_filename
set_alpha_filename
Specifies a separate file that will be loaded in with the 1- or 3-component texture and applied as th...
Definition: eggTexture.h:347
EggNurbsSurface::get_u_index
int get_u_index(int vertex_index) const
Returns the U index number of the given vertex within the EggPrimitive's linear list of vertices.
Definition: eggNurbsSurface.I:203
MayaToEggConverter::get_additional_extensions
virtual std::string get_additional_extensions() const
Returns a space-separated list of extension, in addition to the one returned by get_extension(),...
Definition: mayaToEggConverter.cxx:173
eggTextureCollection.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SomethingToEggConverter::has_input_frame_rate
bool has_input_frame_rate() const
Returns true if the frame rate has been explicitly specified via set_input_frame_rate(),...
Definition: somethingToEggConverter.I:277
EggPolygon::calculate_normal
bool calculate_normal(LNormald &result, CoordinateSystem cs=CS_default) const
Calculates the true polygon normal–the vector pointing out of the front of the polygon–based on the v...
Definition: eggPolygon.cxx:57
MayaToEggConverter
This class supervises the construction of an EggData structure from a single Maya file,...
Definition: mayaToEggConverter.h:52
MayaNodeDesc::get_num_blend_descs
int get_num_blend_descs() const
Returns the number of unique MayaBlendDesc objects (and hence the number of morph sliders) that affec...
Definition: mayaNodeDesc.cxx:169
EggObject::get_user_data
EggUserData * get_user_data() const
Returns the user data pointer most recently stored on this object, or NULL if nothing was previously ...
Definition: eggObject.cxx:84
EggXfmSAnim
This corresponds to an <Xfm$Anim_S$> entry, which is a collection of up to nine <S$Anim> entries that...
Definition: eggXfmSAnim.h:28
pre_maya_include.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
SomethingToEggConverter::get_neutral_frame
double get_neutral_frame() const
Returns the value set by a previous call to set_neutral_frame().
Definition: somethingToEggConverter.I:245
Filename::set_fullpath
void set_fullpath(const std::string &s)
Replaces the entire filename: directory, basename, extension.
Definition: filename.cxx:695
MayaBlendDesc::get_slider
PN_stdfloat get_slider() const
Returns the current position of the Maya slider associated with this blend shape.
Definition: mayaBlendDesc.cxx:59
EggNode::get_node_frame_inv
const LMatrix4d & get_node_frame_inv() const
Returns the inverse of the matrix returned by get_node_frame().
Definition: eggNode.I:149
MayaNodeDesc::is_tagged
bool is_tagged() const
Returns true if the node has been tagged to be converted, false otherwise.
Definition: mayaNodeDesc.cxx:236
EggVertex::set_uv
void set_uv(const LTexCoordd &texCoord)
Replaces the unnamed UV coordinate pair on the vertex with the indicated value.
Definition: eggVertex.I:193
MayaShaders::find_shader_for_shading_engine
MayaShader * find_shader_for_shading_engine(MObject engine, bool legacy_shader)
Returns the MayaShader object associated with the indicated "shading engine".
Definition: mayaShaders.cxx:133
EggXfmSAnim::optimize
void optimize()
Optimizes the table by collapsing redundant sub-tables.
Definition: eggXfmSAnim.cxx:63
MayaEggGroupUserData
This class contains extra user data which is piggybacked onto EggGroup objects for the purpose of the...
Definition: mayaEggGroupUserData.h:24
EggVertex
Any one-, two-, three-, or four-component vertex, possibly with attributes such as a normal.
Definition: eggVertex.h:39
MayaNodeTree::get_egg_anim
EggXfmSAnim * get_egg_anim(MayaNodeDesc *node_desc)
Returns the anim table corresponding to the joint for the indicated node.
Definition: mayaNodeTree.cxx:481
MayaToEggConverter::clear_force_joints
void clear_force_joints()
Empties the list of force_joints added via add_force_joint().
Definition: mayaToEggConverter.cxx:308
SomethingToEggConverter
This is a base class for a family of converter classes that manage a conversion from some file type t...
Definition: somethingToEggConverter.h:38
EggNurbsCurve
A parametric NURBS curve.
Definition: eggNurbsCurve.h:26
EggPolygon
A single polygon.
Definition: eggPolygon.h:24
eggVertex.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggVertexPool::begin
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
Definition: eggVertexPool.cxx:374
get_bool_attribute
bool get_bool_attribute(MObject &node, const string &attribute_name, bool &value)
Extracts the named boolean attribute from the MObject.
Definition: maya_funcs.cxx:157
MayaNodeTree::build_hierarchy
bool build_hierarchy()
Walks through the complete Maya hierarchy but does not tag any nodes for conversion.
Definition: mayaNodeTree.cxx:68
second_of_pair_iterator
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
Definition: iterator_types.h:43
maya_funcs.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaShaderColorDef::project_uv
LTexCoordd project_uv(const LPoint3d &pos, const LPoint3d &ref_point) const
If the shader has a projection (has_projection() returns true), this computes the appropriate UV corr...
Definition: mayaShaderColorDef.cxx:174
EggPrimitive::add_texture
void add_texture(EggTexture *texture)
Applies the indicated texture to the primitive.
Definition: eggPrimitive.I:162
MayaToEggConverter::get_name
virtual std::string get_name() const
Returns the English name of the file type this converter supports.
Definition: mayaToEggConverter.cxx:156
MayaBlendDesc
A handle to a Maya blend shape description.
Definition: mayaBlendDesc.h:40
MayaToEggConverter::add_force_joint
void add_force_joint(const GlobPattern &glob)
Adds a name pattern to the list of force_joints.
Definition: mayaToEggConverter.cxx:322
get_vec3d_attribute
bool get_vec3d_attribute(MObject &node, const string &attribute_name, LVecBase3d &value)
Extracts the named three-component vector from the MObject.
Definition: maya_funcs.cxx:302
MayaToEggConverter::clear_subsets
void clear_subsets()
Empties the list of subset nodes added via add_subset().
Definition: mayaToEggConverter.cxx:235
SomethingToEggConverter::get_input_frame_rate
double get_input_frame_rate() const
Returns the value set by a previous call to set_input_frame_rate().
Definition: somethingToEggConverter.I:286
EggGroup::has_object_type
bool has_object_type(const std::string &object_type) const
Returns true if the indicated object type has been added to the group, or false otherwise.
Definition: eggGroup.cxx:145
EggPrimitive::add_vertex
EggVertex * add_vertex(EggVertex *vertex)
Adds the indicated vertex to the end of the primitive's list of vertices, and returns it.
Definition: eggPrimitive.cxx:654
SomethingToEggConverter::get_animation_convert
AnimationConvert get_animation_convert() const
Returns how source animation will be converted into egg structures.
Definition: somethingToEggConverter.I:75
plist
This is our own Panda specialization on the default STL list.
Definition: plist.h:35
init_libmayaegg
void init_libmayaegg()
Initializes the library.
Definition: config_mayaegg.cxx:48
SomethingToEggConverter::clear_error
void clear_error()
Resets the error flag to the no-error state.
Definition: somethingToEggConverter.I:19
MayaToEggConverter::set_from_selection
void set_from_selection(bool from_selection)
Sets the flag that indicates whether the currently selected Maya geometry will be converted.
Definition: mayaToEggConverter.cxx:349
EggTransform::set_transform3d
void set_transform3d(const LMatrix4d &mat)
Sets the overall transform as a 4x4 matrix.
Definition: eggTransform.I:188
EggNurbsSurface::get_num_cvs
int get_num_cvs() const
Returns the total number of control vertices that *should* be defined for the surface.
Definition: eggNurbsSurface.I:192
eggNurbsCurve.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggVertex::get_external_index2
int get_external_index2() const
Returns the number set by set_external_index2().
Definition: eggVertex.I:319
MayaToEggConverter::convert_file
virtual bool convert_file(const Filename &filename)
Handles the reading of the input file and converting it to egg.
Definition: mayaToEggConverter.cxx:185
SomethingToEggConverter::has_end_frame
bool has_end_frame() const
Returns true if the ending frame has been explicitly specified via set_end_frame(),...
Definition: somethingToEggConverter.I:156
EggGroup::has_dcs_type
bool has_dcs_type() const
Returns true if the specified DCS type is not DC_none and not DC_unspecified.
Definition: eggGroup.I:199
EggVertexPool::end
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
Definition: eggVertexPool.cxx:385
MayaNodeTree::clear
void clear()
Resets the entire tree in preparation for repopulating with a new scene.
Definition: mayaNodeTree.cxx:272
EggNurbsSurface::get_v_index
int get_v_index(int vertex_index) const
Returns the V index number of the given vertex within the EggPrimitive's linear list of vertices.
Definition: eggNurbsSurface.I:215
EggObject::has_user_data
bool has_user_data() const
Returns true if a generic user data pointer has recently been set and not yet cleared,...
Definition: eggObject.cxx:107
EggTextureCollection::create_unique_texture
EggTexture * create_unique_texture(const EggTexture &copy, int eq)
Creates a new texture if there is not already one equivalent (according to eq, see EggTexture::is_equ...
Definition: eggTextureCollection.cxx:432
EggTransform::add_matrix4
void add_matrix4(const LMatrix4d &mat)
Appends an arbitrary 4x4 matrix to the current transform.
Definition: eggTransform.I:132
MayaToEggConverter::clear_subroots
void clear_subroots()
Empties the list of subroot nodes added via add_subroot().
Definition: mayaToEggConverter.cxx:215
MayaShaderColorDef::has_projection
bool has_projection() const
Returns true if the shader has a projection in effect.
Definition: mayaShaderColorDef.cxx:162
MayaToEggConverter::make_copy
virtual SomethingToEggConverter * make_copy()
Allocates and returns a new copy of the converter.
Definition: mayaToEggConverter.cxx:148
SomethingToEggConverter::get_frame_inc
double get_frame_inc() const
Returns the value set by a previous call to set_frame_inc().
Definition: somethingToEggConverter.I:207
DistanceUnit
DistanceUnit
This enumerated type lists all the kinds of units we're likely to come across in model conversion pro...
Definition: distanceUnit.h:23
EggGroup::ref_vertex
void ref_vertex(EggVertex *vert, double membership=1.0)
Adds the vertex to the set of those referenced by the group, at the indicated membership level.
Definition: eggGroup.cxx:608
SomethingToEggConverter::get_output_frame_rate
double get_output_frame_rate() const
Returns the value set by a previous call to set_output_frame_rate().
Definition: somethingToEggConverter.I:325
MayaToEggConverter::force_joint
bool force_joint(const std::string &name) const
Returns true if the indicated name is on the list of DAG nodes to treat as a joint,...
Definition: mayaToEggConverter.cxx:331
MayaNodeTree::get_egg_group
EggGroup * get_egg_group(MayaNodeDesc *node_desc)
Returns the EggGroupNode corresponding to the group or joint for the indicated node.
Definition: mayaNodeTree.cxx:307
EggPrimitive::reverse_vertex_ordering
virtual void reverse_vertex_ordering()
Reverses the ordering of the vertices in this primitive, if appropriate, in order to change the direc...
Definition: eggPrimitive.cxx:485
mayaToEggConverter.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
EggMorph
A single <Dxyz> or <Duv> or some such entry.
Definition: eggMorph.h:30
MayaShader::get_color_def
MayaShaderColorDef * get_color_def(size_t idx=0) const
This is part of the deprecated codepath.
Definition: mayaShader.cxx:115
MayaNodeDesc::get_dag_path
const MDagPath & get_dag_path() const
Returns the dag path associated with this node.
Definition: mayaNodeDesc.cxx:159
MayaToEggConverter::add_exclude
void add_exclude(const GlobPattern &glob)
Adds a name pattern to the list of excluded nodes.
Definition: mayaToEggConverter.cxx:262
EggTexture
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:30
eggNurbsSurface.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaToEggConverter::add_subroot
void add_subroot(const GlobPattern &glob)
Adds a name pattern to the list of subroot nodes.
Definition: mayaToEggConverter.cxx:226
MayaNodeTree::tag_joint_named
bool tag_joint_named(const GlobPattern &glob)
Tags nodes matching the indicated glob (and all of their children) for conversion.
Definition: mayaNodeTree.cxx:122
EggVertex::get_pos3
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:131
EggVertexPool::create_unique_vertex
EggVertex * create_unique_vertex(const EggVertex &copy)
Creates a new vertex in the pool that is a copy of the indicated one and returns it.
Definition: eggVertexPool.cxx:472
SomethingToEggConverter::get_start_frame
double get_start_frame() const
Returns the value set by a previous call to set_start_frame().
Definition: somethingToEggConverter.I:124
EggVertexPool
A collection of vertices.
Definition: eggVertexPool.h:41
MayaNodeTree::tag_named
bool tag_named(const GlobPattern &glob)
Tags nodes matching the indicated glob (and all of their children) for conversion.
Definition: mayaNodeTree.cxx:152
EggNurbsCurve::setup
void setup(int order, int num_knots)
Prepares a new curve definition with the indicated order and number of knots.
Definition: eggNurbsCurve.cxx:35
EggTexture::set_alpha_fullpath
set_alpha_fullpath
Records the full pathname to the file, for the benefit of get_alpha_fullpath().
Definition: eggTexture.h:348
MayaShader::get_rgba
LColor get_rgba(size_t idx=0) const
Returns the overall color of the shader as a single-precision rgba value, where the alpha component r...
Definition: mayaShader.cxx:131
EggNurbsSurface::set_u_knot
void set_u_knot(int k, double value)
Resets the value of the indicated knot as indicated.
Definition: eggNurbsSurface.I:79
EggTransform::add_translate3d
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
Definition: eggTransform.cxx:71
MayaNodeDesc
Describes a single instance of a node in the Maya scene graph, relating it to the corresponding egg s...
Definition: mayaNodeDesc.h:40
EggVertex::get_external_index
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:300
EggTransform::clear_transform
void clear_transform()
Resets the transform to empty, identity.
Definition: eggTransform.I:114
EggNode
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:35
EggGroup
The main glue of the egg hierarchy, this corresponds to the <Group>, <Instance>, and <Joint> type nod...
Definition: eggGroup.h:34
EggGroupNode::add_child
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
Definition: eggGroupNode.cxx:243
EggTransform::set_transform2d
void set_transform2d(const LMatrix3d &mat)
Sets the overall transform as a 3x3 matrix.
Definition: eggTransform.I:165
EggVertex::set_pos
void set_pos(double pos)
Sets the vertex position.
Definition: eggVertex.I:42
MayaToEggConverter::ignore_slider
bool ignore_slider(const std::string &name) const
Returns true if the indicated name is on the list of sliders to ignore, false otherwise.
Definition: mayaToEggConverter.cxx:292
MayaNodeTree::get_num_nodes
int get_num_nodes() const
Returns the total number of nodes in the hierarchy, not counting the root node.
Definition: mayaNodeTree.cxx:255
MayaNodeTree::get_blend_desc
MayaBlendDesc * get_blend_desc(int n) const
Returns the nth MayaBlendDesc object discovered in the tree.
Definition: mayaNodeTree.cxx:557
GlobPattern
This class can be used to test for string matches against standard Unix- shell filename globbing conv...
Definition: globPattern.h:32
EggNurbsSurface
A parametric NURBS surface.
Definition: eggNurbsSurface.h:27
EggXfmSAnim::add_data
bool add_data(const LMatrix4d &mat)
Adds a new matrix to the table, by adding a new row to each of the subtables.
Definition: eggXfmSAnim.cxx:476
eggGroup.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
MayaToEggConverter::clear_ignore_sliders
void clear_ignore_sliders()
Empties the list of ignore_sliders added via add_ignore_slider().
Definition: mayaToEggConverter.cxx:271
EggSAnimData
Corresponding to an <S$Anim> entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:25
MayaNodeTree::build_node
MayaNodeDesc * build_node(const MDagPath &dag_path)
Returns a pointer to the node corresponding to the indicated dag_path object, creating it first if ne...
Definition: mayaNodeTree.cxx:57
EggVertex::set_external_index
void set_external_index(int external_index)
Sets a special index number that is associated with the EggVertex (but is not written to the egg file...
Definition: eggVertex.I:292
MayaToEggConverter::clear_excludes
void clear_excludes()
Empties the list of excluded nodes added via add_exclude().
Definition: mayaToEggConverter.cxx:254
MayaNodeTree::get_egg_slider
EggSAnimData * get_egg_slider(MayaBlendDesc *blend_desc)
Returns the anim table corresponding to the slider for the indicated blend.
Definition: mayaNodeTree.cxx:491
Filename
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
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
MayaToEggConverter::close_api
void close_api()
Closes the Maya API, if it was previously opened.
Definition: mayaToEggConverter.cxx:556
MayaNodeTree::tag_joint_all
void tag_joint_all()
Tags the entire hierarchy for conversion.
Definition: mayaNodeTree.cxx:112
eggPolygon.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pset
This is our own Panda specialization on the default STL set.
Definition: pset.h:49
MayaShaders::find_uv_link
std::string find_uv_link(const std::string &match)
Returns the current mapping from file to uvset for the given file texture name.
Definition: mayaShaders.cxx:158
EggTexture::determine_wrap_v
WrapMode determine_wrap_v() const
Determines the appropriate wrap in the V direction.
Definition: eggTexture.I:134