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