Panda3D
 All Classes Functions Variables Enumerations
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
bool has_input_frame_rate() const
Returns true if the frame rate has been explicitly specified via set_input_frame_rate(), or false otherwise.
void clear_transform()
Resets the transform to empty, identity.
Definition: eggTransform.I:125
bool has_dag_path() const
Returns true if a Maya dag path has been associated with this node, false otherwise.
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
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
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
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.
MayaBlendDesc * get_blend_desc(int n) const
Returns the nth MayaBlendDesc object that affects the geometry in this node.
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...
void tag_all()
Tags the entire hierarchy for conversion.
const LMatrix4d & get_transform3d() const
Returns the overall transform as a 4x4 matrix.
Definition: eggTransform.I:251
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
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
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
LColor get_color() const
Returns the color set on this particular attribute.
static TransformType string_transform_type(const string &arg)
Returns the TransformType value corresponding to the indicated string, or TT_invalid.
WrapMode determine_wrap_v() const
Determines the appropriate wrap in the V direction.
Definition: eggTexture.I:166
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.
bool has_object_type(string object_type) const
Returns true if this node or any of its parent has_object_type of object_type.
EggVertex * add_vertex(EggVertex *vertex, int index=-1)
Adds the indicated vertex to the pool.
void set_pos(double pos)
Sets the vertex position.
Definition: eggVertex.I:54
int get_num_cvs() const
Returns the total number of control vertices that should* be defined for the curve.
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:443
void clear()
Resets the entire tree in preparation for repopulating with a new scene.
int get_num_nodes() const
Returns the total number of nodes in the hierarchy, not counting the root node.
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:33
bool ignore_slider(const string &name) const
Returns true if the indicated name is on the list of sliders to ignore, false otherwise.
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.
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
MayaNodeDesc * get_node(int n) const
Returns the nth node in the hierarchy, in an arbitrary ordering.
LVertexd get_pos3() const
Valid if get_num_dimensions() returns 3 or 4.
Definition: eggVertex.I:160
const LMatrix4d & get_vertex_frame_inv() const
Returns the inverse of the matrix returned by get_vertex_frame().
Definition: eggNode.I:167
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 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 &quot;map1&quot;.
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:1661
Corresponding to an &lt;S$Anim&gt; entry, this stores a single column of numbers, for instance for a morph ...
Definition: eggSAnimData.h:28
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:411
bool almost_equal(const LVecBase3d &other, double threshold) const
Returns true if two vectors are memberwise equal within a specified tolerance.
Definition: lvecBase3.h:2614
void clear()
Frees all of the Maya pointers kept within this object, in preparation for loading a new scene or rel...
This is our own Panda specialization on the default STL list.
Definition: plist.h:38
bool has_output_frame_rate() const
Returns true if the frame rate has been explicitly specified via set_output_frame_rate(), or false otherwise.
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.
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 &lt;Dxyz&gt; or &lt;Duv&gt; or some such entry.
Definition: eggMorph.h:33
bool is_tagged() const
Returns true if the node has been tagged to be converted, 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...
bool tag_named(const GlobPattern &glob)
Tags nodes matching the indicated glob (and all of their children) for conversion.
static const LVector3d & zero()
Returns a zero-length vector.
Definition: lvector3.h:915
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:376
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.
MayaShader * find_shader_for_node(MObject node, bool legacy_shader)
Extracts the shader assigned to the indicated node.
Definition: mayaShaders.cxx:56
void clear_force_joints()
Empties the list of force_joints added via add_force_joint().
LMatrix3d compute_texture_matrix() const
Returns a texture matrix corresponding to the texture transforms indicated by the shader...
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
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:39
void set_bface_flag(bool flag)
Sets the backfacing flag of the polygon.
Definition: eggPrimitive.I:289
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...
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 &lt;Group&gt;, &lt;Instance&gt;, and &lt;Joint&gt; 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.
This is a 3-by-3 transform matrix.
Definition: lmatrix.h:4375
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 string get_extension() const
Returns the common extension of the file type this converter supports.
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
iterator end() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
This defines the various attributes that Maya may associate with the &quot;color&quot; channel for a particular...
void clear_ignore_sliders()
Empties the list of ignore_sliders added via add_ignore_slider().
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
WrapMode determine_wrap_u() const
Determines the appropriate wrap in the U direction.
Definition: eggTexture.I:132
PN_stdfloat get_slider() const
Returns the current position of the Maya slider associated with this blend shape. ...
This corresponds to an &lt;Xfm$Anim_S$&gt; entry, which is a collection of up to nine &lt;S$Anim&gt; entries that...
Definition: eggXfmSAnim.h:33
MayaShaderColorDef * get_color_def(size_t idx=0) const
This is part of the deprecated codepath.
Definition: mayaShader.cxx:123
string find_uv_link(const string &match)
Returns the current mapping from file to uvset for the given file texture name.
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
int get_num_textures() const
Returns the number of textures applied to the primitive.
Definition: eggPrimitive.I:218
virtual string get_name() const
Returns the English name of the file type this converter supports.
bool tag_joint_named(const GlobPattern &glob)
Tags nodes matching the indicated glob (and all of their children) for conversion.
Corresponds to a single &quot;shader&quot; 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 &quot;shading engine&quot;.
bool has_projection() const
Returns true if the shader has a projection in effect.
A parametric NURBS curve.
Definition: eggNurbsCurve.h:28
int get_num_cvs() const
Returns the total number of control vertices that should* be defined for the surface.
void optimize()
Optimizes the table by collapsing redundant sub-tables.
Definition: eggXfmSAnim.cxx:75
bool had_error() const
Returns true if an error was detected during the conversion process (unless _allow_errors is true)...
void clear_subroots()
Empties the list of subroot nodes added via add_subroot().
AnimationConvert get_animation_convert() const
Returns how source animation will be converted into egg structures.
A single polygon.
Definition: eggPolygon.h:26
void add_translate3d(const LVector3d &translate)
Appends a 3-d translation operation to the current transform.
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.
double get_input_frame_rate() const
Returns the value set by a previous call to set_input_frame_rate().
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
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:746
string get_basename_wo_extension() const
Returns the basename part of the filename, without the file extension.
Definition: filename.I:460
double get_frame_inc() const
Returns the value set by a previous call to set_frame_inc().
This is a three-component point in space (as opposed to a three-component vector, which represents a ...
Definition: lpoint3.h:531
EggNode * add_child(EggNode *node)
Adds the indicated child to the group and returns it.
const MDagPath & get_dag_path() const
Returns the dag path associated with this node.
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
double get_start_frame() const
Returns the value set by a previous call to set_start_frame().
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.
MayaBlendDesc * get_blend_desc(int n) const
Returns the nth MayaBlendDesc object discovered in the tree.
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
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
void add_force_joint(const GlobPattern &glob)
Adds a name pattern to the list of force_joints.
A parametric NURBS surface.
int get_external_index2() const
Returns the number set by set_external_index2().
Definition: eggVertex.I:401
bool calculate_normal(LNormald &result, CoordinateSystem cs=CS_default) const
Calculates the true polygon normal–the vector pointing out of the front of the polygon–based on the v...
Definition: eggPolygon.cxx:57
double get_neutral_frame() const
Returns the value set by a previous call to set_neutral_frame().
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.
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.
void set_from_selection(bool from_selection)
Sets the flag that indicates whether the currently selected Maya geometry will be converted...
void tag_joint_all()
Tags the entire hierarchy for conversion.
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_v_index(int vertex_index) const
Returns the V index number of the given vertex within the EggPrimitive&#39;s linear list of vertices...
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...
double get_output_frame_rate() const
Returns the value set by a previous call to set_output_frame_rate().
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...
int get_num_blend_descs() const
Returns the number of unique MayaBlendDesc objects (and hence the number of morph sliders) that affec...
const LMatrix4d & get_node_frame_inv() const
Returns the inverse of the matrix returned by get_node_frame().
Definition: eggNode.I:183
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
double get_end_frame() const
Returns the value set by a previous call to set_end_frame().
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
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
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