1 #define NOMINMAX
2 #include <maya/MAnimControl.h>
3 #include <maya/MDagPath.h>
4 #include <maya/MDagPathArray.h>
5 #include <maya/MDistance.h>
6 #include <maya/MEulerRotation.h>
7 #include <maya/MFnEnumAttribute.h>
8 #include <maya/MFnIkJoint.h>
9 #include <maya/MFnMesh.h>
10 #include <maya/MFnSet.h>
11 #include <maya/MFnSingleIndexedComponent.h>
12 #include <maya/MFnSkinCluster.h>
13 #include <maya/MFnTransform.h>
14 #include <maya/MGlobal.h>
15 #include <maya/MIntArray.h>
16 #include <maya/MItDag.h>
17 #include <maya/MItDependencyNodes.h>
18 #include <maya/MItMeshEdge.h>
19 #include <maya/MItMeshPolygon.h>
20 #include <maya/MItMeshVertex.h>
21 #include <maya/MItSelectionList.h>
22 #include <maya/MPlug.h>
23 #include <maya/MPlugArray.h>
24 #include <maya/MPointArray.h>
25 #include <maya/MSelectionList.h>
26 #include "maya_export_tools.h"
27 #include "xr_object.h"
28 #include "xr_skl_motion.h"
29 #include "xr_envelope.h"
30 #include "xr_utils.h"
31
32 using namespace xray_re;
33
extract_bones(MFnSkinCluster & skin_fn,xr_bone_vec & bones)34 static MStatus extract_bones(MFnSkinCluster& skin_fn, xr_bone_vec& bones)
35 {
36 MStatus status;
37
38 MDagPathArray joints;
39 skin_fn.influenceObjects(joints, &status);
40 unsigned num_joints = joints.length();
41 if (num_joints == 0) {
42 msg("can't find any influence object");
43 return MS::kInvalidParameter;
44 } else if (num_joints > MAX_BONES) {
45 msg("too many bones (%u > %u)", num_joints, MAX_BONES);
46 }
47
48 bones.resize(num_joints);
49
50 MString command("dagPose -r -g -bp ");
51 for (unsigned i = num_joints; i != 0;) {
52 MFnIkJoint joint_fn(joints[--i], &status);
53 if (!status) {
54 msg("can't handle non-joint node %s",
55 joints[i].partialPathName().asChar());
56 return status;
57 }
58 command += joint_fn.partialPathName();
59 command += " ";
60
61 xr_bone* bone = new xr_bone;
62 bones[i] = bone;
63 const char* name = joint_fn.name().asChar();
64 bone->vmap_name() = bone->name() = name;
65
66 unsigned num_parents = joint_fn.parentCount();
67 if (num_parents > 1) {
68 msg("can't handle multi-parented joint %s", name);
69 return MS::kInvalidParameter;
70 } else if (num_parents == 1) {
71 MObject parent_obj = joint_fn.parent(0);
72 if (parent_obj.hasFn(MFn::kJoint) &&
73 joint_fn.setObject(parent_obj)) {
74 bone->parent_name() = joint_fn.name().asChar();
75 }
76 }
77 }
78
79 xr_bone* root = 0;
80 for (xr_bone_vec_it it = bones.begin(), end = bones.end(); it != end; ++it) {
81 xr_bone* bone = *it;
82 xr_assert(bone);
83 if (bone->parent_name().empty()) {
84 if (root == 0) {
85 root = bone;
86 continue;
87 } else {
88 msg("can't handle multiple root joints in skeleton");
89 return MS::kInvalidParameter;
90 }
91 }
92 xr_bone* parent = find_by_name(bones, bone->parent_name());
93 if (parent == 0) {
94 msg("can't find parent bone %s", bone->parent_name().c_str());
95 return MS::kFailure;
96 }
97 parent->children().push_back(bone);
98 }
99 if (root == 0) {
100 msg("can't find root joint");
101 return MS::kInvalidParameter;
102 }
103
104 if (!(status = MGlobal::executeCommand(command))) {
105 msg("can't set skeleton to bind pose");
106 return MS::kFailure;
107 }
108
109 for (unsigned i = num_joints; i != 0;) {
110 MFnIkJoint joint_fn(joints[--i]);
111 xr_bone* bone = bones[i];
112
113 MVector t = joint_fn.getTranslation(MSpace::kTransform, &status);
114 CHECK_MSTATUS(status);
115 bone->bind_offset().set(float(MDistance(t.x, MDistance::kCentimeters).asMeters()),
116 float(MDistance(t.y, MDistance::kCentimeters).asMeters()),
117 float(MDistance(-t.z, MDistance::kCentimeters).asMeters()));
118
119 MEulerRotation r;
120 status = joint_fn.getRotation(r);
121 CHECK_MSTATUS(status);
122 r.reorderIt(MEulerRotation::kZXY);
123 bone->bind_rotate().set(float(-r.x), float(-r.y), float(r.z));
124 }
125
126 return status;
127 }
128
extract_points(MFnMesh & mesh_fn,std::vector<fvector3> & points,fbox & aabb)129 static MStatus extract_points(MFnMesh& mesh_fn, std::vector<fvector3>& points, fbox& aabb)
130 {
131 MStatus status;
132
133 int num_points = mesh_fn.numVertices();
134 if (num_points < 4) {
135 msg("can't export mesh %s with less than four vertices",
136 mesh_fn.name().asChar());
137 return MS::kInvalidParameter;
138 }
139 points.reserve(size_t(num_points & INT_MAX));
140 aabb.invalidate();
141 MPoint p0;
142 fvector3 p;
143 for (int i = 0; i != num_points; ++i) {
144 mesh_fn.getPoint(i, p0);
145 p0.cartesianize();
146 aabb.extend(p.set(float(MDistance(p0.x, MDistance::kCentimeters).asMeters()),
147 float(MDistance(p0.y, MDistance::kCentimeters).asMeters()),
148 float(MDistance(-p0.z, MDistance::kCentimeters).asMeters())));
149 points.push_back(p);
150 }
151 return status;
152 }
153
extract_faces(MFnMesh & mesh_fn,lw_face_vec & faces)154 static MStatus extract_faces(MFnMesh& mesh_fn, lw_face_vec& faces)
155 {
156 MStatus status;
157
158 // FIXME: it would be nice to support automatic triangulation using getTriangles() etc.
159 int num_polys = mesh_fn.numPolygons(&status);
160 if (num_polys < 2) {
161 msg("can't export mesh %s with less than two faces",
162 mesh_fn.name().asChar());
163 return MS::kInvalidParameter;
164 }
165 faces.reserve(size_t(num_polys & INT_MAX));
166 MIntArray verts;
167 for (int i = 0; i != num_polys; ++i) {
168 status = mesh_fn.getPolygonVertices(i, verts);
169 if (!status || verts.length() != 3) {
170 msg("can't handle polygons with 4 or more sides");
171 return MS::kInvalidParameter;
172 }
173 lw_face face(verts[2], verts[1], verts[0]);
174 faces.push_back(face);
175 }
176 return status;
177 }
178
extract_uvs(MFnMesh & mesh_fn,lw_face_vec & faces,lw_vmref_vec & vmrefs,xr_vmap_vec & vmaps)179 static MStatus extract_uvs(MFnMesh& mesh_fn, lw_face_vec& faces,
180 lw_vmref_vec& vmrefs, xr_vmap_vec& vmaps)
181 {
182 MStatus status;
183
184 xr_uv_vmap* uv_vmap = 0;
185 xr_face_uv_vmap* face_uv_vmap = 0;
186
187 for (MItMeshVertex it(mesh_fn.object()); !it.isDone(); it.next()) {
188 uint32_t vert_idx = uint32_t(it.index() & INT_MAX);
189
190 fvector2 uv0;
191 if (!it.getUV(uv0.xy)) {
192 msg("can't extract shared UVs for vert %"PRIu32, vert_idx);
193 return MS::kInvalidParameter;
194 }
195 uv0.v = 1.f - uv0.v;
196
197 if (uv_vmap == 0) {
198 uv_vmap = new xr_uv_vmap("Texture");
199 uv_vmap->reserve(size_t(mesh_fn.numVertices() & INT_MAX));
200 vmaps.push_back(uv_vmap);
201 }
202 lw_vmref vmref0;
203 vmref0.push_back(lw_vmref_entry(0, uv_vmap->add_uv(uv0, vert_idx)));
204
205 uint32_t vmref0_idx = uint32_t(vmrefs.size() & UINT32_MAX);
206 vmrefs.push_back(vmref0);
207
208 MIntArray adjacents;
209 it.getConnectedFaces(adjacents);
210 for (unsigned i = adjacents.length(); i != 0;) {
211 uint32_t face_idx = uint32_t(adjacents[--i] & INT_MAX), vmref_idx;
212
213 fvector2 uv;
214 if (!it.getUV(face_idx, uv.xy)) {
215 msg("can't extract UVs for vert %" PRIu32 " face %"PRIu32, vert_idx, face_idx);
216 uv = uv0;
217 }
218 uv.v = 1.f - uv.v;
219
220 lw_face& face = faces[face_idx];
221 if (uv == uv0) {
222 vmref_idx = vmref0_idx;
223 } else {
224 if (face_uv_vmap == 0) {
225 face_uv_vmap = new xr_face_uv_vmap("Texture");
226 vmaps.push_back(face_uv_vmap);
227 }
228 lw_vmref vmref;
229 vmref.push_back(lw_vmref_entry(1, face_uv_vmap->add_uv(uv, vert_idx, face_idx)));
230 vmref_idx = uint32_t(vmrefs.size() & UINT32_MAX);
231 vmrefs.push_back(vmref);
232 }
233 for (uint_fast32_t j = 3; j != 0;) {
234 if (face.v[--j] == vert_idx) {
235 face.ref[j] = vmref_idx;
236 vmref_idx = UINT32_MAX;
237 break;
238 }
239 }
240 xr_assert(vmref_idx == UINT32_MAX);
241 }
242 }
243 return status;
244 }
245
extract_weights(MFnMesh & mesh_fn,MFnSkinCluster & skin_fn,lw_face_vec & faces,lw_vmref_vec & vmrefs,xr_vmap_vec & vmaps)246 static MStatus extract_weights(MFnMesh& mesh_fn, MFnSkinCluster& skin_fn,
247 lw_face_vec& faces, lw_vmref_vec& vmrefs, xr_vmap_vec& vmaps)
248 {
249 MStatus status;
250
251 MDagPathArray joints;
252 skin_fn.influenceObjects(joints, &status);
253 CHECK_MSTATUS(status);
254
255 // collect bone weights in vmaps and build vertex-ordered refs
256 lw_vmref_vec weight_vmrefs(size_t(mesh_fn.numVertices() & INT_MAX));
257 for (unsigned joint_idx = joints.length(); joint_idx != 0;) {
258 MDoubleArray weights;
259 MSelectionList affected;
260 status = skin_fn.getPointsAffectedByInfluence(joints[--joint_idx], affected, weights);
261 CHECK_MSTATUS(status);
262 if (affected.isEmpty())
263 continue;
264
265 MFnIkJoint joint_fn(joints[joint_idx], &status);
266 CHECK_MSTATUS(status);
267
268 msg("joint=%s", joint_fn.name().asChar());
269
270 xr_weight_vmap* weight_vmap = new xr_weight_vmap(joint_fn.name().asChar());
271 weight_vmap->reserve(weights.length());
272 uint32_t vmap_idx = uint32_t(vmaps.size() & UINT32_MAX);
273 vmaps.push_back(weight_vmap);
274
275 msg(" num_affected=%u, num_weights=%u", affected.length(), weights.length());
276
277 // FIXME: is it enough to expect the single element in the list here?
278 for (unsigned i = affected.length(), k = weights.length(); i != 0;) {
279 MDagPath dag_path;
280 MObject component_obj;
281 status = affected.getDagPath(--i, dag_path, component_obj);
282 CHECK_MSTATUS(status);
283 MFnSingleIndexedComponent component_fn(component_obj, &status);
284 CHECK_MSTATUS(status);
285 for (int j = component_fn.elementCount() - 1; j >= 0; --j) {
286 int vert_idx = component_fn.element(j, &status);
287 CHECK_MSTATUS(status);
288 uint32_t weight_idx = weight_vmap->add_weight(float(weights[--k]), uint32_t(vert_idx & INT_MAX));
289 xr_assert(!weight_vmrefs[vert_idx].full());
290 weight_vmrefs[vert_idx].push_back(lw_vmref_entry(vmap_idx, weight_idx));
291 }
292 }
293 }
294
295 // merge weight vmrefs with the already extracted uv vmrefs
296 for (lw_face_vec_it it = faces.begin(), end = faces.end(); it != end; ++it) {
297 for (uint_fast32_t i = 3; i != 0;) {
298 lw_vmref& vmref = vmrefs[it->ref[--i]];
299 if (vmref.size() > 1)
300 continue;
301 vmref.append(weight_vmrefs[it->v[i]]);
302 }
303 }
304
305 return status;
306 }
307
get_xraymtl_attr(MFnDependencyNode & dep_fn,const char * name,std::string & value)308 static void get_xraymtl_attr(MFnDependencyNode& dep_fn, const char* name, std::string& value)
309 {
310 MStatus status;
311 MPlug plug = dep_fn.findPlug(name, &status);
312 if (status) {
313 MFnEnumAttribute attr_fn(plug.attribute(&status), &status);
314 if (status) {
315 MString temp = attr_fn.fieldName(plug.asShort(), &status);
316 if (status) {
317 value = temp.asChar();
318 for (std::string::size_type i = 0; (i = value.find('/', i)) != std::string::npos; ++i)
319 value[i] = '\\';
320 return;
321 }
322 }
323 }
324 msg("can't get attribute %s", name);
325 }
326
create_surface(const char * surf_name,MFnSet & set_fn)327 xr_surface* maya_export_tools::create_surface(const char* surf_name, MFnSet& set_fn)
328 {
329 xr_surface* surface = new xr_surface(m_skeletal);
330 surface->name() = surf_name;
331
332 MStatus status;
333 MPlugArray connected_plugs;
334 set_fn.findPlug("ss").connectedTo(connected_plugs, true, false, &status);
335 MObject shader_obj;
336 for (unsigned i = connected_plugs.length(); i != 0;) {
337 MObject obj = connected_plugs[--i].node();
338 MFnDependencyNode dep_fn(obj);
339 if (dep_fn.typeName() == "XRayMtl") {
340 shader_obj = obj;
341 get_xraymtl_attr(dep_fn, "xrayGameMaterial", surface->gamemtl());
342 get_xraymtl_attr(dep_fn, "xrayEngineShader", surface->eshader());
343 get_xraymtl_attr(dep_fn, "xrayCompilerShader", surface->cshader());
344 MPlug plug = dep_fn.findPlug("xrayDoubleSide", &status);
345 if (status && plug.asBool())
346 surface->set_two_sided();
347 break;
348 } else if (obj.hasFn(MFn::kLambert) || obj.hasFn(MFn::kPhong) || obj.hasFn(MFn::kBlinn)) {
349 if (shader_obj.isNull())
350 shader_obj = obj;
351 }
352 }
353 if (shader_obj.isNull()) {
354 msg("can't find shader node for surface %s", surf_name);
355 return surface;
356 }
357
358 MFnDependencyNode shader_fn(shader_obj);
359 shader_fn.findPlug("c").connectedTo(connected_plugs, true, false);
360 for (unsigned i = connected_plugs.length(); i != 0;) {
361 MFnDependencyNode dep_fn(connected_plugs[--i].node());
362 if (dep_fn.typeName() == "file") {
363 MString file_path = dep_fn.findPlug("ftn").asString();
364 if (file_path.numChars()) {
365 int i, j;
366 if ((i = file_path.rindexW('/')) < 0)
367 i = file_path.rindexW('\\');
368 if ((j = file_path.rindexW('.')) < 0)
369 j = file_path.numChars();
370 MString name(file_path.substringW(i + 1, j - 1));
371 if ((i = name.indexW('_')) > 0) {
372 surface->texture() = (name.substringW(0, i - 1) + "\\" + name).asChar();
373 } else {
374 surface->texture() = name.asChar();
375 }
376 }
377 break;
378 }
379 }
380 return surface;
381 }
382
extract_surfaces(MFnMesh & mesh_fn,xr_surfmap_vec & surfmaps)383 MStatus maya_export_tools::extract_surfaces(MFnMesh& mesh_fn, xr_surfmap_vec& surfmaps)
384 {
385 MObjectArray shading_groups;
386 MIntArray faces;
387 MStatus status = mesh_fn.getConnectedShaders(0, shading_groups, faces);
388 if (!status) {
389 msg("can't get connected shaders for mesh %s", mesh_fn.name().asChar());
390 return MS::kInvalidParameter;
391 }
392 surfmaps.resize(shading_groups.length());
393 for (unsigned i = faces.length(); i != 0;) {
394 xr_surfmap* smap = surfmaps[faces[--i]];
395 if (smap == 0) {
396 MFnSet set_fn(shading_groups[faces[i]], &status);
397 CHECK_MSTATUS(status);
398 const char* surf_name = set_fn.name().asChar();
399 xr_surface*& surface = m_shared_surfaces[surf_name];
400 if (surface == 0) {
401 smap = new xr_surfmap(create_surface(surf_name, set_fn));
402 surface = smap->surface;
403 } else {
404 smap = new xr_surfmap(surface);
405 }
406 surfmaps[faces[i]] = smap;
407 }
408 smap->faces.push_back(i);
409 }
410 return status;
411 }
412
413 struct temp_edge {
414 temp_edge();
415 uint32_t faces[2];
416 };
417
temp_edge()418 inline temp_edge::temp_edge() { faces[0] = UINT32_MAX; faces[1] = UINT32_MAX; }
419
420 struct temp_face {
421 uint32_t edges[3];
422 };
423
424 // FIXME: consider using 3ds Max smoothing groups.
extract_smoothing_groups(MFnMesh & mesh_fn,std::vector<uint32_t> & sgroups)425 static MStatus extract_smoothing_groups(MFnMesh& mesh_fn, std::vector<uint32_t>& sgroups)
426 {
427 MStatus status = MS::kSuccess;
428
429 MIntArray connected;
430
431 temp_edge* temp_edges = new temp_edge[unsigned(mesh_fn.numEdges() & INT_MAX)];
432 for (MItMeshEdge it(mesh_fn.object()); !it.isDone(); it.next()) {
433 if (it.isSmooth()) {
434 it.getConnectedFaces(connected, &status);
435 CHECK_MSTATUS(status);
436 unsigned n = connected.length();
437 if (n <= 2) {
438 temp_edge& edge = temp_edges[it.index()];
439 while (n) {
440 --n;
441 edge.faces[n] = uint32_t(connected[n] & INT_MAX);
442 }
443 }
444 }
445 }
446
447 unsigned num_faces = unsigned(mesh_fn.numPolygons() & INT_MAX);
448 temp_face* temp_faces = new temp_face[num_faces];
449 for (MItMeshPolygon it(mesh_fn.object()); !it.isDone(); it.next()) {
450 status = it.getEdges(connected);
451 CHECK_MSTATUS(status);
452 unsigned n = connected.length();
453 if (n != 3) {
454 msg("can't build smoothing groups");
455 delete[] temp_edges;
456 delete[] temp_faces;
457 return MS::kInvalidParameter;
458 }
459 temp_face& face = temp_faces[it.index(&status)];
460 CHECK_MSTATUS(status);
461 while (n) {
462 --n;
463 face.edges[n] = uint32_t(connected[n] & INT_MAX);
464 }
465 }
466
467 sgroups.assign(num_faces, EMESH_NO_SG);
468 std::vector<uint32_t> adjacents;
469 adjacents.reserve(512);
470 uint32_t sgroup = 0;
471 for (uint_fast32_t base_idx = num_faces; base_idx != 0;) {
472 if (sgroups[--base_idx] != EMESH_NO_SG)
473 continue;
474 bool new_sgroup = false;
475 for (uint_fast32_t face_idx = base_idx;;) {
476 const temp_face& face = temp_faces[face_idx];
477 for (uint_fast32_t i = 3; i != 0;) {
478 temp_edge& edge = temp_edges[face.edges[--i]];
479 if (edge.faces[0] == UINT32_MAX || edge.faces[1] == UINT32_MAX)
480 continue;
481 if (!new_sgroup) {
482 new_sgroup = true;
483 sgroups[face_idx] = sgroup;
484 }
485 uint32_t adj_face_idx = edge.faces[0] == face_idx ?
486 edge.faces[1] : edge.faces[0];
487 if (sgroups[adj_face_idx] == EMESH_NO_SG) {
488 adjacents.push_back(adj_face_idx);
489 sgroups[adj_face_idx] = sgroup;
490 }
491 }
492 if (adjacents.empty())
493 break;
494 face_idx = adjacents.back();
495 adjacents.pop_back();
496 }
497 if (new_sgroup)
498 ++sgroup;
499 }
500
501 delete[] temp_edges;
502 delete[] temp_faces;
503
504 return status;
505 }
506
commit_surfaces(xr_surface_vec & surfaces)507 void maya_export_tools::commit_surfaces(xr_surface_vec& surfaces)
508 {
509 surfaces.reserve(m_shared_surfaces.size());
510 for (xr_surface_map_it it = m_shared_surfaces.begin(),
511 end = m_shared_surfaces.end(); it != end; ++it) {
512 surfaces.push_back(it->second);
513 }
514 }
515
create_object(MObjectArray & mesh_objs)516 xr_object* maya_export_tools::create_object(MObjectArray& mesh_objs)
517 {
518 MStatus status;
519
520 xr_object* object = new xr_object;
521 object->flags() = EOF_STATIC;
522 object->meshes().reserve(mesh_objs.length());
523
524 for (unsigned i = mesh_objs.length(); i != 0;) {
525 MFnMesh mesh_fn(mesh_objs[--i]);
526
527 xr_mesh* mesh = new xr_mesh;
528 object->meshes().push_back(mesh);
529 mesh->name() = mesh_fn.name().asChar();
530
531 if (!(status = extract_points(mesh_fn, mesh->points(), mesh->bbox())))
532 goto fail;
533
534 if (!(status = extract_faces(mesh_fn, mesh->faces())))
535 goto fail;
536
537 if (!(status = extract_uvs(mesh_fn, mesh->faces(), mesh->vmrefs(), mesh->vmaps())))
538 goto fail;
539
540 if (!(status = extract_surfaces(mesh_fn, mesh->surfmaps())))
541 goto fail;
542
543 if (!(status = extract_smoothing_groups(mesh_fn, mesh->sgroups())))
544 goto fail;
545 }
546
547 commit_surfaces(object->surfaces());
548
549 return object;
550
551 fail:
552 delete object;
553 return 0;
554 }
555
create_skl_object(MObject & mesh_obj,MObject & skin_obj)556 xr_object* maya_export_tools::create_skl_object(MObject& mesh_obj, MObject& skin_obj)
557 {
558 MStatus status;
559
560 xr_object* object = new xr_object;
561 object->flags() = EOF_DYNAMIC;
562
563 MFnMesh mesh_fn(mesh_obj);
564
565 xr_mesh* mesh = new xr_mesh;
566 // attach now to allow auto-deletion in case of error
567 object->meshes().push_back(mesh);
568 mesh->name() = mesh_fn.name().asChar();
569
570 MFnSkinCluster skin_fn(skin_obj);
571 if (!(status = extract_bones(skin_fn, object->bones())))
572 goto fail;
573
574 if (!(status = extract_points(mesh_fn, mesh->points(), mesh->bbox())))
575 goto fail;
576
577 if (!(status = extract_faces(mesh_fn, mesh->faces())))
578 goto fail;
579
580 if (!(status = extract_uvs(mesh_fn, mesh->faces(), mesh->vmrefs(), mesh->vmaps())))
581 goto fail;
582
583 if (!(status = extract_weights(mesh_fn, skin_fn, mesh->faces(), mesh->vmrefs(), mesh->vmaps())))
584 goto fail;
585
586 if (!(status = extract_surfaces(mesh_fn, mesh->surfmaps())))
587 goto fail;
588
589 if (!(status = extract_smoothing_groups(mesh_fn, mesh->sgroups())))
590 goto fail;
591
592 object->partitions().push_back(new xr_partition(object->bones()));
593
594 commit_surfaces(object->surfaces());
595
596 return object;
597
598 fail:
599 delete object;
600 return 0;
601 }
602
collect_meshes(MObjectArray & mesh_objs,MObject & root_obj=MObject::kNullObj)603 static void collect_meshes(MObjectArray& mesh_objs, MObject& root_obj = MObject::kNullObj)
604 {
605 MItDag dag_it;
606 if (!root_obj.isNull() && !dag_it.reset(root_obj))
607 return;
608
609 MStatus status;
610 for (MDagPath dag_path; !dag_it.isDone(); dag_it.next()) {
611 status = dag_it.getPath(dag_path);
612 if (!status)
613 continue;
614 MFnDagNode dag_fn(dag_path);
615 if (dag_fn.isIntermediateObject())
616 continue;
617 if (!dag_path.hasFn(MFn::kTransform))
618 continue;
619 for (unsigned i = dag_fn.childCount(); i != 0;) {
620 MObject obj = dag_fn.child(--i);
621 if (obj.hasFn(MFn::kMesh)) {
622 MFnMesh mesh_fn(obj);
623 if (!mesh_fn.isIntermediateObject())
624 mesh_objs.append(obj);
625 }
626 }
627 }
628 }
629
collect_meshes(MObjectArray & mesh_objs,bool selection_only)630 static void collect_meshes(MObjectArray& mesh_objs, bool selection_only)
631 {
632 if (selection_only) {
633 MSelectionList selection;
634 MStatus status = MGlobal::getActiveSelectionList(selection);
635 if (!status || selection.isEmpty())
636 return;
637 MDagPath dag_path;
638 for (MItSelectionList it(selection); !it.isDone(); it.next()) {
639 if (!it.getDagPath(dag_path))
640 continue;
641 MObject root_obj = dag_path.node(&status);
642 if (status)
643 collect_meshes(mesh_objs, root_obj);
644 }
645 } else {
646 collect_meshes(mesh_objs);
647 }
648 }
649
export_object(const char * path,bool selection_only)650 MStatus maya_export_tools::export_object(const char* path, bool selection_only)
651 {
652 MObjectArray mesh_objs;
653 collect_meshes(mesh_objs, selection_only);
654 if (mesh_objs.length() == 0) {
655 msg("can't find any mesh to export");
656 return MS::kFailure;
657 }
658
659 m_skeletal = false;
660
661 MStatus status = MS::kFailure;
662 if (xr_object* object = create_object(mesh_objs)) {
663 if (object->save_object(path))
664 status = MS::kSuccess;
665 delete object;
666 }
667
668 return status;
669 }
670
find_mesh_and_skin(MObject * mesh_obj,MObject * skin_obj,bool selection_only)671 static MStatus find_mesh_and_skin(MObject* mesh_obj, MObject* skin_obj, bool selection_only)
672 {
673 MObjectArray mesh_objs;
674 collect_meshes(mesh_objs, selection_only);
675 switch (mesh_objs.length()) {
676 case 0:
677 msg("can't find any mesh to export");
678 return MS::kFailure;
679
680 case 1:
681 break;
682
683 default:
684 msg("can't handle multiple meshes in skeletal object");
685 return MS::kFailure;
686 }
687
688 MObjectArray skin_objs;
689 for (MItDependencyNodes dep_it(MFn::kSkinClusterFilter); !dep_it.isDone(); dep_it.next()) {
690 MObject skin_obj = dep_it.thisNode();
691 MFnSkinCluster skin_fn(skin_obj);
692 MObjectArray affected;
693 skin_fn.getOutputGeometry(affected);
694 msg("skin cluster %s", skin_fn.name().asChar());
695 for (unsigned i = affected.length(); i != 0;) {
696 if (affected[--i] == mesh_objs[0])
697 skin_objs.append(skin_obj);
698 }
699 }
700 switch (skin_objs.length()) {
701 case 0:
702 msg("can't find skin cluster for mesh");
703 return MS::kFailure;
704
705 case 1:
706 break;
707
708 default:
709 msg("can't handle multiple skin clusters in skeletal object");
710 return MS::kFailure;
711 }
712
713 if (mesh_obj)
714 *mesh_obj = mesh_objs[0];
715 if (skin_obj)
716 *skin_obj = skin_objs[0];
717 return MS::kSuccess;
718 }
719
export_skl_object(const char * path,bool selection_only)720 MStatus maya_export_tools::export_skl_object(const char* path, bool selection_only)
721 {
722 MObject mesh_obj, skin_obj;
723 MStatus status = find_mesh_and_skin(&mesh_obj, &skin_obj, selection_only);
724 if (!status)
725 return status;
726
727 m_skeletal = true;
728
729 status = MS::kFailure;
730 if (xr_object* object = create_skl_object(mesh_obj, skin_obj)) {
731 if (object->save_object(path))
732 status = MS::kSuccess;
733 delete object;
734 }
735 return status;
736 }
737
export_skl(const char * path,bool selection_only)738 MStatus maya_export_tools::export_skl(const char* path, bool selection_only)
739 {
740 if (MTime::uiUnit() != MTime::kNTSCFrame)
741 msg("warning: motion export with non-NTSC frame frequency was not tested");
742
743 MObject skin_obj;
744 MStatus status = find_mesh_and_skin(0, &skin_obj, selection_only);
745 if (!status)
746 return status;
747
748 MFnSkinCluster skin_fn(skin_obj);
749 MDagPathArray joints;
750 skin_fn.influenceObjects(joints, &status);
751 unsigned num_joints = joints.length();
752 if (num_joints == 0) {
753 msg("can't find any influence object");
754 return MS::kFailure;
755 }
756 xr_bone_motion_vec bmotions(num_joints);
757 for (unsigned i = num_joints; i != 0;) {
758 MFnIkJoint joint_fn(joints[--i], &status);
759 if (!status) {
760 msg("can't handle non-joint node %s",
761 joints[i].partialPathName().asChar());
762 return status;
763 }
764 xr_bone_motion* bmotion = new xr_bone_motion(joint_fn.name().asChar());
765 bmotion->create_envelopes();
766 bmotions[i] = bmotion;
767 }
768
769 MTime saved_time(MAnimControl::currentTime());
770
771 int32_t frame_start = int32_t(MAnimControl::minTime().as(MTime::kNTSCFrame));
772 int32_t frame_end = int32_t(MAnimControl::maxTime().as(MTime::kNTSCFrame));
773 msg("range=%d - %d", frame_start, frame_end);
774
775 for (int32_t frame = frame_start; frame != frame_end; ++frame) {
776 MGlobal::viewFrame(MTime(double(frame), MTime::kNTSCFrame));
777 float time = frame/30.f;
778 for (unsigned i = num_joints; i != 0;) {
779 MFnIkJoint joint_fn(joints[--i]);
780 xr_envelope* const* envelopes = bmotions[i]->envelopes();
781
782 MVector t = joint_fn.getTranslation(MSpace::kTransform, &status);
783 CHECK_MSTATUS(status);
784 envelopes[0]->insert_key(time, float(MDistance(t.x, MDistance::kCentimeters).asMeters()));
785 envelopes[1]->insert_key(time, float(MDistance(t.y, MDistance::kCentimeters).asMeters()));
786 envelopes[2]->insert_key(time, float(MDistance(-t.z, MDistance::kCentimeters).asMeters()));
787
788 MEulerRotation r;
789 status = joint_fn.getRotation(r);
790 CHECK_MSTATUS(status);
791 r.reorderIt(MEulerRotation::kZXY);
792 envelopes[4]->insert_key(time, float(-r.x));
793 envelopes[3]->insert_key(time, float(-r.y));
794 envelopes[5]->insert_key(time, float(r.z));
795 }
796 }
797 xr_skl_motion* smotion = new xr_skl_motion;
798 smotion->name() = "unnamed";
799 smotion->fps() = 30.f;
800 smotion->set_frame_range(frame_start, frame_end);
801 smotion->bone_motions().swap(bmotions);
802 status = smotion->save_skl(path) ? MS::kSuccess : MS::kFailure;
803 delete smotion;
804
805 MAnimControl::setCurrentTime(saved_time);
806
807 return status;
808 }
809