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