//-----------------------------/------------------------------------------------ // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #ifdef _MSC_VER #pragma warning(disable : 4786) #endif #include "mayaAppMesh.h" #include "mayaAppNode.h" #include "appConfig.h" #include "appIfl.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace DTS { bool getMeshAndSkin(MDagPath & path, MFnSkinCluster & skinCluster) { MStatus status; // The following is the "correct" way to get the // mesh and skinCluster in case the mesh is a skin, // according to (and adapted from): // http://www.greggman.com/pages/mayastuff.htm // But we don't get the MeshFn from the input plug of the // skinCluster as he does. Two reasons for this: // 1) couldn't get it to work properly, not a valid MeshFn. // 2) we want the final mesh, not the intermediate product. // the deformed mesh comes into the visible mesh // through its "inmesh" plug MPlug inMeshPlug = MFnDagNode(path).findPlug("inMesh", &status); if (status != MS::kSuccess || !inMeshPlug.isConnected()) return false; // no skin found // walk the tree of stuff upstream from this plug MItDependencyGraph dgIt(inMeshPlug,MFn::kInvalid, MItDependencyGraph::kUpstream, MItDependencyGraph::kDepthFirst, MItDependencyGraph::kPlugLevel, &status); if (MS::kSuccess == status) { S32 count = 0; dgIt.disablePruningOnFilter(); for ( ; ! dgIt.isDone(); dgIt.next() ) { MObject thisNode = dgIt.thisNode(); // go until we find a skinCluster if (thisNode.apiType() == MFn::kSkinClusterFilter) { skinCluster.setObject(thisNode); return true; // found a skin } } } return false; // no skin found } MayaAppMesh::MayaAppMesh(MDagPath & nodePath, MDagPath & path) { mPath = path; mNodePath = nodePath; mName = NULL; } MayaAppMesh::~MayaAppMesh() { } void MayaAppMesh::getSkinData() { MFnSkinCluster skinCluster; if (!getMeshAndSkin(mPath,skinCluster)) return; // no skin, no sweat MStatus status; S32 i; // get bones/influence array MDagPathArray bones; S32 numBones = skinCluster.influenceObjects(bones, &status); if (!CheckMayaStatus(status)) return; S32 bone; for (bone=0; bone); } MItGeometry gitr(mPath,&status); if (!CheckMayaStatus(status)) return; AppConfig::PrintDump(PDObjectStateDetails,avar("SkinPath: %s, # verts: %i, # bones: %i", mPath.partialPathName().asChar(),gitr.count(),numBones)); for (; !gitr.isDone(); gitr.next()) { while ( numBones && mWeights[0]->size() <= U32(gitr.index())) for (i=0; ipush_back(0.0f); MObject comp = gitr.component(&status); if (!CheckMayaStatus(status)) return; MFloatArray wts; U32 weightCount; status = skinCluster.getWeights(mPath,comp,wts,weightCount); if (!CheckMayaStatus(status)) return; if (weightCount != numBones) { AppConfig::SetExportError("maya3", "Assertion failed on skinned mesh"); return; } for (i=0; isize() < U32(numVerts)) for (i=0; ipush_back(0.0f); } const char * MayaAppMesh::getName() { MStatus status; if (!mName) { MFnDagNode fnNode(mNodePath,&status); CheckMayaStatus(status); const char * newname = fnNode.name(&status).asChar(); CheckMayaStatus(status); if (newname) { mName = strnew(newname); HandleMayaNegative(mName); } } CheckMayaStatus(status); return mName; } Matrix<4,4,F32> MayaAppMesh::getMeshTransform(const AppTime & time) { MStatus status; MayaSetTime(time); MMatrix mayaMat = mPath.inclusiveMatrix(&status); CheckMayaStatus(status); return MayaToDtsTransform(mayaMat); } F32 MayaAppMesh::getVisValue(const AppTime & time) { // Need to check for "visibility" plug, which is built into // maya and is a bool, and "vis" plug, which is custom and // assumed to be a float. We added the "vis" plug to allow // non-bool vis values. The "vis" plug takes precedence. MayaSetTime(time); F32 val = 1.0f; if (getFloat("vis",val)) return val; bool ret = true; getBool("visibility",ret); return ret ? 1.0f : 0.0f; } bool MayaAppMesh::getFloat(const char * propName, F32 & defaultVal) { MStatus status; MFnDagNode fnNode(mNodePath,&status); CheckMayaStatus(status); MPlug plug = fnNode.findPlug(MString(propName),&status); if (status!=MStatus::kSuccess) return false; F32 val; status = plug.getValue(val); if (status == MStatus::kSuccess) defaultVal = val; return status==MStatus::kSuccess; } bool MayaAppMesh::getInt(const char * propName, S32 & defaultVal) { MStatus status; MFnDagNode fnNode(mNodePath,&status); CheckMayaStatus(status); MPlug plug = fnNode.findPlug(MString(propName),&status); if (status!=MStatus::kSuccess) return false; S32 val; status = plug.getValue(val); if (status == MStatus::kSuccess) defaultVal = val; return status==MStatus::kSuccess; } bool MayaAppMesh::getBool(const char * propName, bool & defaultVal) { MStatus status; MFnDagNode fnNode(mNodePath,&status); CheckMayaStatus(status); MPlug plug = fnNode.findPlug(MString(propName),&status); if (status!=MStatus::kSuccess) return false; bool val; status = plug.getValue(val); if (status == MStatus::kSuccess) defaultVal = val; return status==MStatus::kSuccess; } bool MayaAppMesh::getMaterial(S32 matIdx, Material & mat) { if (matIdx >= 0 && U32(matIdx) < mMaterials.size()) { mat = mMaterials[matIdx]; return true; } return false; } bool MayaAppMesh::animatesVis(const AppSequenceData & seqData) { MStatus status; // Need to check to see if either "visibility" or "vis" is animated. // "visibility" is the built-in visibility mechanism, but it is // only a bool. So we also look for custom "vis" which is assumed // to be a float. MFnDagNode fnNode(mNodePath,&status); CheckMayaStatus(status); MPlug plug = fnNode.findPlug(MString("visibility"),&status); CheckMayaStatus(status); if (status!=MStatus::kSuccess) return false; if (MAnimUtil::isAnimated(plug,false,&status)) return Parent::animatesVis(seqData); CheckMayaStatus(status); // don't animate "visibility", what about "vis" MPlug plug2 = fnNode.findPlug(MString("vis"),&status); if (status!=MStatus::kSuccess) return false; if (MAnimUtil::isAnimated(plug2,false,&status)) return Parent::animatesVis(seqData); return false; } AppMeshLock MayaAppMesh::lockMesh(const AppTime & time, const Matrix<4,4,F32> & objectOffset) { S32 i; MayaSetTime(time); MStatus status; MObject component = MObject::kNullObj; // not really sure what this is for... MFnMesh fnMesh(mPath,&status); if (!CheckMayaStatus(status)) return Parent::lockMesh(time,objectOffset); MItMeshPolygon polyIter(mPath,component,&status); if (!CheckMayaStatus(status)) return Parent::lockMesh(time,objectOffset); S32 polyVertexCount = polyIter.polygonVertexCount(); if (polyVertexCount != 3) { AppConfig::SetExportError("maya5", "Non-triangle mesh not supported"); return Parent::lockMesh(time,objectOffset); } MItMeshVertex vtxIter(mPath,component,&status); if (!CheckMayaStatus(status)) return Parent::lockMesh(time,objectOffset); // Create transform based on objectOffset for transforming normals Matrix<4,4,F32> normOffset = objectOffset; normOffset[0][3] = 0.0f; normOffset[1][3] = 0.0f; normOffset[2][3] = 0.0f; normOffset[3][0] = 0.0f; normOffset[3][1] = 0.0f; normOffset[3][2] = 0.0f; normOffset[3][3] = 1.0f; F32 normScale = 0.0f; for (i=0; i<9; i++) { S32 j = i % 3; normScale += normOffset[(i-j)/3][j]; } normScale /= 3.0f; normScale = 1.0f/normScale; // Write out the vertex table // std::vector verts; std::vector tverts; for ( ; !vtxIter.isDone(); vtxIter.next() ) { MPoint p = vtxIter.position( MSpace::kObject ); // convert from maya point to dts point... // ...also, convert from internal units // to the current ui units Point3D pos; MDistance dist; // starts as internal units dist.setValue(p.x); pos[0] = F32(dist.asMeters()); dist.setValue(p.y); pos[1] = F32(dist.asMeters()); dist.setValue(p.z); pos[2] = F32(dist.asMeters()); verts.push_back(objectOffset * pos); } MFloatArray uArray, vArray; fnMesh.getUVs( uArray, vArray ); S32 uvLength = uArray.length(); for (i=0; i=0; vtx-- ) { U16 vertId = polyIter.vertexIndex(vtx); Point3D vert = verts[vertId]; // compute normal (TODO: handle non-uniform scale properly) MVector mayaNorm; status = polyIter.getNormal(vtx,mayaNorm,MSpace::kObject); if (!CheckMayaStatus(status)) return Parent::lockMesh(time,objectOffset); F32 norm0, norm1, norm2; norm0 = F32(mayaNorm[0]); norm1 = F32(mayaNorm[1]); norm2 = F32(mayaNorm[2]); Point3D norm(norm0, norm1, norm2); norm = normOffset * norm; norm *= normScale; if (norm.length()>0.001f) norm.normalize(); else norm = Point3D(0,0,1); // grab tvert Point2D tvert(0,0); if (fnMesh.numUVs() > 0) { if (polyIter.hasUVs()) { S32 uvIndex; if (polyIter.getUVIndex(vtx,uvIndex)) tvert = tverts[uvIndex]; } } mIndices.push_back(addVertex(vert,norm,tvert,vertId)); } mFaces.push_back(face); } getMaterials(); // are we 2-sided? bool twoSided = false; getBool("twoSided",twoSided); if (twoSided) { S32 numFaces = mFaces.size(); for (i=0; i0.1f || hasTransparency) mat.flags |= Material::Translucent; if (selfIlluminating>0.1f) mat.flags |= Material::SelfIlluminating; } MFnReflectShader rshader(shaderNode,&status); if (status == MStatus::kSuccess) { F32 reflectivity = rshader.reflectivity(); if (reflectivity>0.01f) { mat.reflection = reflectivity; mat.flags ^= Material::NeverEnvMap; } } // now get the texture placement node status = dgIt.resetTo(colorPlug, MFn::kPlace2dTexture, MItDependencyGraph::kUpstream, MItDependencyGraph::kBreadthFirst, MItDependencyGraph::kNodeLevel); dgIt.disablePruningOnFilter(); if (status != MS::kFailure) if (!dgIt.isDone()) { MObject placeNode = dgIt.thisNode(); MStatus stat; MFnDependencyNode fnNode(placeNode,&stat); bool swrap = true; bool twrap = true; MPlug plug = fnNode.findPlug(MString("wrapU"),&stat); plug.getValue(swrap); plug = fnNode.findPlug(MString("wrapV"),&stat); plug.getValue(swrap); if (!swrap) mat.flags ^= Material::SWrap; if (!twrap) mat.flags ^= Material::TWrap; } /* Need to deal with these flags: Additive = 0x00000008, Subtractive = 0x00000010, IFLMaterial = 0x08000000, */ //------------ // If this is an ifl, then create the ifl material if it doesn't exist and mark as ifl const char * dot = strchr(textureName.asChar(),'.'); if (dot && !stricmp(dot+1,"ifl")) { mat.flags |= Material::IFLMaterial; S32 matIdx = mMaterials.size(); while (mIfls.size() <= U32(matIdx)) mIfls.push_back(NULL); if (!mIfls[matIdx]) mIfls[matIdx] = new AppIfl(mat.name.c_str()); } mMaterials.push_back(mat); } } MObject MayaAppMesh::findShader( MObject& setNode ) { // Description: Find the shading node for the given shading group set node. MFnDependencyNode fnNode(setNode); MPlug shaderPlug = fnNode.findPlug("surfaceShader"); if (!shaderPlug.isNull()) { MPlugArray connectedPlugs; bool asSrc = false; bool asDst = true; shaderPlug.connectedTo( connectedPlugs, asDst, asSrc ); if (connectedPlugs.length() != 1) AppConfig::SetExportError("maya4", "Error getting shader\r\n"); else return connectedPlugs[0].node(); } return MObject::kNullObj; } void MayaAppMesh::unlockMesh() { Parent::unlockMesh(); // no more cleanup...but if there were some to do, we'd do it here } void MayaSetTime(const AppTime & time) { MTime mayaTime(time.getF64(),MTime::kSeconds); MGlobal::viewFrame(mayaTime); } Matrix<4,4,F32> MayaToDtsTransform(MMatrix & mayaMat) { Matrix<4,4,F32> mat; for (S32 i=0; i<4; i++) for (S32 j=0; j<4; j++) mat[j][i] = F32(mayaMat[i][j]); F64 scale; MDistance dist; // starts as internal units dist.setValue(1.0f); scale = dist.asMeters(); mat[0][3] *= F32(scale); mat[1][3] *= F32(scale); mat[2][3] *= F32(scale); return mat; } bool CheckMayaStatus(MStatus & status) { if (!bool(status)) { AppConfig::SetExportError("maya2", avar("Maya error: %s",status.errorString().asChar())); status = MStatus::kSuccess; return false; } return true; } void HandleMayaNegative(char * name) { S32 pos = strlen(name); while (pos) { --pos; if (isdigit(name[pos])) continue; if (name[pos]=='_') name[pos]='-'; break; } } }; // namespace DTS