Panda3D
 All Classes Functions Variables Enumerations
maxToEggConverter.cxx
1 // Filename: maxToEggConverter.cxx
2 // Created by Corey Revilla and Ken Strickland (6/22/03)
3 // from mayaToEggConverter.cxx created by drose (10Nov99)
4 //
5 // Updated by Fei Wang, Carnegie Mellon University Entertainment
6 // Technology Center student, 29Jul2009: Fixed vertex color,
7 // animation hierarchy, texture swapping bugs; added collision choices to
8 // exporter.
9 //
10 // Updated by Andrew Gartner, Carnegie Mellon University Entertainment
11 // Technology Center. 27Apr2010: Collision is now done through User Defined Properties
12 // By default a plane without a standard material gets UV's as well
13 // as any object without a texture but with a standard material.
14 // Point objects are now supported as "locators" for a point in space
15 // within the egg.
16 ////////////////////////////////////////////////////////////////////
17 //
18 // PANDA 3D SOFTWARE
19 // Copyright (c) Carnegie Mellon University. All rights reserved.
20 //
21 // All use of this software is subject to the terms of the revised BSD
22 // license. You should have received a copy of this license along
23 // with this source code in a file named "LICENSE."
24 //
25 ////////////////////////////////////////////////////////////////////
26 
27 #include "maxEgg.h"
28 #include "config_util.h"
29 
30 ////////////////////////////////////////////////////////////////////
31 // Function: MaxToEggConverter::Constructor
32 // Access: Public
33 // Description:
34 ////////////////////////////////////////////////////////////////////
35 MaxToEggConverter::
36 MaxToEggConverter()
37 {
38  reset();
39 }
40 
41 ////////////////////////////////////////////////////////////////////
42 // Function: MaxToEggConverter::Destructor
43 // Access: Public, Virtual
44 // Description:
45 ////////////////////////////////////////////////////////////////////
46 MaxToEggConverter::
47 ~MaxToEggConverter()
48 {
49 }
50 
51 ////////////////////////////////////////////////////////////////////
52 // Function: MaxToEggConverter::reset
53 ////////////////////////////////////////////////////////////////////
54 void MaxToEggConverter::reset() {
55  _cur_tref = 0;
56  _current_frame = 0;
57  _textures.clear();
58  _egg_data = NULL;
59 }
60 
61 ////////////////////////////////////////////////////////////////////
62 // Function: MaxToEggConverter::convert
63 // Access: Public
64 // Description: Fills up the egg_data structure according to the
65 // global Max model data. Returns true if successful,
66 // false if there is an error. If from_selection is
67 // true, the converted geometry is based on that which
68 // is selected; otherwise, it is the entire Max scene.
69 ////////////////////////////////////////////////////////////////////
71 
72  _options = options;
73 
74  Filename fn;
75 #ifdef _UNICODE
76  fn = Filename::from_os_specific_w(_options->_file_name);
77 #else
78  fn = Filename::from_os_specific(_options->_file_name);
79 #endif
80 
81  _options->_path_replace->_path_directory = fn.get_dirname();
82 
83  _egg_data = new EggData;
84  if (_egg_data->get_coordinate_system() == CS_default) {
85  _egg_data->set_coordinate_system(CS_zup_right);
86  }
87 
88  // Figure out the animation parameters.
89 
90  // Get the start and end frames and the animation frame rate from Max
91 
92  Interval anim_range = _options->_max_interface->GetAnimRange();
93  int start_frame = anim_range.Start()/GetTicksPerFrame();
94  int end_frame = anim_range.End()/GetTicksPerFrame();
95 
96  if (!_options->_export_all_frames) {
97  if (_options->_start_frame < start_frame) _options->_start_frame = start_frame;
98  if (_options->_start_frame > end_frame) _options->_start_frame = end_frame;
99  if (_options->_end_frame < start_frame) _options->_end_frame = start_frame;
100  if (_options->_end_frame > end_frame) _options->_end_frame = end_frame;
101  if (_options->_end_frame < _options->_start_frame) _options->_end_frame = _options->_start_frame;
102  start_frame = _options->_start_frame;
103  end_frame = _options->_end_frame;
104  }
105 
106  int frame_inc = 1;
107  int output_frame_rate = GetFrameRate();
108 
109  bool all_ok = true;
110 
111  if (_options->_export_whole_scene) {
112  _tree._export_mesh = false;
113  all_ok = _tree.build_complete_hierarchy(_options->_max_interface->GetRootNode(), NULL, 0);
114  } else {
115  _tree._export_mesh = true;
116  all_ok = _tree.build_complete_hierarchy(_options->_max_interface->GetRootNode(), &_options->_node_list.front(), _options->_node_list.size());
117  }
118 
119  if (all_ok) {
120  switch (_options->_anim_type) {
121  case MaxEggOptions::AT_pose:
122  //pose: set to a specific frame, then get out the static geometry.
123  //sprintf(Logger::GetLogString(), "Extracting geometry from frame #%d.", start_frame);
124  //Logger::Log( MTEC, Logger::SAT_MEDIUM_LEVEL, Logger::GetLogString() );
125  //Logger::Log( MTEC, Logger::SAT_MEDIUM_LEVEL, "Converting static model." );
126  _current_frame = start_frame;
127  all_ok = convert_hierarchy(_egg_data);
128  break;
129 
130  case MaxEggOptions::AT_model:
131  // model: get out an animatable model with joints and vertex
132  // membership.
133  all_ok = convert_char_model();
134  break;
135 
136  case MaxEggOptions::AT_chan:
137  // chan: get out a series of animation tables.
138  all_ok = convert_char_chan(start_frame, end_frame, frame_inc,
139  output_frame_rate);
140  break;
141 
142  case MaxEggOptions::AT_both:
143  // both: Put a model and its animation into the same egg file.
144  _options->_anim_type = MaxEggOptions::AT_model;
145  if (!convert_char_model()) {
146  all_ok = false;
147  }
148  _options->_anim_type = MaxEggOptions::AT_chan;
149  if (!convert_char_chan(start_frame, end_frame, frame_inc,
150  output_frame_rate)) {
151  all_ok = false;
152  }
153  // Set the type back to AT_both
154  _options->_anim_type = MaxEggOptions::AT_both;
155  break;
156 
157  default:
158  all_ok = false;
159  };
160 
161  reparent_decals(_egg_data);
162  }
163 
164  if (all_ok) {
165  _egg_data->recompute_tangent_binormal_auto();
166  _egg_data->remove_unused_vertices(true);
167  }
168 
169  _options->_successful = all_ok;
170 
171  if (all_ok) {
172 #ifdef _UNICODE
173  Filename fn = Filename::from_os_specific_w(_options->_file_name);
174 #else
175  Filename fn = Filename::from_os_specific(_options->_file_name);
176 #endif
177  return _egg_data->write_egg(fn);
178  } else {
179  return false;
180  }
181 }
182 
183 ////////////////////////////////////////////////////////////////////
184 // Function: MaxToEggConverter::convert_char_model
185 // Access: Private
186 // Description: Converts the file as an animatable character
187 // model, with joints and vertex membership.
188 ////////////////////////////////////////////////////////////////////
189 bool MaxToEggConverter::
190 convert_char_model() {
191  std::string character_name = "character";
192  _current_frame = _options->_start_frame;
193 
194  EggGroup *char_node = new EggGroup(character_name);
195  _egg_data->add_child(char_node);
196  char_node->set_dart_type(EggGroup::DT_default);
197 
198  return convert_hierarchy(char_node);
199 }
200 
201 ////////////////////////////////////////////////////////////////////
202 // Function: MaxToEggConverter::convert_char_chan
203 // Access: Private
204 // Description: Converts the animation as a series of tables to apply
205 // to the character model, as retrieved earlier via
206 // AC_model.
207 ////////////////////////////////////////////////////////////////////
208 bool MaxToEggConverter::
209 convert_char_chan(double start_frame, double end_frame, double frame_inc,
210  double output_frame_rate) {
211  std::string character_name = "character";
212 
213  EggTable *root_table_node = new EggTable();
214  _egg_data->add_child(root_table_node);
215  EggTable *bundle_node = new EggTable(character_name);
216  bundle_node->set_table_type(EggTable::TT_bundle);
217  root_table_node->add_child(bundle_node);
218  EggTable *skeleton_node = new EggTable("<skeleton>");
219  bundle_node->add_child(skeleton_node);
220 
221  // Set the frame rate before we start asking for anim tables to be
222  // created.
223  _tree._fps = output_frame_rate / frame_inc;
224  _tree.clear_egg(_egg_data, NULL, skeleton_node);
225 
226  // Now we can get the animation data by walking through all of the
227  // frames, one at a time, and getting the joint angles at each
228  // frame.
229 
230  // This is just a temporary EggGroup to receive the transform for
231  // each joint each frame.
232  EggGroup* tgroup;
233 
234  int num_nodes = _tree.get_num_nodes();
235  int i;
236 
237  TimeValue frame = start_frame;
238  TimeValue frame_stop = end_frame;
239  while (frame <= frame_stop) {
240  _current_frame = frame;
241  for (i = 0; i < num_nodes; i++) {
242  // Find all joints in the hierarchy
243  MaxNodeDesc *node_desc = _tree.get_node(i);
244  if (node_desc->is_joint()) {
245  tgroup = new EggGroup();
246  INode *max_node = node_desc->get_max_node();
247 
248  if (node_desc->_parent && node_desc->_parent->is_joint()) {
249  // If this joint also has a joint as a parent, the parent's
250  // transformation has to be divided out of this joint's TM
251  get_joint_transform(max_node, node_desc->_parent->get_max_node(),
252  tgroup);
253  } else {
254  get_joint_transform(max_node, NULL, tgroup);
255  }
256 
257  EggXfmSAnim *anim = _tree.get_egg_anim(node_desc);
258  if (!anim->add_data(tgroup->get_transform3d())) {
259  // *** log an error
260  }
261  delete tgroup;
262  }
263  }
264 
265  frame += frame_inc;
266  }
267 
268  // Now optimize all of the tables we just filled up, for no real
269  // good reason, except that it makes the resulting egg file a little
270  // easier to read.
271  for (i = 0; i < num_nodes; i++) {
272  MaxNodeDesc *node_desc = _tree.get_node(i);
273  if (node_desc->is_joint()) {
274  _tree.get_egg_anim(node_desc)->optimize();
275  }
276  }
277 
278  return true;
279 }
280 
281 ////////////////////////////////////////////////////////////////////
282 // Function: MaxToEggConverter::convert_hierarchy
283 // Access: Private
284 // Description: Generates egg structures for each node in the Max
285 // hierarchy.
286 ////////////////////////////////////////////////////////////////////
287 bool MaxToEggConverter::
288 convert_hierarchy(EggGroupNode *egg_root) {
289  //int num_nodes = _tree.get_num_nodes();
290 
291  _tree.clear_egg(_egg_data, egg_root, NULL);
292  for (int i = 0; i < _tree.get_num_nodes(); i++) {
293  if (!process_model_node(_tree.get_node(i))) {
294  return false;
295  }
296  }
297 
298  return true;
299 }
300 
301 ////////////////////////////////////////////////////////////////////
302 // Function: MaxToEggConverter::process_model_node
303 // Access: Private
304 // Description: Converts the indicated Max node to the
305 // corresponding Egg structure. Returns true if
306 // successful, false if an error was encountered.
307 ////////////////////////////////////////////////////////////////////
308 bool MaxToEggConverter::
309 process_model_node(MaxNodeDesc *node_desc) {
310  if (!node_desc->has_max_node()) {
311  // If the node has no Max equivalent, never mind.
312  return true;
313  }
314 
315  // Skip all nodes that represent joints in the geometry, but aren't
316  // the actual joints themselves
317  if (node_desc->is_node_joint()) {
318  return true;
319  }
320 
321  TimeValue time = 0;
322  INode *max_node = node_desc->get_max_node();
323 
324  ObjectState state;
325  state = max_node->EvalWorldState(_current_frame * GetTicksPerFrame());
326 
327  if (node_desc->is_joint()) {
328  EggGroup *egg_group = _tree.get_egg_group(node_desc);
329  // Don't bother with joints unless we're getting an animatable
330  // model.
331  if (_options->_anim_type == MaxEggOptions::AT_model) {
332  get_joint_transform(max_node, egg_group);
333  }
334  } else {
335  if (state.obj) {
336  EggGroup *egg_group = NULL;
337  TriObject *myMaxTriObject;
338  Mesh max_mesh;
339  //Call the correct exporter based on what type of object this is.
340  switch( state.obj->SuperClassID() ){
341 
342  case GEOMOBJECT_CLASS_ID:
343  egg_group = _tree.get_egg_group(node_desc);
344  get_transform(max_node, egg_group);
345 
346  //Try converting this geometric object to a mesh we can use.
347  if (!state.obj->CanConvertToType(Class_ID(TRIOBJ_CLASS_ID, 0))) {
348  return false;
349  }
350  //Convert our state object to a TriObject.
351  myMaxTriObject = (TriObject *) state.obj->ConvertToType(time, Class_ID(TRIOBJ_CLASS_ID, 0 ));
352  // *** Want to figure this problem out
353  // If actual conversion was required, then we want to delete this
354  // new mesh later to avoid mem leaks. **BROKEN. doesnt delete
355 
356  //Now, get the mesh.
357  max_mesh = myMaxTriObject->GetMesh();
358  make_polyset(max_node, &max_mesh, egg_group);
359 
360  if (myMaxTriObject != state.obj)
361  delete myMaxTriObject;
362  break;
363 
364  case SHAPE_CLASS_ID:
365  if (state.obj->ClassID() == EDITABLE_SURF_CLASS_ID) {
366  NURBSSet getSet;
367  if (GetNURBSSet(state.obj, time, getSet, TRUE)) {
368  NURBSObject *nObj = getSet.GetNURBSObject(0);
369  if (nObj->GetType() == kNCVCurve) {
370  //It's a CV Curve, process it
371  egg_group = _tree.get_egg_group(node_desc);
372  get_transform(max_node, egg_group);
373  make_nurbs_curve(max_node, (NURBSCVCurve *)nObj,
374  time, egg_group);
375  }
376  }
377  }
378  break;
379 
380  case CAMERA_CLASS_ID:
381  break;
382 
383  case LIGHT_CLASS_ID:
384  break;
385 
386  case HELPER_CLASS_ID:
387  //we should export Point objects to give Max the equivalent of Maya locators
388  if (state.obj->ClassID() == Class_ID(POINTHELP_CLASS_ID, 0)) {
389 
390  egg_group = _tree.get_egg_group(node_desc);
391  get_transform(max_node, egg_group);
392 
393  } else {
394 
395  break;
396 
397  }
398 
399 
400 
401 
402  }
403  }
404  }
405 
406  return true;
407 }
408 
409 ////////////////////////////////////////////////////////////////////
410 // Function: MaxToEggConverter::get_transform
411 // Access: Private
412 // Description: Extracts the transform on the indicated Maya node,
413 // and applies it to the corresponding Egg node.
414 ////////////////////////////////////////////////////////////////////
415 void MaxToEggConverter::
416 get_transform(INode *max_node, EggGroup *egg_group) {
417  if (_options->_anim_type == MaxEggOptions::AT_model) {
418  // When we're getting an animated model, we only get transforms
419  // for joints.
420  return;
421  }
422 
423  if ( !egg_group ) {
424  return;
425  }
426 
427  // Gets the TM for this node, a matrix which encapsulates all transformations
428  // it takes to get to the current node, including parent transformations.
429  Matrix3 pivot = max_node->GetNodeTM(_current_frame * GetTicksPerFrame());
430 
431  //This is the Panda-flava-flav-style matrix we'll be exporting to.
432  Point3 row0 = pivot.GetRow(0);
433  Point3 row1 = pivot.GetRow(1);
434  Point3 row2 = pivot.GetRow(2);
435  Point3 row3 = pivot.GetRow(3);
436 
437  LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
438  row1.x, row1.y, row1.z, 0.0f,
439  row2.x, row2.y, row2.z, 0.0f,
440  row3.x, row3.y, row3.z, 1.0f );
441 
442  // Now here's the tricky part. I believe this command strips out the node
443  // "frame" which is the sum of all transformations enacted by the parent of
444  // this node. This should reduce to the transformation relative to this
445  // node's parent
446  m4d = m4d * egg_group->get_node_frame_inv();
447  if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
448  egg_group->add_matrix4(m4d);
449  }
450 }
451 
452 ////////////////////////////////////////////////////////////////////
453 // Function: MaxToEggConverter::get_object_transform
454 // Access: Private
455 // Description: Extracts the transform on the indicated Maya node,
456 // and applies it to the corresponding Egg node.
457 ////////////////////////////////////////////////////////////////////
458 LMatrix4d MaxToEggConverter::
459 get_object_transform(INode *max_node) {
460 
461  // Gets the TM for this node, a matrix which encapsulates all transformations
462  // it takes to get to the current node, including parent transformations.
463  Matrix3 pivot = max_node->GetObjectTM(_current_frame * GetTicksPerFrame());
464 
465  Point3 row0 = pivot.GetRow(0);
466  Point3 row1 = pivot.GetRow(1);
467  Point3 row2 = pivot.GetRow(2);
468  Point3 row3 = pivot.GetRow(3);
469 
470  LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
471  row1.x, row1.y, row1.z, 0.0f,
472  row2.x, row2.y, row2.z, 0.0f,
473  row3.x, row3.y, row3.z, 1.0f );
474  return m4d;
475 }
476 
477 ////////////////////////////////////////////////////////////////////
478 // Function: MaxToEggConverter::get_joint_transform
479 // Access: Private
480 // Description: Extracts the transform on the indicated Maya node,
481 // as appropriate for a joint in an animated character,
482 // and applies it to the indicated node. This is
483 // different from get_transform() in that it does not
484 // respect the _transform_type flag, and it does not
485 // consider the relative transforms within the egg file.
486 ////////////////////////////////////////////////////////////////////
487 void MaxToEggConverter::
488 get_joint_transform(INode *max_node, EggGroup *egg_group) {
489 
490  if ( !egg_group ) {
491  return;
492  }
493 
494  // Gets the TM for this node, a matrix which encapsulates all transformations
495  // it takes to get to the current node, including parent transformations.
496  Matrix3 pivot = max_node->GetNodeTM(_current_frame * GetTicksPerFrame());
497  Point3 row0 = pivot.GetRow(0);
498  Point3 row1 = pivot.GetRow(1);
499  Point3 row2 = pivot.GetRow(2);
500  Point3 row3 = pivot.GetRow(3);
501 
502  LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
503  row1.x, row1.y, row1.z, 0.0f,
504  row2.x, row2.y, row2.z, 0.0f,
505  row3.x, row3.y, row3.z, 1.0f );
506 
507  // Now here's the tricky part. I believe this command strips out the node
508  // "frame" which is the sum of all transformations enacted by the parent of
509  // this node. This should reduce to the transformation relative to this
510  // node's parent
511  m4d = m4d * egg_group->get_node_frame_inv();
512  if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
513  egg_group->add_matrix4(m4d);
514  }
515 }
516 
517 ////////////////////////////////////////////////////////////////////
518 // Function: MaxToEggConverter::get_joint_transform
519 // Access: Private
520 // Description: Extracts the transform on the indicated Maya node,
521 // as appropriate for a joint in an animated character,
522 // and applies it to the indicated node. This is
523 // different from get_transform() in that it does not
524 // respect the _transform_type flag, and it does not
525 // consider the relative transforms within the egg file.
526 ////////////////////////////////////////////////////////////////////
527 void MaxToEggConverter::
528 get_joint_transform(INode *max_node, INode *parent_node, EggGroup *egg_group) {
529 
530  if ( !egg_group ) {
531  return;
532  }
533 
534  // Gets the TM for this node, a matrix which encapsulates all transformations
535  // it takes to get to the current node, including parent transformations.
536  Matrix3 pivot = max_node->GetNodeTM(_current_frame * GetTicksPerFrame());
537  Point3 row0 = pivot.GetRow(0);
538  Point3 row1 = pivot.GetRow(1);
539  Point3 row2 = pivot.GetRow(2);
540  Point3 row3 = pivot.GetRow(3);
541 
542  LMatrix4d m4d(row0.x, row0.y, row0.z, 0.0f,
543  row1.x, row1.y, row1.z, 0.0f,
544  row2.x, row2.y, row2.z, 0.0f,
545  row3.x, row3.y, row3.z, 1.0f );
546 
547  if (parent_node) {
548  Matrix3 parent_pivot = parent_node->GetNodeTM(_current_frame * GetTicksPerFrame());
549  // parent_pivot.Invert();
550  row0 = parent_pivot.GetRow(0);
551  row1 = parent_pivot.GetRow(1);
552  row2 = parent_pivot.GetRow(2);
553  row3 = parent_pivot.GetRow(3);
554 
555  LMatrix4d pi_m4d(row0.x, row0.y, row0.z, 0.0f,
556  row1.x, row1.y, row1.z, 0.0f,
557  row2.x, row2.y, row2.z, 0.0f,
558  row3.x, row3.y, row3.z, 1.0f );
559 
560  // Now here's the tricky part. I believe this command strips out the node
561  // "frame" which is the sum of all transformations enacted by the parent of
562  // this node. This should reduce to the transformation relative to this
563  // node's parent
564  pi_m4d.invert_in_place();
565  m4d = m4d * pi_m4d;
566  }
567  if (!m4d.almost_equal(LMatrix4d::ident_mat(), 0.0001)) {
568  egg_group->add_matrix4(m4d);
569  }
570 }
571 
572 ////////////////////////////////////////////////////////////////////
573 // Function: MaxToEggConverter::make_nurbs_curve
574 // Access: Private
575 // Description: Converts the indicated Maya NURBS curve (a standalone
576 // curve, not a trim curve) to a corresponding egg
577 // structure and attaches it to the indicated egg group.
578 ////////////////////////////////////////////////////////////////////
579 bool MaxToEggConverter::
580 make_nurbs_curve(INode *max_node, NURBSCVCurve *curve,
581  TimeValue time, EggGroup *egg_group)
582 {
583  int degree = curve->GetOrder();
584  int cvs = curve->GetNumCVs();
585  int knots = curve->GetNumKnots();
586  int i;
587 
588  if (knots != cvs + degree) {
589  return false;
590  }
591 
592  string name = max_node->GetName();
593 
594  string vpool_name = name + ".cvs";
595  EggVertexPool *vpool = new EggVertexPool(vpool_name);
596  egg_group->add_child(vpool);
597 
598  EggNurbsCurve *egg_curve = new EggNurbsCurve(name);
599  egg_group->add_child(egg_curve);
600  egg_curve->setup(degree, knots);
601 
602  for (i = 0; i < knots; i++)
603  egg_curve->set_knot(i, curve->GetKnot(i));
604 
605  LMatrix4d vertex_frame_inv = egg_group->get_vertex_frame_inv();
606 
607  for (i = 0; i < cvs; i++) {
608  NURBSControlVertex *cv = curve->GetCV(i);
609  if (!cv) {
610  char buf[1024];
611  sprintf(buf, "Error getting CV %d", i);
612  return false;
613  } else {
614  EggVertex vert;
615  LPoint4d p4d(0, 0, 0, 1.0);
616  cv->GetPosition(time, p4d[0], p4d[1], p4d[2]);
617  p4d = p4d * vertex_frame_inv;
618  vert.set_pos(p4d);
619  egg_curve->add_vertex(vpool->create_unique_vertex(vert));
620  }
621  }
622 
623  return true;
624 }
625 
626 ////////////////////////////////////////////////////////////////////
627 // Function: MaxToEggConverter::make_polyset
628 // Access: Private
629 // Description: Converts the indicated Maya polyset to a bunch of
630 // EggPolygons and parents them to the indicated egg
631 // group.
632 ////////////////////////////////////////////////////////////////////
633 void MaxToEggConverter::
634 make_polyset(INode *max_node, Mesh *mesh,
635  EggGroup *egg_group, Shader *default_shader) {
636 
637  mesh->buildNormals();
638 
639  if (mesh->getNumFaces() == 0) {
640  return;
641  }
642 
643  // One way to convert the mesh would be to first get out all the
644  // vertices in the mesh and add them into the vpool, then when we
645  // traverse the polygons we would only have to index them into the
646  // vpool according to their Maya vertex index.
647 
648  // Unfortunately, since Maya may store multiple normals and/or
649  // colors for each vertex according to which polygon it is in, that
650  // approach won't necessarily work. In egg, those split-property
651  // vertices have to become separate vertices. So instead of adding
652  // all the vertices up front, we'll start with an empty vpool, and
653  // add vertices to it on the fly.
654 
655  string node_name = max_node->GetName();
656  string vpool_name = node_name + ".verts";
657  EggVertexPool *vpool = new EggVertexPool(vpool_name);
658  egg_group->add_child(vpool);
659 
660  // We will need to transform all vertices from world coordinate
661  // space into the vertex space appropriate to this node. Usually,
662  // this is the same thing as world coordinate space, and this matrix
663  // will be identity; but if the node is under an instance
664  // (particularly, for instance, a billboard) then the vertex space
665  // will be different from world space.
666  LMatrix4d vertex_frame = get_object_transform(max_node) *
667  egg_group->get_vertex_frame_inv();
668 
669 
670  for ( int iFace=0; iFace < mesh->getNumFaces(); iFace++ ) {
671  EggPolygon *egg_poly = new EggPolygon;
672  egg_group->add_child(egg_poly);
673 
674  egg_poly->set_bface_flag(_options->_double_sided);
675 
676  Face face = mesh->faces[iFace];
677 
678  const PandaMaterial &pmat = get_panda_material(max_node->GetMtl(), face.getMatID());
679 
680  // Get the vertices for the polygon.
681  for ( int iVertex=0; iVertex < 3; iVertex++ ) {
682  EggVertex vert;
683 
684  // Get the vertex position
685  Point3 vertex = mesh->getVert(face.v[iVertex]);
686  LPoint3d p3d(vertex.x, vertex.y, vertex.z);
687  p3d = p3d * vertex_frame;
688  vert.set_pos(p3d);
689 
690  // Get the vertex normal
691  Point3 normal = get_max_vertex_normal(mesh, iFace, iVertex);
692  LVector3d n3d(normal.x, normal.y, normal.z);
693  // *** Not quite sure if this transform should be applied, but it may
694  // explain why normals were weird previously
695  n3d = n3d * vertex_frame;
696  vert.set_normal(n3d);
697 
698  // Get the vertex color
699  if(mesh->vcFace) // if has vcFace, has used vertex color
700  {
701  VertColor vertexColor = get_max_vertex_color(mesh, iFace, iVertex);
702  LColor pVC(vertexColor.x, vertexColor.y, vertexColor.z, 1);
703  vert.set_color(pVC);
704  }
705  // Get the UVs for this vertex
706 
707  //first check if we returned nothing in the channels slot
708  //we need UV's even in this case
709  //because the user may not have put a material
710  //on the object at all
711  if (pmat._map_channels.size() == 0) {
712  //since the channel will always be one because there's
713  //no other textures then don't bother with the name
714  UVVert uvw = get_max_vertex_texcoord(mesh, iFace, iVertex, 1);
715  vert.set_uv( LTexCoordd(uvw.x, uvw.y));
716  }
717  //otherwise go through and generate the maps per channel
718  //this will also generate default UV's as long
719  //as the user applies a standard material to the object
720  for (int iChan=0; iChan<pmat._map_channels.size(); iChan++) {
721  int channel = pmat._map_channels[iChan];
722  ostringstream uvname;
723  uvname << "m" << channel;
724  UVVert uvw = get_max_vertex_texcoord(mesh, iFace, iVertex, channel);
725  // changes allow the first channel to be swapped
726  if(channel == 1)
727  vert.set_uv( LTexCoordd(uvw.x, uvw.y));
728  else
729  vert.set_uv( uvname.str(), LTexCoordd(uvw.x, uvw.y));
730 
731  }
732 
733  vert.set_external_index(face.v[iVertex]);
734 
735  egg_poly->add_vertex(vpool->create_unique_vertex(vert));
736  }
737 
738  //Max uses normals, not winding, to determine which way a
739  //polygon faces. Make sure the winding and that normal agree
740 
741  EggVertex *verts[3];
742  LPoint3d points[3];
743 
744  for (int i = 0; i < 3; i++) {
745  verts[i] = egg_poly->get_vertex(i);
746  points[i] = verts[i]->get_pos3();
747  }
748 
749  LVector3d realNorm = ((points[1] - points[0]).cross(points[2] - points[0]));
750  Point3 maxNormTemp = mesh->getFaceNormal(iFace);
751  LVector3d maxNorm = (LVector3d(maxNormTemp.x, maxNormTemp.y, maxNormTemp.z) *
752  vertex_frame);
753 
754  if (realNorm.dot(maxNorm) < 0.0) {
755  egg_poly->set_vertex(0, verts[2]);
756  egg_poly->set_vertex(2, verts[0]);
757  }
758 
759  for (int i=0; i<pmat._texture_list.size(); i++) {
760  egg_poly->add_texture(pmat._texture_list[i]);
761  }
762  egg_poly->set_color(pmat._color);
763 
764 
765  }
766 
767  // Now that we've added all the polygons (and created all the
768  // vertices), go back through the vertex pool and set up the
769  // appropriate joint membership for each of the vertices.
770 
771  if (_options->_anim_type == MaxEggOptions::AT_model) {
772  get_vertex_weights(max_node, vpool);
773  }
774 }
775 
776 UVVert MaxToEggConverter::get_max_vertex_texcoord(Mesh *mesh, int faceNo, int vertNo, int channel) {
777 
778  // extract the texture coordinate
779  UVVert uvVert(0,0,0);
780  if(mesh->mapSupport(channel)) {
781  TVFace *pTVFace = mesh->mapFaces(channel);
782  UVVert *pUVVert = mesh->mapVerts(channel);
783  uvVert = pUVVert[pTVFace[faceNo].t[vertNo]];
784  } else if(mesh->numTVerts > 0) {
785  uvVert = mesh->tVerts[mesh->tvFace[faceNo].t[vertNo]];
786  }
787  return uvVert;
788 }
789 
790 VertColor MaxToEggConverter::get_max_vertex_color(Mesh *mesh,int FaceNo,int VertexNo, int channel) {
791 
792  VertColor vc(0,0,0);
793  if(mesh->mapSupport(channel))
794  {
795  // We get the color from vcFace
796  TVFace& _vcface = mesh->vcFace[FaceNo];
797  //Get its index into the vertCol array
798  int VertexColorIndex = _vcface.t[VertexNo];
799  //Get its color
800  vc =mesh->vertCol[VertexColorIndex];
801  }
802  else
803  {
804  TVFace *pTVFace = mesh->mapFaces(channel);
805  vc = mesh->vertCol[pTVFace[FaceNo].t[VertexNo]];
806  }
807  return vc;
808 }
809 
810 VertColor MaxToEggConverter::get_max_vertex_color(Mesh *mesh,int FaceNo,int VertexNo)
811 {
812  VertColor vc(0,0,0);
813  // We get the color from vcFace
814  TVFace& _vcface = mesh->vcFace[FaceNo];
815  //Get its index into the vertCol array
816  int VertexColorIndex = _vcface.t[VertexNo];
817  //Get its color
818  vc =mesh->vertCol[VertexColorIndex];
819  return vc;
820 }
821 
822 Point3 MaxToEggConverter::get_max_vertex_normal(Mesh *mesh, int faceNo, int vertNo)
823 {
824  Face f = mesh->faces[faceNo];
825  DWORD smGroup = f.smGroup;
826  int vert = f.getVert(vertNo);
827  RVertex *rv = mesh->getRVertPtr(vert);
828 
829  int numNormals;
830  Point3 vertexNormal;
831 
832  // Is normal specified
833  // SPCIFIED is not currently used, but may be used in future versions.
834  if (rv->rFlags & SPECIFIED_NORMAL) {
835  vertexNormal = rv->rn.getNormal();
836  }
837  // If normal is not specified it's only available if the face belongs
838  // to a smoothing group
839  else if ((numNormals = rv->rFlags & NORCT_MASK) && smGroup) {
840  // If there is only one vertex is found in the rn member.
841  if (numNormals == 1) {
842  vertexNormal = rv->rn.getNormal();
843  }
844  else {
845  // If two or more vertices are there you need to step through them
846  // and find the vertex with the same smoothing group as the current face.
847  // You will find multiple normals in the ern member.
848  for (int i = 0; i < numNormals; i++) {
849  if (rv->ern[i].getSmGroup() & smGroup) {
850  vertexNormal = rv->ern[i].getNormal();
851  }
852  }
853  }
854  }
855  else {
856  // Get the normal from the Face if no smoothing groups are there
857  vertexNormal = mesh->getFaceNormal(faceNo);
858  }
859 
860  return vertexNormal;
861 }
862 
863 ////////////////////////////////////////////////////////////////////
864 // Function: MaxToEggConverter::get_vertex_weights
865 // Access: Private
866 // Description:
867 ////////////////////////////////////////////////////////////////////
868 void MaxToEggConverter::
869 get_vertex_weights(INode *max_node, EggVertexPool *vpool) {
870  //Try to get the weights out of a physique if one exists
871  Modifier *mod = FindSkinModifier(max_node, PHYSIQUE_CLASSID);
873 
874  if (mod) {
875  // create a physique export interface
876  IPhysiqueExport *pPhysiqueExport = (IPhysiqueExport *)mod->GetInterface(I_PHYINTERFACE);
877  if (pPhysiqueExport) {
878  // create a context export interface
879  IPhyContextExport *pContextExport =
880  (IPhyContextExport *)pPhysiqueExport->GetContextInterface(max_node);
881  if (pContextExport) {
882  // set the flags in the context export interface
883  pContextExport->ConvertToRigid(TRUE);
884  pContextExport->AllowBlending(TRUE);
885 
886  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
887  EggVertex *vert = (*vi);
888  int max_vi = vert->get_external_index();
889 
890  // get the vertex export interface
891  IPhyVertexExport *pVertexExport =
892  (IPhyVertexExport *)pContextExport->GetVertexInterface(max_vi);
893  if (pVertexExport) {
894  int vertexType = pVertexExport->GetVertexType();
895 
896  // handle the specific vertex type
897  if(vertexType == RIGID_TYPE) {
898  // typecast to rigid vertex
899  IPhyRigidVertex *pTypeVertex = (IPhyRigidVertex *)pVertexExport;
900  INode *bone_node = pTypeVertex->GetNode();
901  MaxNodeDesc *joint_node_desc = _tree.find_joint(bone_node);
902  if (joint_node_desc){
903  EggGroup *joint = _tree.get_egg_group(joint_node_desc);
904  if (joint != (EggGroup *)NULL)
905  joint->ref_vertex(vert, 1.0f);
906  }
907  }
908  else if(vertexType == RIGID_BLENDED_TYPE) {
909  // typecast to blended vertex
910  IPhyBlendedRigidVertex *pTypeVertex = (IPhyBlendedRigidVertex *)pVertexExport;
911 
912  for (int ji = 0; ji < pTypeVertex->GetNumberNodes(); ++ji) {
913  PN_stdfloat weight = pTypeVertex->GetWeight(ji);
914  if (weight > 0.0f) {
915  INode *bone_node = pTypeVertex->GetNode(ji);
916  MaxNodeDesc *joint_node_desc = _tree.find_joint(bone_node);
917  if (joint_node_desc){
918  EggGroup *joint = _tree.get_egg_group(joint_node_desc);
919  if (joint != (EggGroup *)NULL)
920  joint->ref_vertex(vert, weight);
921  }
922  }
923  }
924  }
925  //Release the vertex interface
926  pContextExport->ReleaseVertexInterface(pVertexExport);
927  }
928  }
929  //Release the context interface
930  pPhysiqueExport->ReleaseContextInterface(pContextExport);
931  }
932  //Release the physique export interface
933  mod->ReleaseInterface(I_PHYINTERFACE, pPhysiqueExport);
934  }
935  }
936  else {
937  //No physique, try to find a skin
938  mod = FindSkinModifier(max_node, SKIN_CLASSID);
939  if (mod) {
940  ISkin *skin = (ISkin*)mod->GetInterface(I_SKIN);
941  if (skin) {
942  ISkinContextData *skinMC = skin->GetContextInterface(max_node);
943  if (skinMC) {
944  for (vi = vpool->begin(); vi != vpool->end(); ++vi) {
945  EggVertex *vert = (*vi);
946  int max_vi = vert->get_external_index();
947 
948  for (int ji = 0; ji < skinMC->GetNumAssignedBones(max_vi); ++ji) {
949  PN_stdfloat weight = skinMC->GetBoneWeight(max_vi, ji);
950  if (weight > 0.0f) {
951  INode *bone_node = skin->GetBone(skinMC->GetAssignedBone(max_vi, ji));
952  MaxNodeDesc *joint_node_desc = _tree.find_joint(bone_node);
953  if (joint_node_desc){
954  EggGroup *joint = _tree.get_egg_group(joint_node_desc);
955  if (joint != (EggGroup *)NULL) {
956  joint->ref_vertex(vert, weight);
957  }
958  }
959  }
960  }
961  }
962  }
963  }
964  }
965  }
966 }
967 
968 
969 ////////////////////////////////////////////////////////////////////
970 // Function: MaxToEggConverter::get_material_textures
971 // Access: Private
972 // Description: Converts a Max material into a set of Panda textures
973 // and a primitive color.
974 ////////////////////////////////////////////////////////////////////
975 const MaxToEggConverter::PandaMaterial &MaxToEggConverter::
976 get_panda_material(Mtl *mtl, MtlID matID) {
977 
978  MaterialMap::iterator it = _material_map.find(mtl);
979  if (it != _material_map.end()) {
980  return (*it).second;
981  }
982 
983  PandaMaterial &pandaMat = _material_map[mtl];
984  pandaMat._color = LColor(1,1,1,1);
985  pandaMat._any_diffuse = false;
986  pandaMat._any_opacity = false;
987  pandaMat._any_gloss = false;
988  pandaMat._any_normal = false;
989 
990 
991 
992 
993  // If it's a multi-material, dig down.
994 
995  while (( mtl != 0) && (mtl->ClassID() == Class_ID(MULTI_CLASS_ID, 0 ))) {
996  if (matID < mtl->NumSubMtls()) {
997  mtl = mtl->GetSubMtl(matID);
998  } else {
999  mtl = 0;
1000  }
1001  }
1002 
1003  // If it's a standard material, we're good.
1004 
1005  if ((mtl != 0) && (mtl->ClassID() == Class_ID(DMTL_CLASS_ID, 0 ))) {
1006  StdMat *maxMaterial = (StdMat*)mtl;
1007  analyze_diffuse_maps(pandaMat, maxMaterial->GetSubTexmap(ID_DI));
1008  analyze_opacity_maps(pandaMat, maxMaterial->GetSubTexmap(ID_OP));
1009  analyze_gloss_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SP));
1010  if (!pandaMat._any_gloss)
1011  analyze_gloss_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SS));
1012  if (!pandaMat._any_gloss)
1013  analyze_gloss_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SH));
1014  analyze_glow_maps(pandaMat, maxMaterial->GetSubTexmap(ID_SI));
1015  analyze_normal_maps(pandaMat, maxMaterial->GetSubTexmap(ID_BU));
1016  for (int i=0; i<pandaMat._texture_list.size(); i++) {
1017  EggTexture *src = pandaMat._texture_list[i];
1018  pandaMat._texture_list[i] =
1019  _textures.create_unique_texture(*src, ~EggTexture::E_tref_name);
1020  }
1021 
1022  // The existence of a texture on either color channel completely
1023  // replaces the corresponding flat color.
1024  if (!pandaMat._any_diffuse) {
1025  // Get the default diffuse color of the material without the texture map
1026  Point3 diffuseColor = Point3(maxMaterial->GetDiffuse(0));
1027  pandaMat._color[0] = diffuseColor.x;
1028  pandaMat._color[1] = diffuseColor.y;
1029  pandaMat._color[2] = diffuseColor.z;
1030  }
1031  if (!pandaMat._any_opacity) {
1032  pandaMat._color[3] = (maxMaterial->GetOpacity(_current_frame * GetTicksPerFrame()));
1033  }
1034  if (pandaMat._texture_list.size() < 1) {
1035  //if we don't have any maps whatsoever,
1036  //give the material a dummy channel
1037  //so that UV's get created
1038  pandaMat._map_channels.push_back(1);
1039  }
1040  return pandaMat;
1041  }
1042 
1043  // Otherwise, it's unrecognizable. Leave result blank.
1044  return pandaMat;
1045 }
1046 ////////////////////////////////////////////////////////////////////
1047 // Function: MayaShader::analyze_diffuse_maps
1048 // Access: Private
1049 // Description:
1050 ////////////////////////////////////////////////////////////////////
1051 void MaxToEggConverter::analyze_diffuse_maps(PandaMaterial &pandaMat, Texmap *mat) {
1052  if (mat == 0) return;
1053 
1054  if (mat->ClassID() == Class_ID(RGBMULT_CLASS_ID, 0)) {
1055  for (int i=0; i<mat->NumSubTexmaps(); i++) {
1056  analyze_diffuse_maps(pandaMat, mat->GetSubTexmap(i));
1057  }
1058  return;
1059  }
1060 
1061  if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
1062  pandaMat._any_diffuse = true;
1063  PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
1064 
1065  BitmapTex *diffuseTex = (BitmapTex *)mat;
1066 
1067  Filename fullpath, outpath;
1068 #ifdef _UNICODE
1069  Filename filename = Filename::from_os_specific_w(diffuseTex->GetMapName());
1070 #else
1071  Filename filename = Filename::from_os_specific(diffuseTex->GetMapName());
1072 #endif
1073  _options->_path_replace->full_convert_path(filename, get_model_path(),
1074  fullpath, outpath);
1075  tex->set_filename(outpath);
1076  tex->set_fullpath(fullpath);
1077 
1078  apply_texture_properties(*tex, diffuseTex->GetMapChannel());
1079  add_map_channel(pandaMat, diffuseTex->GetMapChannel());
1080 
1081  Bitmap *diffuseBitmap = diffuseTex->GetBitmap(0);
1082  if ( diffuseBitmap && diffuseBitmap->HasAlpha()) {
1083  tex->set_format(EggTexture::F_rgba);
1084  } else {
1085  tex->set_format(EggTexture::F_rgb);
1086  }
1087  tex->set_env_type(EggTexture::ET_modulate);
1088 
1089  pandaMat._texture_list.push_back(tex);
1090  }
1091 }
1092 
1093 ////////////////////////////////////////////////////////////////////
1094 // Function: MayaShader::analyze_opacity_maps
1095 // Access: Private
1096 // Description:
1097 ////////////////////////////////////////////////////////////////////
1098 void MaxToEggConverter::analyze_opacity_maps(PandaMaterial &pandaMat, Texmap *mat) {
1099  if (mat == 0) return;
1100 
1101  if (mat->ClassID() == Class_ID(RGBMULT_CLASS_ID, 0)) {
1102  for (int i=0; i<mat->NumSubTexmaps(); i++) {
1103  analyze_opacity_maps(pandaMat, mat->GetSubTexmap(i));
1104  }
1105  return;
1106  }
1107 
1108  if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
1109  pandaMat._any_opacity = true;
1110  BitmapTex *transTex = (BitmapTex *)mat;
1111 
1112  Filename fullpath, outpath;
1113 #ifdef _UNICODE
1114  Filename filename = Filename::from_os_specific_w(transTex->GetMapName());
1115 #else
1116  Filename filename = Filename::from_os_specific(transTex->GetMapName());
1117 #endif
1118  _options->_path_replace->full_convert_path(filename, get_model_path(),
1119  fullpath, outpath);
1120 
1121  // See if this opacity map already showed up.
1122  for (int i=0; i<pandaMat._texture_list.size(); i++) {
1123  EggTexture *tex = pandaMat._texture_list[i];
1124  if ((tex->get_env_type()==EggTexture::ET_modulate)&&(tex->get_fullpath() == fullpath)) {
1125  tex->set_format(EggTexture::F_rgba);
1126  return;
1127  }
1128  }
1129 
1130  // Try to find a diffuse map to pair this with as an alpha-texture.
1131  std::string uvname = get_uv_name(transTex->GetMapChannel());
1132  for (int i=0; i<pandaMat._texture_list.size(); i++) {
1133  EggTexture *tex = pandaMat._texture_list[i];
1134  if ((tex->get_env_type()==EggTexture::ET_modulate)&&
1135  (tex->get_format() == EggTexture::F_rgb)&&
1136  (tex->get_uv_name() == uvname)) {
1137  tex->set_format(EggTexture::F_rgba);
1138  tex->set_alpha_filename(outpath);
1139  tex->set_alpha_fullpath(fullpath);
1140  return;
1141  }
1142  }
1143 
1144  // Otherwise, just create it as an alpha-texture.
1145  PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
1146  tex->set_filename(outpath);
1147  tex->set_fullpath(fullpath);
1148 
1149  apply_texture_properties(*tex, transTex->GetMapChannel());
1150  add_map_channel(pandaMat, transTex->GetMapChannel());
1151  tex->set_format(EggTexture::F_alpha);
1152 
1153  pandaMat._texture_list.push_back(tex);
1154  }
1155 }
1156 
1157 ////////////////////////////////////////////////////////////////////
1158 // Function: MayaShader::analyze_glow_maps
1159 // Access: Private
1160 // Description:
1161 ////////////////////////////////////////////////////////////////////
1162 void MaxToEggConverter::analyze_glow_maps(PandaMaterial &pandaMat, Texmap *mat) {
1163  if (mat == 0) return;
1164 
1165  if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
1166  BitmapTex *gtex = (BitmapTex *)mat;
1167 
1168  Filename fullpath, outpath;
1169 #ifdef _UNICODE
1170  Filename filename = Filename::from_os_specific_w(gtex->GetMapName());
1171 #else
1172  Filename filename = Filename::from_os_specific(gtex->GetMapName());
1173 #endif
1174  _options->_path_replace->full_convert_path(filename, get_model_path(),
1175  fullpath, outpath);
1176 
1177  // Try to find a diffuse map to pair this with as an alpha-texture.
1178  std::string uvname = get_uv_name(gtex->GetMapChannel());
1179  for (int i=0; i<pandaMat._texture_list.size(); i++) {
1180  EggTexture *tex = pandaMat._texture_list[i];
1181  if ((tex->get_env_type()==EggTexture::ET_modulate)&&
1182  (tex->get_format() == EggTexture::F_rgb)&&
1183  (tex->get_uv_name() == uvname)) {
1184  tex->set_env_type(EggTexture::ET_modulate_glow);
1185  tex->set_format(EggTexture::F_rgba);
1186  tex->set_alpha_filename(outpath);
1187  tex->set_alpha_fullpath(fullpath);
1188  return;
1189  }
1190  }
1191 
1192  // Otherwise, just create it as a separate glow-texture.
1193  PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
1194  tex->set_env_type(EggTexture::ET_glow);
1195  tex->set_filename(outpath);
1196  tex->set_fullpath(fullpath);
1197  apply_texture_properties(*tex, gtex->GetMapChannel());
1198  add_map_channel(pandaMat, gtex->GetMapChannel());
1199  tex->set_format(EggTexture::F_alpha);
1200 
1201  pandaMat._texture_list.push_back(tex);
1202  }
1203 }
1204 
1205 ////////////////////////////////////////////////////////////////////
1206 // Function: MayaShader::analyze_gloss_maps
1207 // Access: Private
1208 // Description:
1209 ////////////////////////////////////////////////////////////////////
1210 void MaxToEggConverter::analyze_gloss_maps(PandaMaterial &pandaMat, Texmap *mat) {
1211  if (mat == 0) return;
1212 
1213  if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
1214  pandaMat._any_gloss = true;
1215  BitmapTex *gtex = (BitmapTex *)mat;
1216 
1217  Filename fullpath, outpath;
1218 #ifdef _UNICODE
1219  Filename filename = Filename::from_os_specific_w(gtex->GetMapName());
1220 #else
1221  Filename filename = Filename::from_os_specific(gtex->GetMapName());
1222 #endif
1223  _options->_path_replace->full_convert_path(filename, get_model_path(),
1224  fullpath, outpath);
1225 
1226  // Try to find a diffuse map to pair this with as an alpha-texture.
1227  std::string uvname = get_uv_name(gtex->GetMapChannel());
1228  for (int i=0; i<pandaMat._texture_list.size(); i++) {
1229  EggTexture *tex = pandaMat._texture_list[i];
1230  if ((tex->get_env_type()==EggTexture::ET_modulate)&&
1231  (tex->get_format() == EggTexture::F_rgb)&&
1232  (tex->get_uv_name() == uvname)) {
1233  tex->set_env_type(EggTexture::ET_modulate_gloss);
1234  tex->set_format(EggTexture::F_rgba);
1235  tex->set_alpha_filename(outpath);
1236  tex->set_alpha_fullpath(fullpath);
1237  return;
1238  }
1239  }
1240 
1241  // Otherwise, just create it as a separate gloss-texture.
1242  PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
1243  tex->set_env_type(EggTexture::ET_gloss);
1244  tex->set_filename(outpath);
1245  tex->set_fullpath(fullpath);
1246  apply_texture_properties(*tex, gtex->GetMapChannel());
1247  add_map_channel(pandaMat, gtex->GetMapChannel());
1248  tex->set_format(EggTexture::F_alpha);
1249 
1250  pandaMat._texture_list.push_back(tex);
1251  }
1252 }
1253 
1254 ////////////////////////////////////////////////////////////////////
1255 // Function: MayaShader::analyze_normal_maps
1256 // Access: Private
1257 // Description:
1258 ////////////////////////////////////////////////////////////////////
1259 void MaxToEggConverter::analyze_normal_maps(PandaMaterial &pandaMat, Texmap *mat) {
1260  if (mat == 0) return;
1261 
1262  if (mat->ClassID() == Class_ID(BMTEX_CLASS_ID, 0)) {
1263  pandaMat._any_normal = true;
1264  BitmapTex *ntex = (BitmapTex *)mat;
1265 
1266  Filename fullpath, outpath;
1267 #ifdef _UNICODE
1268  Filename filename = Filename::from_os_specific_w(ntex->GetMapName());
1269 #else
1270  Filename filename = Filename::from_os_specific(ntex->GetMapName());
1271 #endif
1272  _options->_path_replace->full_convert_path(filename, get_model_path(),
1273  fullpath, outpath);
1274 
1275  PT(EggTexture) tex = new EggTexture(generate_tex_name(), "");
1276  tex->set_env_type(EggTexture::ET_normal);
1277  tex->set_filename(outpath);
1278  tex->set_fullpath(fullpath);
1279  apply_texture_properties(*tex, ntex->GetMapChannel());
1280  add_map_channel(pandaMat, ntex->GetMapChannel());
1281  tex->set_format(EggTexture::F_rgb);
1282 
1283  pandaMat._texture_list.push_back(tex);
1284  }
1285 }
1286 
1287 ////////////////////////////////////////////////////////////////////
1288 // Function: MayaShader::add_map_channel
1289 // Access: Private
1290 // Description: Adds the specified map channel to the map channel
1291 // list, if it's not already there.
1292 ////////////////////////////////////////////////////////////////////
1293 void MaxToEggConverter::add_map_channel(PandaMaterial &pandaMat, int chan) {
1294  for (int i=0; i<pandaMat._map_channels.size(); i++) {
1295  if (pandaMat._map_channels[i] == chan) {
1296  return;
1297  }
1298  }
1299  pandaMat._map_channels.push_back(chan);
1300 }
1301 
1302 ////////////////////////////////////////////////////////////////////
1303 // Function: MayaShader::generate_tex_name
1304 // Access: Private
1305 // Description: Generates an arbitrary unused texture name.
1306 ////////////////////////////////////////////////////////////////////
1307 std::string MaxToEggConverter::generate_tex_name() {
1308  ostringstream name_strm;
1309  name_strm << "Tex" << ++_cur_tref;
1310  return name_strm.str();
1311 }
1312 
1313 ////////////////////////////////////////////////////////////////////
1314 // Function: MayaShader::get_uv_name
1315 // Access: Private
1316 // Description: Returns the UV-name of the nth map-channel.
1317 ////////////////////////////////////////////////////////////////////
1318 std::string MaxToEggConverter::get_uv_name(int channel) {
1319  ostringstream uvname;
1320  uvname << "m" << channel;
1321  return uvname.str();
1322 }
1323 
1324 ////////////////////////////////////////////////////////////////////
1325 // Function: MayaShader::apply_texture_properties
1326 // Access: Private
1327 // Description: Applies all the appropriate texture properties to the
1328 // EggTexture object, including wrap modes and texture
1329 // matrix.
1330 ////////////////////////////////////////////////////////////////////
1331 void MaxToEggConverter::
1332 apply_texture_properties(EggTexture &tex, int channel) {
1333 
1334  // we leave a channel 1 for texture swapping, so don't name it
1335  if(channel == 1)
1336  tex.set_uv_name("");
1337  else
1338  tex.set_uv_name(get_uv_name(channel));
1339 
1340  tex.set_minfilter(EggTexture::FT_linear_mipmap_linear);
1341  tex.set_magfilter(EggTexture::FT_linear);
1342 
1343  EggTexture::WrapMode wrap_u = EggTexture::WM_repeat;
1344  EggTexture::WrapMode wrap_v = EggTexture::WM_repeat;
1345 
1346  tex.set_wrap_u(wrap_u);
1347  tex.set_wrap_v(wrap_v);
1348 }
1349 
1350 
1351 ////////////////////////////////////////////////////////////////////
1352 // Function: MayaShader::reparent_decals
1353 // Access: Private
1354 // Description: Recursively walks the egg hierarchy, reparenting
1355 // "decal" type nodes below their corresponding
1356 // "decalbase" type nodes, and setting the flags.
1357 //
1358 // Returns true on success, false if some nodes were
1359 // incorrect.
1360 ////////////////////////////////////////////////////////////////////
1361 bool MaxToEggConverter::
1362 reparent_decals(EggGroupNode *egg_parent) {
1363  bool okflag = true;
1364 
1365  // First, walk through all children of this node, looking for the
1366  // one decal base, if any.
1367  EggGroup *decal_base = (EggGroup *)NULL;
1368  pvector<EggGroup *> decal_children;
1369 
1370  EggGroupNode::iterator ci;
1371  for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
1372  EggNode *child = (*ci);
1373  if (child->is_of_type(EggGroup::get_class_type())) {
1374  EggGroup *child_group = (EggGroup *) child;
1375  if (child_group->has_object_type("decalbase")) {
1376  if (decal_base != (EggNode *)NULL) {
1377  // error
1378  okflag = false;
1379  }
1380  child_group->remove_object_type("decalbase");
1381  decal_base = child_group;
1382 
1383  } else if (child_group->has_object_type("decal")) {
1384  child_group->remove_object_type("decal");
1385  decal_children.push_back(child_group);
1386  }
1387  }
1388  }
1389 
1390  if (decal_base == (EggGroup *)NULL) {
1391  if (!decal_children.empty()) {
1392  // warning
1393  }
1394 
1395  } else {
1396  if (decal_children.empty()) {
1397  // warning
1398 
1399  } else {
1400  // All the decal children get moved to be a child of decal base.
1401  // This usually will not affect the vertex positions, but it
1402  // could if the decal base has a transform and the decal child
1403  // is an instance node. So don't do that.
1405  for (di = decal_children.begin(); di != decal_children.end(); ++di) {
1406  EggGroup *child_group = (*di);
1407  decal_base->add_child(child_group);
1408  }
1409 
1410  // Also set the decal state on the base.
1411  decal_base->set_decal_flag(true);
1412  }
1413  }
1414 
1415  // Now recurse on each of the child nodes.
1416  for (ci = egg_parent->begin(); ci != egg_parent->end(); ++ci) {
1417  EggNode *child = (*ci);
1418  if (child->is_of_type(EggGroupNode::get_class_type())) {
1419  EggGroupNode *child_group = (EggGroupNode *) child;
1420  if (!reparent_decals(child_group)) {
1421  okflag = false;
1422  }
1423  }
1424  }
1425 
1426  return okflag;
1427 }
1428 
1429 Modifier* MaxToEggConverter::FindSkinModifier (INode* node, const Class_ID &type)
1430 {
1431  // Get object from node. Abort if no object.
1432  Object* pObj = node->GetObjectRef();
1433  if (!pObj) return NULL;
1434 
1435  // Is derived object ?
1436  while (pObj->SuperClassID() == GEN_DERIVOB_CLASS_ID) {
1437  // Yes -> Cast.
1438  IDerivedObject* pDerObj = static_cast<IDerivedObject*>(pObj);
1439 
1440  // Iterate over all entries of the modifier stack.
1441  for (int stackId = 0; stackId < pDerObj->NumModifiers(); ++stackId) {
1442  // Get current modifier.
1443  Modifier* mod = pDerObj->GetModifier(stackId);
1444 
1445  // Is this what we are looking for?
1446  if (mod->ClassID() == type )
1447  return mod;
1448  }
1449 
1450  // continue with next derived object
1451  pObj = pDerObj->GetObjRef();
1452  }
1453 
1454  // Not found.
1455  return NULL;
1456 }
void setup(int order, int num_knots)
Prepares a new curve definition with the indicated order and number of knots.
bool convert(MaxEggOptions *options)
Fills up the egg_data structure according to the global Max model data.
This is an iterator adaptor that converts any iterator that returns a pair (e.g.
EggGroup * get_egg_group(MaxNodeDesc *node_desc)
Returns the EggGroupNode corresponding to the group or joint for the indicated node.
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...
bool add_data(const LMatrix4d &mat)
Adds a new matrix to the table, by adding a new row to each of the subtables.
This is a 4-by-4 transform matrix.
Definition: lmatrix.h:4716
void add_texture(EggTexture *texture)
Applies the indicated texture to the primitive.
Definition: eggPrimitive.I:197
void set_pos(double pos)
Sets the vertex position.
Definition: eggVertex.I:54
bool is_joint() const
Returns true if the node should be treated as a joint by the converter.
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
EggXfmSAnim * get_egg_anim(MaxNodeDesc *node_desc)
Returns the anim table corresponding to the joint for the indicated node.
Defines a texture map that may be applied to geometry.
Definition: eggTexture.h:33
void set_vertex(int index, EggVertex *vertex)
Replaces a particular vertex based on its index number in the list of vertices.
Definition: eggPrimitive.I:455
bool is_of_type(TypeHandle handle) const
Returns true if the current object is or derives from the indicated type.
Definition: typedObject.I:63
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 is_node_joint() const
Returns true if the node is the parent or ancestor of a joint.
MaxNodeDesc * get_node(int n) const
Returns the nth node in the hierarchy, in an arbitrary ordering.
void clear()
Removes all textures from the collection.
Definition: shader.h:50
bool build_complete_hierarchy(INode *root, ULONG *selection_list, int len)
Walks through the complete Max hierarchy and builds up the corresponding tree.
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
MaxNodeDesc * find_joint(INode *max_node)
The recursive implementation of build_node().
This is a two-component point in space.
Definition: lpoint2.h:411
INode * get_max_node() const
Returns the INode associated with this node.
string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:424
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
This is the primary interface into all the egg data, and the root of the egg file structure...
Definition: eggData.h:41
void set_knot(int k, double value)
Resets the value of the indicated knot as indicated.
Definition: eggNurbsCurve.I:75
int get_external_index() const
Returns the number set by set_external_index().
Definition: eggVertex.I:376
void set_coordinate_system(CoordinateSystem coordsys)
Changes the coordinate system of the EggData.
Definition: eggData.cxx:279
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
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
const string & get_uv_name() const
Returns the texcoord name that has been specified for this texture, or the empty string if no texcoor...
Definition: eggTexture.I:705
void clear_egg(EggData *egg_data, EggGroupNode *egg_root, EggGroupNode *skeleton_node)
Removes all of the references to generated egg structures from the tree, and prepares the tree for ge...
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
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
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
const Filename & get_fullpath() const
Returns the full pathname to the file, if it is known; otherwise, returns the same thing as get_filen...
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
Describes a single instance of a node in the Max scene graph, relating it to the corresponding egg st...
Definition: maxNodeDesc.h:26
int get_num_nodes() const
Returns the total number of nodes in the hierarchy, not counting the root node.
A parametric NURBS curve.
Definition: eggNurbsCurve.h:28
void optimize()
Optimizes the table by collapsing redundant sub-tables.
Definition: eggXfmSAnim.cxx:75
bool has_max_node() const
Returns true if a Max INode has been associated with this node, false otherwise.
A single polygon.
Definition: eggPolygon.h:26
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
This corresponds to a.
Definition: eggTable.h:31
This is the base class for all three-component vectors and points.
Definition: lvecBase4.h:111
This is a three-component vector distance (as opposed to a three-component point, which represents a ...
Definition: lvector3.h:746
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.
This class supervises the construction of an EggData structure from a Max model.
A base class for things that may be directly added into the egg hierarchy.
Definition: eggNode.h:38
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
static Filename from_os_specific_w(const wstring &os_specific, Type type=T_general)
The wide-string variant of from_os_specific().
Definition: filename.cxx:401
EggVertex * get_vertex(int index) const
Returns a particular index based on its index number.
Definition: eggPrimitive.I:466
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
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...
const LMatrix4d & get_node_frame_inv() const
Returns the inverse of the matrix returned by get_node_frame().
Definition: eggNode.I:183
iterator begin() const
Returns an iterator that can be used to traverse through all the vertices in the pool.
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