// Filename: mayaFile.cxx // Created by: drose (10Nov99) // //////////////////////////////////////////////////////////////////// // // PANDA 3D SOFTWARE // Copyright (c) 2001, Disney Enterprises, Inc. All rights reserved // // All use of this software is subject to the terms of the Panda 3d // Software license. You should have received a copy of this license // along with this source code; you will also find a current copy of // the license at http://www.panda3d.org/license.txt . // // To contact the maintainers of this program write to // panda3d@yahoogroups.com . // //////////////////////////////////////////////////////////////////// #include "mayaFile.h" #include "mayaShader.h" #include "global_parameters.h" #include "maya_funcs.h" #include "eggData.h" #include "eggGroup.h" #include "eggVertex.h" #include "eggVertexPool.h" #include "eggNurbsSurface.h" #include "eggNurbsCurve.h" #include "eggPolygon.h" #include "pre_maya_include.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "post_maya_include.h" MayaFile:: MayaFile() { verbose = 0; } MayaFile:: ~MayaFile() { MLibrary::cleanup(); } bool MayaFile:: init(const string &program) { MStatus stat = MLibrary::initialize((char *)program.c_str()); if (!stat) { stat.perror("MLibrary::initialize"); return false; } return true; } bool MayaFile:: read(const string &filename) { MFileIO::newFile(true); nout << "Loading \"" << filename << "\" ... " << flush; // Load the file into Maya MStatus stat = MFileIO::open(filename.c_str()); if (!stat) { stat.perror(filename.c_str()); return false; } nout << " done.\n"; return true; } void MayaFile:: make_egg(EggData &data) { traverse(data); } //////////////////////////////////////////////////////////////////// // Function: MayaFile::get_units // Access: Public, Static // Description: Returns Maya's internal units in effect. //////////////////////////////////////////////////////////////////// DistanceUnit MayaFile:: get_units() { switch (MDistance::internalUnit()) { case MDistance::kInches: return DU_inches; case MDistance::kFeet: return DU_feet; case MDistance::kYards: return DU_yards; case MDistance::kMiles: return DU_statute_miles; case MDistance::kMillimeters: return DU_millimeters; case MDistance::kCentimeters: return DU_centimeters; case MDistance::kKilometers: return DU_kilometers; case MDistance::kMeters: return DU_meters; default: return DU_invalid; } } //////////////////////////////////////////////////////////////////// // Function: MayaFile::get_coordinate_system // Access: Public, Static // Description: Returns Maya's internal coordinate system in effect. //////////////////////////////////////////////////////////////////// CoordinateSystem MayaFile:: get_coordinate_system() { if (MGlobal::isYAxisUp()) { return CS_yup_right; } else { return CS_zup_right; } } bool MayaFile:: traverse(EggData &data) { MStatus status; MItDag dag_iterator(MItDag::kDepthFirst, MFn::kTransform, &status); if (!status) { status.perror("MItDag constructor"); return false; } if (verbose >= 1) { nout << "Traversing scene graph.\n"; } // Scan the entire DAG and output the name and depth of each node while (!dag_iterator.isDone()) { MDagPath dag_path; status = dag_iterator.getPath(dag_path); if (!status) { status.perror("MItDag::getPath"); } else { process_node(dag_path, data); } dag_iterator.next(); } if (verbose == 1) { nout << "\nDone.\n"; } return true; } bool MayaFile:: process_node(const MDagPath &dag_path, EggData &data) { MStatus status; MFnDagNode dag_node(dag_path, &status); if (!status) { status.perror("MFnDagNode constructor"); return false; } if (verbose == 1) { nout << "." << flush; } else if (verbose >= 2) { nout << dag_node.name() << ": " << dag_node.typeName() << "\n" << " dag_path: " << dag_path.fullPathName() << "\n"; } if (dag_path.hasFn(MFn::kCamera)) { if (verbose >= 2) { nout << "Ignoring camera node " << dag_path.fullPathName() << "\n"; } } else if (dag_path.hasFn(MFn::kLight)) { if (verbose >= 2) { nout << "Ignoring light node " << dag_path.fullPathName() << "\n"; } } else if (dag_path.hasFn(MFn::kNurbsSurface)) { EggGroup *egg_group = get_egg_group(dag_path.fullPathName().asChar(), data); if (egg_group == (EggGroup *)NULL) { nout << "Cannot determine group node.\n"; } else { get_transform(dag_path, egg_group); MFnNurbsSurface surface(dag_path, &status); if (!status) { if (verbose >= 2) { nout << "Error in node " << dag_path.fullPathName() << ":\n" << " it appears to have a NURBS surface, but does not.\n"; } } else { make_nurbs_surface(dag_path, surface, egg_group); } } } else if (dag_path.hasFn(MFn::kNurbsCurve)) { EggGroup *egg_group = get_egg_group(dag_path.fullPathName().asChar(), data); if (egg_group == (EggGroup *)NULL) { nout << "Cannot determine group node.\n"; } else { get_transform(dag_path, egg_group); MFnNurbsCurve curve(dag_path, &status); if (!status) { if (verbose >= 2) { nout << "Error in node " << dag_path.fullPathName() << ":\n" << " it appears to have a NURBS curve, but does not.\n"; } } else { make_nurbs_curve(dag_path, curve, egg_group); } } } else if (dag_path.hasFn(MFn::kMesh)) { EggGroup *egg_group = get_egg_group(dag_path.fullPathName().asChar(), data); if (egg_group == (EggGroup *)NULL) { nout << "Cannot determine group node.\n"; } else { get_transform(dag_path, egg_group); MFnMesh mesh(dag_path, &status); if (!status) { if (verbose >= 2) { nout << "Error in node " << dag_path.fullPathName() << ":\n" << " it appears to have a polygon mesh, but does not.\n"; } } else { make_polyset(dag_path, mesh, egg_group); } } } else { // Get the translation/rotation/scale data EggGroup *egg_group = get_egg_group(dag_path.fullPathName().asChar(), data); if (egg_group != (EggGroup *)NULL) { get_transform(dag_path, egg_group); } } return true; } void MayaFile:: get_transform(const MDagPath &dag_path, EggGroup *egg_group) { if (ignore_transforms) { return; } MStatus status; MObject transformNode = dag_path.transform(&status); // This node has no transform - i.e., it's the world node if (!status && status.statusCode() == MStatus::kInvalidParameter) return; MFnDagNode transform(transformNode, &status); if (!status) { status.perror("MFnDagNode constructor"); return; } MTransformationMatrix matrix(transform.transformationMatrix()); if (verbose >= 3) { nout << " translation: " << matrix.translation(MSpace::kWorld) << "\n"; double d[3]; MTransformationMatrix::RotationOrder rOrder; matrix.getRotation(d, rOrder, MSpace::kWorld); nout << " rotation: [" << d[0] << ", " << d[1] << ", " << d[2] << "]\n"; matrix.getScale(d, MSpace::kWorld); nout << " scale: [" << d[0] << ", " << d[1] << ", " << d[2] << "]\n"; } MMatrix mat = matrix.asMatrix(); MMatrix ident_mat; ident_mat.setToIdentity(); if (!mat.isEquivalent(ident_mat, 0.0001)) { egg_group->set_transform (LMatrix4d(mat[0][0], mat[0][1], mat[0][2], mat[0][3], mat[1][0], mat[1][1], mat[1][2], mat[1][3], mat[2][0], mat[2][1], mat[2][2], mat[2][3], mat[3][0], mat[3][1], mat[3][2], mat[3][3])); } } void MayaFile:: make_nurbs_surface(const MDagPath &dag_path, MFnNurbsSurface &surface, EggGroup *egg_group) { MStatus status; string name = surface.name().asChar(); if (verbose >= 3) { nout << " numCVs: " << surface.numCVsInU() << " * " << surface.numCVsInV() << "\n"; nout << " numKnots: " << surface.numKnotsInU() << " * " << surface.numKnotsInV() << "\n"; nout << " numSpans: " << surface.numSpansInU() << " * " << surface.numSpansInV() << "\n"; } MayaShader *shader = _shaders.find_shader_for_node(surface.object()); if (polygon_output) { // If we want polygon output only, tesselate the NURBS and output // that. MTesselationParams params; params.setFormatType(MTesselationParams::kStandardFitFormat); params.setOutputType(MTesselationParams::kQuads); params.setStdFractionalTolerance(polygon_tolerance); // We'll create the tesselation as a sibling of the NURBS surface. // That way we inherit all of the transformations. MDagPath polyset_path = dag_path; MObject polyset_parent = polyset_path.node(); MObject polyset = surface.tesselate(params, polyset_parent, &status); if (!status) { status.perror("MFnNurbsSurface::tesselate"); return; } status = polyset_path.push(polyset); if (!status) { status.perror("MDagPath::push"); } MFnMesh polyset_fn(polyset, &status); if (!status) { status.perror("MFnMesh constructor"); return; } make_polyset(polyset_path, polyset_fn, egg_group, shader); return; } MPointArray cv_array; status = surface.getCVs(cv_array, MSpace::kWorld); if (!status) { status.perror("MFnNurbsSurface::getCVs"); return; } MDoubleArray u_knot_array, v_knot_array; status = surface.getKnotsInU(u_knot_array); if (!status) { status.perror("MFnNurbsSurface::getKnotsInU"); return; } status = surface.getKnotsInV(v_knot_array); if (!status) { status.perror("MFnNurbsSurface::getKnotsInV"); return; } MFnNurbsSurface::Form u_form = surface.formInU(); MFnNurbsSurface::Form v_form = surface.formInV(); int u_degree = surface.degreeU(); int v_degree = surface.degreeV(); int u_cvs = surface.numCVsInU(); int v_cvs = surface.numCVsInV(); int u_knots = surface.numKnotsInU(); int v_knots = surface.numKnotsInV(); assert(u_knots == u_cvs + u_degree - 1); assert(v_knots == v_cvs + v_degree - 1); string vpool_name = name + ".cvs"; EggVertexPool *vpool = new EggVertexPool(vpool_name); egg_group->add_child(vpool); EggNurbsSurface *egg_nurbs = new EggNurbsSurface(name); egg_nurbs->setup(u_degree + 1, v_degree + 1, u_knots + 2, v_knots + 2); int i; egg_nurbs->set_u_knot(0, u_knot_array[0]); for (i = 0; i < u_knots; i++) { egg_nurbs->set_u_knot(i + 1, u_knot_array[i]); } egg_nurbs->set_u_knot(u_knots + 1, u_knot_array[u_knots - 1]); egg_nurbs->set_v_knot(0, v_knot_array[0]); for (i = 0; i < v_knots; i++) { egg_nurbs->set_v_knot(i + 1, v_knot_array[i]); } egg_nurbs->set_v_knot(v_knots + 1, v_knot_array[v_knots - 1]); for (i = 0; i < egg_nurbs->get_num_cvs(); i++) { int ui = egg_nurbs->get_u_index(i); int vi = egg_nurbs->get_v_index(i); double v[4]; MStatus status = cv_array[v_cvs * ui + vi].get(v); if (!status) { status.perror("MPoint::get"); } else { EggVertex vert; vert.set_pos(LPoint4d(v[0], v[1], v[2], v[3])); egg_nurbs->add_vertex(vpool->create_unique_vertex(vert)); } } // Now consider the trim curves, if any. unsigned num_trims = surface.numRegions(); int trim_curve_index = 0; for (unsigned ti = 0; ti < num_trims; ti++) { unsigned num_loops = surface.numBoundaries(ti); if (num_loops > 0) { egg_nurbs->_trims.push_back(EggNurbsSurface::Trim()); EggNurbsSurface::Trim &egg_trim = egg_nurbs->_trims.back(); for (unsigned li = 0; li < num_loops; li++) { egg_trim.push_back(EggNurbsSurface::Loop()); EggNurbsSurface::Loop &egg_loop = egg_trim.back(); MFnNurbsSurface::BoundaryType type = surface.boundaryType(ti, li, &status); bool keep_loop = false; if (!status) { status.perror("MFnNurbsSurface::BoundaryType"); } else { keep_loop = (type == MFnNurbsSurface::kInner || type == MFnNurbsSurface::kOuter); } if (keep_loop) { unsigned num_edges = surface.numEdges(ti, li); for (unsigned ei = 0; ei < num_edges; ei++) { MObjectArray edge = surface.edge(ti, li, ei, true, &status); if (!status) { status.perror("MFnNurbsSurface::edge"); } else { unsigned num_segs = edge.length(); for (unsigned si = 0; si < num_segs; si++) { MObject segment = edge[si]; if (segment.hasFn(MFn::kNurbsCurve)) { MFnNurbsCurve curve(segment, &status); if (!status) { nout << "Trim curve appears to be a nurbs curve, but isn't.\n"; } else { // Finally, we have a valid curve! EggNurbsCurve *egg_curve = make_trim_curve(curve, name, egg_group, trim_curve_index); trim_curve_index++; if (egg_curve != (EggNurbsCurve *)NULL) { egg_loop.push_back(egg_curve); } } } else { nout << "Trim curve segment is not a nurbs curve.\n"; } } } } } } } } // We add the NURBS to the group down here, after all of the vpools // for the trim curves have been added. egg_group->add_child(egg_nurbs); if (shader != (MayaShader *)NULL) { shader->set_attributes(*egg_nurbs, *this); } } EggNurbsCurve *MayaFile:: make_trim_curve(const MFnNurbsCurve &curve, const string &nurbs_name, EggGroupNode *egg_group, int trim_curve_index) { if (verbose >= 3) { nout << "Trim curve:\n"; nout << " numCVs: " << curve.numCVs() << "\n"; nout << " numKnots: " << curve.numKnots() << "\n"; nout << " numSpans: " << curve.numSpans() << "\n"; } MStatus status; MPointArray cv_array; status = curve.getCVs(cv_array, MSpace::kWorld); if (!status) { status.perror("MFnNurbsCurve::getCVs"); return (EggNurbsCurve *)NULL; } MDoubleArray knot_array; status = curve.getKnots(knot_array); if (!status) { status.perror("MFnNurbsCurve::getKnots"); return (EggNurbsCurve *)NULL; } MFnNurbsCurve::Form form = curve.form(); int degree = curve.degree(); int cvs = curve.numCVs(); int knots = curve.numKnots(); assert(knots == cvs + degree - 1); char trim_str[20]; sprintf(trim_str, "trim%d", trim_curve_index); assert(strlen(trim_str) < 20); string trim_name = trim_str; string vpool_name = nurbs_name + "." + trim_name; EggVertexPool *vpool = new EggVertexPool(vpool_name); egg_group->add_child(vpool); EggNurbsCurve *egg_curve = new EggNurbsCurve(trim_name); egg_curve->setup(degree + 1, knots + 2); int i; egg_curve->set_knot(0, knot_array[0]); for (i = 0; i < knots; i++) { egg_curve->set_knot(i + 1, knot_array[i]); } egg_curve->set_knot(knots + 1, knot_array[knots - 1]); for (i = 0; i < egg_curve->get_num_cvs(); i++) { double v[4]; MStatus status = cv_array[i].get(v); if (!status) { status.perror("MPoint::get"); } else { EggVertex vert; vert.set_pos(LPoint3d(v[0], v[1], v[3])); egg_curve->add_vertex(vpool->create_unique_vertex(vert)); } } return egg_curve; } void MayaFile:: make_nurbs_curve(const MDagPath &, const MFnNurbsCurve &curve, EggGroup *egg_group) { MStatus status; string name = curve.name().asChar(); if (verbose >= 3) { nout << " numCVs: " << curve.numCVs() << "\n"; nout << " numKnots: " << curve.numKnots() << "\n"; nout << " numSpans: " << curve.numSpans() << "\n"; } MPointArray cv_array; status = curve.getCVs(cv_array, MSpace::kWorld); if (!status) { status.perror("MFnNurbsCurve::getCVs"); return; } MDoubleArray knot_array; status = curve.getKnots(knot_array); if (!status) { status.perror("MFnNurbsCurve::getKnots"); return; } MFnNurbsCurve::Form form = curve.form(); int degree = curve.degree(); int cvs = curve.numCVs(); int knots = curve.numKnots(); assert(knots == cvs + degree - 1); string vpool_name = name + ".cvs"; EggVertexPool *vpool = new EggVertexPool(vpool_name); egg_group->add_child(vpool); EggNurbsCurve *egg_curve = new EggNurbsCurve(name); egg_group->add_child(egg_curve); egg_curve->setup(degree + 1, knots + 2); int i; egg_curve->set_knot(0, knot_array[0]); for (i = 0; i < knots; i++) { egg_curve->set_knot(i + 1, knot_array[i]); } egg_curve->set_knot(knots + 1, knot_array[knots - 1]); for (i = 0; i < egg_curve->get_num_cvs(); i++) { double v[4]; MStatus status = cv_array[i].get(v); if (!status) { status.perror("MPoint::get"); } else { EggVertex vert; vert.set_pos(LPoint4d(v[0], v[1], v[2], v[3])); egg_curve->add_vertex(vpool->create_unique_vertex(vert)); } } MayaShader *shader = _shaders.find_shader_for_node(curve.object()); if (shader != (MayaShader *)NULL) { shader->set_attributes(*egg_curve, *this); } } void MayaFile:: make_polyset(const MDagPath &dag_path, const MFnMesh &mesh, EggGroup *egg_group, MayaShader *default_shader) { MStatus status; string name = mesh.name().asChar(); if (verbose >= 3) { nout << " numPolygons: " << mesh.numPolygons() << "\n"; nout << " numVertices: " << mesh.numVertices() << "\n"; } if (mesh.numPolygons() == 0) { if (verbose >= 2) { nout << "Ignoring empty mesh " << name << "\n"; } return; } string vpool_name = name + ".verts"; EggVertexPool *vpool = new EggVertexPool(vpool_name); egg_group->add_child(vpool); /* MDagPath mesh_path; status = mesh.getPath(mesh_path); if (!status) { status.perror("MFnMesh::dagPath"); return; } */ MObject component_obj; MItMeshPolygon pi(dag_path, component_obj, &status); if (!status) { status.perror("MItMeshPolygon constructor"); return; } MObjectArray shaders; MIntArray poly_shader_indices; status = mesh.getConnectedShaders(dag_path.instanceNumber(), shaders, poly_shader_indices); if (!status) { status.perror("MFnMesh::getConnectedShaders"); } while (!pi.isDone()) { EggPolygon *egg_poly = new EggPolygon; egg_group->add_child(egg_poly); long num_verts = pi.polygonVertexCount(); for (long i = 0; i < num_verts; i++) { EggVertex vert; MPoint p = pi.point(i, MSpace::kWorld); vert.set_pos(LPoint3d(p[0], p[1], p[2])); MVector n; status = pi.getNormal(i, n, MSpace::kWorld); if (!status) { status.perror("MItMeshPolygon::getNormal"); } else { vert.set_normal(LVector3d(n[0], n[1], n[2])); } if (pi.hasUVs()) { float2 uvs; status = pi.getUV(i, uvs); if (!status) { status.perror("MItMeshPolygon::getUV"); } else { vert.set_uv(TexCoordd(uvs[0], uvs[1])); } } if (pi.hasColor()) { MColor c; status = pi.getColor(c, i); if (!status) { status.perror("MItMeshPolygon::getColor"); } else { vert.set_color(Colorf(c.r, c.g, c.b, 1.0)); } } egg_poly->add_vertex(vpool->create_unique_vertex(vert)); } // Determine the shader for this particular polygon. int index = pi.index(); assert(index >= 0 && index < (int)poly_shader_indices.length()); int shader_index = poly_shader_indices[index]; if (shader_index != -1) { assert(shader_index >= 0 && shader_index < (int)shaders.length()); MObject engine = shaders[shader_index]; MayaShader *shader = _shaders.find_shader_for_shading_engine(engine); if (shader != (MayaShader *)NULL) { shader->set_attributes(*egg_poly, *this); } } else if (default_shader != (MayaShader *)NULL) { default_shader->set_attributes(*egg_poly, *this); } pi.next(); } } EggGroup *MayaFile:: get_egg_group(const string &name, EggData &data) { Groups::const_iterator gi = _groups.find(name); if (gi != _groups.end()) { return (*gi).second; } EggGroup *egg_group; if (name.empty()) { // This is the top. egg_group = (EggGroup *)NULL; } else { size_t bar = name.rfind("|"); string parent_name, local_name; if (bar != string::npos) { parent_name = name.substr(0, bar); local_name = name.substr(bar + 1); } else { local_name = name; } EggGroup *parent_egg_group = get_egg_group(parent_name, data); egg_group = new EggGroup(local_name); if (parent_egg_group != (EggGroup *)NULL) { parent_egg_group->add_child(egg_group); } else { data.add_child(egg_group); } } _groups.insert(Groups::value_type(name, egg_group)); return egg_group; }