public override void doIt(MArgList args) { string fileName; MArgDatabase argData = new MArgDatabase(syntax, args); if (argData.isFlagSet(kFileNameFlag)) { fileName = argData.flagArgumentString(kFileNameFlag, 0); if (fileName != null) { string currFile = MFileIO.fileCurrentlyLoading; MStringArray pathDirectories = new MStringArray(currFile.Split('/')); if (pathDirectories.length > 0) { string expandedFileName = ""; for (int i = 0; i < pathDirectories.length - 1; i++) { expandedFileName += pathDirectories[i]; expandedFileName += "/"; } expandedFileName += fileName; MGlobal.sourceFile(expandedFileName); } } } return; }
// --- Utilities --- /// <summary> /// Retreive meshes having specified material id /// Meshes are split into 2 categories: Opaque or Transparent /// </summary> /// <param name="babylonScene"></param> /// <param name="materialId"></param> /// <param name="meshesOpaque"></param> /// <param name="meshesTransparent"></param> private void getMeshesByMaterialId(BabylonScene babylonScene, string materialId, List <BabylonMesh> meshesOpaque, List <BabylonMesh> meshesTransparent) { babylonScene.MeshesList.ForEach(mesh => { if (mesh.materialId == materialId) { // Get mesh full path name (unique) MStringArray meshFullPathName = new MStringArray(); // Surround uuid with quotes like so: ls "18D0785F-4E8E-1621-01E1-84AD39F92289"; // ls command output must be an array MGlobal.executeCommand($@"ls ""{mesh.id}"";", meshFullPathName); int meshOpaqueInt; MGlobal.executeCommand($@"getAttr {meshFullPathName[0]}.aiOpaque;", out meshOpaqueInt); if (meshOpaqueInt == 1) { meshesOpaque.Add(mesh); } else { meshesTransparent.Add(mesh); } } }); }
protected override void processWriteOptions(string optionsString) { includedNodeTypesArray = new MStringArray(); excludedNodeTypesArray = new MStringArray(); sourcedFilesArray = new MStringArray(); excludedNamesArray = new MStringArray(); outputRequirements = true; outputReferences = true; MStringArray optionsArray = new MStringArray(optionsString.Split(';')); for (int i = 0; i < optionsArray.length; i++) { string option = optionsArray[i]; MStringArray optionArray = new MStringArray(option.Split(' ')); if (optionArray[0] == "includeNodeType" && optionArray.length > 1) { includedNodeTypesArray.append(optionArray[1]); } else if (optionArray[0] == "excludeNodeType" && optionArray.length > 1) { excludedNodeTypesArray.append(optionArray[1]); } else if (optionArray[0] == "sourceFile" && optionArray.length > 1) { sourcedFilesArray.append(optionArray[1]); } } return; }
public static List <Curve> getSmothMeshEdges(MFnMesh mayaMesh, bool createInMaya = false) { //MCommandResult result = new MCommandResult(); int ne = mayaMesh.numEdges; MFnTransform group = new MFnTransform(); List <Curve> curveObjects = new List <Curve>(ne); MStringArray resultStr = new MStringArray(); var fullName = mayaMesh.fullPathName.Split('|'); string transformName = fullName[fullName.Length - 2]; if (createInMaya) { for (int i = 0; i < ne; i++) { using (MCommandResult result = new MCommandResult()) { MGlobal.executeCommand( $"polyToCurve -name {transformName}Curves -form 2 -degree 3 -conformToSmoothMeshPreview 1 {transformName}.e[{i}]", result); result.getResult(resultStr); curveObjects.Add( DMCurve.CurveFromMfnNurbsCurveFromName(resultStr[0], MSpace.Space.kPostTransform.ToString())); } } } else { //Parallel.For(0, ne, i => { for (int i = 0; i < ne; i++) { using (MCommandResult result = new MCommandResult()) { MGlobal.executeCommand( $"polyToCurve -name deleteMe11232204332AA -form 2 -degree 3 -conformToSmoothMeshPreview 1 {transformName}.e[{i}]", result); result.getResult(resultStr); curveObjects.Add( DMCurve.CurveFromMfnNurbsCurveFromName(resultStr[0], MSpace.Space.kPostTransform.ToString())); try { MGlobal.deleteNode(DMInterop.getDependNode(resultStr[0])); } catch { MGlobal.displayWarning("getSmothMeshEdges: unable to delete temp object"); } } } // }); } return(curveObjects); }
public static List <List <Curve> > getSmothMeshEdgesPerFace(MFnMesh mayaMesh, bool createInMaya = false) { MCommandResult ptcResult = new MCommandResult(); MCommandResult teResult = new MCommandResult(); int numPoly = mayaMesh.numPolygons; List <List <Curve> > curveObjects = new List <List <Curve> >(numPoly); MStringArray ptcResultStr = new MStringArray(); MStringArray teResultStr = new MStringArray(); MStringArray teResultStrFlat = new MStringArray(); List <Curve> tempCurveArray = null; if (createInMaya) { } else { for (int i = 0; i < numPoly; i++) { MGlobal.executeCommand($"polyListComponentConversion -te {mayaMesh.name}.f[{i}]", teResult); teResult.getResult(teResultStr); MGlobal.clearSelectionList(); foreach (var ters in teResultStr) { MGlobal.selectByName(ters, MGlobal.ListAdjustment.kAddToList); } MGlobal.executeCommand($"ls -sl -fl", teResult); teResult.getResult(teResultStrFlat); tempCurveArray = new List <Curve>((int)teResultStrFlat.length); foreach (var e in teResultStrFlat) { MGlobal.executeCommand($"polyToCurve -name deleteMe11232204332AA -form 2 -degree 3 -conformToSmoothMeshPreview 1 {e}", ptcResult); ptcResult.getResult(ptcResultStr); tempCurveArray.Add(DMCurve.CurveFromMfnNurbsCurveFromName(ptcResultStr[0], MSpace.Space.kPostTransform.ToString())); try { MGlobal.deleteNode(DMInterop.getDependNode(ptcResultStr[0])); } catch { MGlobal.displayWarning("getSmothMeshEdges: unable to delete temp object"); } } curveObjects.Add(tempCurveArray); } } return(curveObjects); }
private static void postPluginUnloadCallback(object sender, MStringArrayFunctionArgs args) { MStringArray str = args.strs; MGlobal.displayInfo("POST plugin unload callback with " + str.length + " items:"); for (int i = 0; i < str.length; i++) { MGlobal.displayInfo("\tCallback item " + i + " is : " + str[i]); } }
internal static bool createLocator(double x, double y, double z, double rx, double ry, double rz, string name) { MStringArray moveResult = new MStringArray(); MStringArray rotateResult = new MStringArray(); MGlobal.executeCommand(string.Format("spaceLocator -a -p {0} {1} {2} -n {3}", x, y, z, name), moveResult); MGlobal.executeCommand(string.Format("rotate {0} {1} {2} {3}", rx, ry, rz, name), rotateResult); return(true); }
private void computeMeshData() { foreach (MayaMesh mesh in allMeshes) { // Get the Maya mesh MFnMesh mayaMesh = new MFnMesh(mesh.mayaObjectPath); // Does the maya mesh have UVs? MStringArray uvSetNames = new MStringArray(); mayaMesh.getUVSetNames(uvSetNames); bool hasUvs = (uvSetNames.length > 0) && (mayaMesh.numUVs(uvSetNames[0]) > 0); // Iterate through all of the vertices and build the data. MItMeshFaceVertex it = new MItMeshFaceVertex(mesh.mayaObjectPath); while (!it.isDone) { // Create a new vertex and populate its data. Vertex vert = new Vertex(); // Get the local position relative to the world origin. MPoint mayaPos = it.position(MSpace.Space.kObject); vert.position = new Vector3((float)mayaPos.x, (float)mayaPos.y, (float)mayaPos.z); //vert.position = new Vector3((float)mayaPos.x - mesh.sourceXForm.mayaWorldPosition.x, //(float)mayaPos.y - mesh.sourceXForm.mayaWorldPosition.y, //(float)mayaPos.z - mesh.sourceXForm.mayaWorldPosition.z); // Get the normal. MVector mayaNrm = new MVector(); it.getNormal(mayaNrm, MSpace.Space.kObject); vert.normal = new Vector3((float)mayaNrm.x, (float)mayaNrm.y, (float)mayaNrm.z); // Texcoords. if (hasUvs && it.hasUVsProperty) { float[] mayaUvs = new float[2]; it.getUV(mayaUvs, uvSetNames[0]); vert.texcoord = new Vector2(mayaUvs[0], mayaUvs[1]); } // Append the vertex. mesh.vertices.Add(vert); it.next(); } // Get all index data. MIntArray mia1 = new MIntArray(); MIntArray mia2 = new MIntArray(); mayaMesh.getTriangleOffsets(mia1, mia2); foreach (int idx in mia2) { mesh.indices.Add((uint)idx); } } }
public static List <object> MelCommand(string MelCommand) { MStringArray stringResults = new MStringArray(); MIntArray intResults = new MIntArray(); MDoubleArray doubleResults = new MDoubleArray(); MVectorArray vectorResults = new MVectorArray(); List <object> results = new List <object>(); MCommandResult mcr = new MCommandResult(); MDagPath dag = new MDagPath(); try { MGlobal.executeCommand(MelCommand, mcr); // MGlobal.executeCommand(MelCommand, stringResults); } catch (MemberAccessException e) { MGlobal.displayWarning(e.Message); } switch (mcr.resultType) { case MCommandResult.Type.kStringArray: mcr.getResult(stringResults); results.AddRange(stringResults); break; case MCommandResult.Type.kIntArray: mcr.getResult(intResults); results.AddRange(intResults.Cast <object>()); break; case MCommandResult.Type.kDoubleArray: mcr.getResult(doubleResults); results.AddRange(doubleResults.Cast <object>()); break; case MCommandResult.Type.kVectorArray: mcr.getResult(vectorResults); results.AddRange(vectorResults.Cast <object>()); break; default: mcr.getResult(stringResults); results.AddRange(stringResults); break; } mcr.Dispose(); return(results); }
internal static bool GetBoolProperty(string property, bool defaultValue = false) { bool value = defaultValue; MCommandResult result = new MCommandResult(); MGlobal.executeCommand($"fileInfo -q \"{property}\"", result); if (result.resultType == MCommandResult.Type.kStringArray) { MStringArray stringArray = new MStringArray(); result.getResult(stringArray); value = string.Join("", stringArray.ToArray()).Equals(true.ToString()); } return(value); }
/// <summary> /// The MEL command only return name and not fullPathName. Unfortunatly you need the full path name to differentiate two nodes with the same names. /// /// </summary> /// <param name="skin">The skin cluster</param> /// <param name="transform">The transform above the mesh</param> /// <returns> /// The array with the node full path names. /// </returns> private MStringArray GetBoneFullPathName(MFnSkinCluster skin, MFnTransform transform) { int logRank = 3; // Get the bone names that influence the mesh // We need to keep this order as we will use an other mel command to get the weight influence MStringArray mayaInfluenceNames = new MStringArray(); MGlobal.executeCommand($"skinCluster -q -influence {transform.name}", mayaInfluenceNames); List <string> boneFullPathNames = new List <string>(); MPlugArray connections = new MPlugArray(); // Get the bone full path names of the skin cluster foreach (MObject node in GetInfluentNodes(skin)) { boneFullPathNames.Add((new MFnDagNode(node)).fullPathName); } // Change the name to the fullPathName. And check that they all share the same root node. string rootName = ""; for (int index = 0; index < mayaInfluenceNames.Count; index++) { string name = mayaInfluenceNames[index]; string name_substring = "|" + name; int indexFullPathName = boneFullPathNames.FindIndex(fullPathName => fullPathName.EndsWith(name_substring)); mayaInfluenceNames[index] = boneFullPathNames[indexFullPathName]; if (index == 0) { rootName = mayaInfluenceNames[index].Split('|')[1]; RaiseVerbose($"rootName: {rootName}", logRank + 1); } RaiseVerbose($"{index}: {name} => {mayaInfluenceNames[index]}", logRank + 1); if (!mayaInfluenceNames[index].StartsWith($"|{rootName}|") && !mayaInfluenceNames[index].Equals($"|{rootName}")) { RaiseError($"Bones don't share the same root node. {rootName} != {mayaInfluenceNames[index].Split('|')[1]}", logRank); return(null); } } return(mayaInfluenceNames); }
internal static bool GetUserPropString(string property, ref string value) { MCommandResult result = new MCommandResult(); MGlobal.executeCommand($"fileInfo -q \"{property}\"", result); if (result.resultType == MCommandResult.Type.kStringArray) { MStringArray stringArray = new MStringArray(); result.getResult(stringArray); value = string.Join("", stringArray.ToArray()); } else { value = null; } return(!string.IsNullOrEmpty(value)); }
//! Ensure that valid geometry is selected bool validGeometrySelected() { MSelectionList list = new MSelectionList(); MGlobal.getActiveSelectionList(list); MItSelectionList iter = new MItSelectionList(list, MFn.Type.kInvalid); for (; !iter.isDone; iter.next()) { MObject dependNode = new MObject(); iter.getDependNode(dependNode); if (dependNode.isNull || !dependNode.hasFn(MFn.Type.kTransform)) { MGlobal.displayWarning("Object in selection list is not right type of node"); return(false); } MFnDependencyNode dependNodeFn = new MFnDependencyNode(dependNode); MStringArray attributeNames = new MStringArray(); attributeNames.append("scaleX"); attributeNames.append("translateX"); int i; for (i = 0; i < attributeNames.length; i++) { MPlug plug = dependNodeFn.findPlug(attributeNames[i]); if (plug.isNull) { MGlobal.displayWarning("Object cannot be manipulated: " + dependNodeFn.name); return(false); } } } return(true); }
/// <summary> /// Write "GenerateExportersParameter" in a Maya MEL script to get the default export parameters /// </summary> /// <param name="argl"></param> /// public override void doIt(MArgList args) { ScriptExportParameters = new ExportParameters(); MStringArray result = new MStringArray(); result.append(""); result.append("babylon"); result.append(""); result.append(ScriptExportParameters.scaleFactor.ToString()); result.append(ScriptExportParameters.writeTextures.ToString()); result.append(ScriptExportParameters.overwriteTextures.ToString()); result.append(ScriptExportParameters.exportHiddenObjects.ToString()); result.append(ScriptExportParameters.exportMaterials.ToString()); result.append(ScriptExportParameters.exportOnlySelected.ToString()); result.append(ScriptExportParameters.bakeAnimationFrames.ToString()); result.append(ScriptExportParameters.optimizeAnimations.ToString()); result.append(ScriptExportParameters.optimizeVertices.ToString()); result.append(ScriptExportParameters.animgroupExportNonAnimated.ToString()); result.append(ScriptExportParameters.generateManifest.ToString()); result.append(ScriptExportParameters.autoSaveSceneFile.ToString()); result.append(ScriptExportParameters.exportTangents.ToString()); result.append(ScriptExportParameters.exportMorphTangents.ToString()); result.append(ScriptExportParameters.exportMorphNormals.ToString()); result.append(ScriptExportParameters.txtQuality.ToString()); result.append(ScriptExportParameters.mergeAOwithMR.ToString()); result.append(ScriptExportParameters.dracoCompression.ToString()); result.append(ScriptExportParameters.enableKHRLightsPunctual.ToString()); result.append(ScriptExportParameters.enableKHRTextureTransform.ToString()); result.append(ScriptExportParameters.enableKHRMaterialsUnlit.ToString()); result.append(ScriptExportParameters.pbrFull.ToString()); result.append(ScriptExportParameters.pbrNoLight.ToString()); result.append(ScriptExportParameters.createDefaultSkybox.ToString()); result.append(""); setResult(result); }
public conditionTest() { // Initialize the static members at the first time the command is used. if (conditionNames == null) { conditionNames = new MStringArray(); MConditionMessage.getConditionNames(conditionNames); uint conditionCount = conditionNames.length; MGlobal.displayInfo("netConditionTest: " + conditionCount + " conditions are defined."); conditionStates = new bool[conditionCount]; conditionCallbacks = new bool[conditionCount]; for (uint i = 0; i < conditionCount; i++) { conditionStates[i] = false; conditionCallbacks[i] = false; } } addMessage = false; delMessage = false; conditions.clear(); }
public override void doIt(MArgList args) { string fileName; MArgDatabase argData = new MArgDatabase(syntax, args); if (argData.isFlagSet(kFileNameFlag)) { fileName = argData.flagArgumentString(kFileNameFlag, 0); if (fileName != null) { string currFile = MFileIO.fileCurrentlyLoading; MStringArray pathDirectories = new MStringArray(currFile.Split('/')); if (pathDirectories.length > 0) { string expandedFileName = ""; for (int i = 0; i < pathDirectories.length-1; i++) { expandedFileName += pathDirectories[i]; expandedFileName += "/"; } expandedFileName += fileName; MGlobal.sourceFile(expandedFileName); } } } return; }
// // Write out the 'addAttr' and 'setAttr' commands for a node. // protected void writeNodeAttrs(FileStream f, MObject node, bool isSelected) { MFnDependencyNode nodeFn = new MFnDependencyNode(node); if (nodeFn.canBeWritten) { MStringArray addAttrCmds = new MStringArray(); MStringArray setAttrCmds = new MStringArray(); getAddAttrCmds(node, addAttrCmds); getSetAttrCmds(node, setAttrCmds); uint numAddAttrCmds = addAttrCmds.length; uint numSetAttrCmds = setAttrCmds.length; if (numAddAttrCmds + numSetAttrCmds > 0) { // // If the node is not already selected, then issue a command to // select it. // if (!isSelected) writeSelectNode(f, node); int i; string result = ""; for (i = 0; i < numAddAttrCmds; i++) result += (addAttrCmds[i] + "\n"); for (i = 0; i < numSetAttrCmds; i++) result += (setAttrCmds[i] + "\n"); writeString(f, result); } } }
// // Write out the "file" commands which specify the reference files used by // the scene. // protected void writeReferences(FileStream f) { MStringArray files = new MStringArray(); MFileIO.getReferences(files); uint numRefs = files.length; int i; for (i = 0; i < numRefs; i++) { string refCmd = "file -r"; string fileName = files[i]; string nsName = ""; // // For simplicity, we assume that namespaces are always used when // referencing. // string tempCmd = "file -q -ns \""; tempCmd += fileName + "\""; try { MGlobal.executeCommand(tempCmd, out nsName); refCmd += " -ns \""; refCmd += nsName + "\""; } catch (Exception) { MGlobal.displayWarning("Could not get namespace name."); } // // Is this a deferred reference? // tempCmd = "file -q -dr \""; tempCmd += fileName + "\""; int isDeferred; try { MGlobal.executeCommand(tempCmd, out isDeferred); if (Convert.ToBoolean(isDeferred)) refCmd += " -dr 1"; } catch (Exception) { MGlobal.displayWarning("Could not get deferred reference info."); } // // Get the file's reference node, if it has one. // tempCmd = "file -q -rfn \""; tempCmd += fileName + "\""; string refNode; try { MGlobal.executeCommand(tempCmd, out refNode); if (refNode.Length > 0) { refCmd += " -rfn \""; refCmd += refNode + "\""; } } catch (Exception) { MGlobal.displayInfo("Could not query reference node name."); } // // Write out the reference command. // string result = ""; result += (refCmd + "\"" + fileName + "\";"); writeString(f, result); } }
protected void getSetAttrCmds(MObject node, MStringArray cmds) { // // Get rid of any garbage already in the array. // cmds.clear(); // // Run through the node's attributes. // MFnDependencyNode nodeFn = new MFnDependencyNode(node); uint numAttrs = nodeFn.attributeCount; uint i; for (i = 0; i < numAttrs; i++) { // // Use the attribute ordering which Maya uses when doing I/O. // MObject attr = nodeFn.reorderedAttribute(i); MFnAttribute attrFn = new MFnAttribute(attr); bool isChild; attrFn.parent(out isChild); // // We don't want attributes which are children of other attributes // because they will be processed when we process the parent. // // And we only want storable attributes which accept inputs. // if (!isChild && attrFn.isStorable && attrFn.isWritable) { // // Get a plug for the attribute. // MPlug plug = new MPlug(node, attr); // // Get setAttr commands for this attribute, and any of its // children, which have had their values changed by the scene. // MStringArray newCmds = new MStringArray(); plug.getSetAttrCmds(newCmds, MPlug.MValueSelector.kChanged, false); uint numCommands = newCmds.length; int c; for (c = 0; c < numCommands; c++) { if (newCmds[c] != "") cmds.append(newCmds[c]); } } } }
// // Maya calls this method to find out if this translator is capable of // handling the given file. // public override MPxFileTranslator.MFileKind identifyFile(MFileObject file, string buffer, short bufferLen) { string tagStr = comment(fTranslatorName); int tagLen = tagStr.Length; // // If the buffer contains enough info to positively identify the file, // then use it. Otherwise we'll base the identification on the file // extension. // if (bufferLen >= tagLen) { string initialContents = buffer.Substring(0,bufferLen); MStringArray initialLines = new MStringArray(initialContents.Split('\n')); //initialLines = initialContents.Split('\n'); if (initialLines.length > 0) { if (((int)initialLines[0].Length >= tagLen) && (initialLines[0].Substring(0, tagLen-1) == tagStr)) { return MPxFileTranslator.MFileKind.kIsMyFileType; } } } else { string fileName = file.name; int fileNameLen = fileName.Length; int startOfExtension = fileName.IndexOf('.') + 1; if ((startOfExtension > 0) && (startOfExtension < fileNameLen) && (fileName.Substring(startOfExtension, fileNameLen) == fExtension)) { return MPxFileTranslator.MFileKind.kIsMyFileType; } } return MPxFileTranslator.MFileKind.kNotMyFileType; }
public override bool getSourceStreams(MObject objPath, MStringArray sourceStreams) { // No source stream needed return false; }
/// <summary> /// Get TRS and visiblity animations of the transform /// </summary> /// <param name="transform">Transform above mesh/camera/light</param> /// <returns></returns> private List <BabylonAnimation> GetAnimation(MFnTransform transform) { // Animations MPlugArray connections = new MPlugArray(); MStringArray animCurvList = new MStringArray(); MIntArray keysTime = new MIntArray(); MDoubleArray keysValue = new MDoubleArray(); MFloatArray translateValues = new MFloatArray(); MFloatArray rotateValues = new MFloatArray(); MFloatArray scaleValues = new MFloatArray(); MFloatArray visibilityValues = new MFloatArray(); MFloatArray keyTimes = new MFloatArray(); List <BabylonAnimationKey> keys = new List <BabylonAnimationKey>(); List <BabylonAnimation> animationsObject = new List <BabylonAnimation>(); //Get the animCurve MGlobal.executeCommand("listConnections -type \"animCurve\" " + transform.fullPathName + ";", animCurvList); List <AnimCurvData> animCurvesData = new List <AnimCurvData>(); foreach (String animCurv in animCurvList) { AnimCurvData animCurvData = new AnimCurvData(); animCurvesData.Add(animCurvData); animCurvData.animCurv = animCurv; //Get the key time for each curves MGlobal.executeCommand("keyframe -q " + animCurv + ";", keysTime); //Get the value for each curves MGlobal.executeCommand("keyframe - q -vc -absolute " + animCurv + ";", keysValue); if (animCurv.EndsWith("translateZ") || animCurv.EndsWith("rotateX") || animCurv.EndsWith("rotateY")) { for (int index = 0; index < keysTime.Count; index++) { // Switch coordinate system at object level animCurvData.valuePerFrame.Add(keysTime[index], (float)keysValue[index] * -1.0f); } } else { for (int index = 0; index < keysTime.Count; index++) { animCurvData.valuePerFrame.Add(keysTime[index], (float)keysValue[index]); } } } string[] mayaAnimationProperties = new string[] { "translate", "rotate", "scale" }; string[] babylonAnimationProperties = new string[] { "position", "rotationQuaternion", "scaling" }; string[] axis = new string[] { "X", "Y", "Z" }; // Init TRS default values Dictionary <string, float> defaultValues = new Dictionary <string, float>(); float[] position = null; float[] rotationQuaternion = null; float[] rotation = null; float[] scaling = null; GetTransform(transform, ref position, ref rotationQuaternion, ref rotation, ref scaling); // coordinate system already switched defaultValues.Add("translateX", position[0]); defaultValues.Add("translateY", position[1]); defaultValues.Add("translateZ", position[2]); defaultValues.Add("rotateX", rotation[0]); defaultValues.Add("rotateY", rotation[1]); defaultValues.Add("rotateZ", rotation[2]); defaultValues.Add("scaleX", scaling[0]); defaultValues.Add("scaleY", scaling[1]); defaultValues.Add("scaleZ", scaling[2]); for (int indexAnimationProperty = 0; indexAnimationProperty < mayaAnimationProperties.Length; indexAnimationProperty++) { string mayaAnimationProperty = mayaAnimationProperties[indexAnimationProperty]; // Retreive animation curves data for current animation property // Ex: all "translate" data are "translateX", "translateY", "translateZ" List <AnimCurvData> animDataProperty = animCurvesData.Where(data => data.animCurv.Contains(mayaAnimationProperty)).ToList(); if (animDataProperty.Count == 0) { // Property is not animated continue; } // Get all frames for this property List <int> framesProperty = new List <int>(); foreach (var animData in animDataProperty) { framesProperty.AddRange(animData.valuePerFrame.Keys); } framesProperty = framesProperty.Distinct().ToList(); framesProperty.Sort(); // Get default values for this property BabylonAnimationKey lastBabylonAnimationKey = new BabylonAnimationKey(); lastBabylonAnimationKey.frame = 0; lastBabylonAnimationKey.values = new float[] { defaultValues[mayaAnimationProperty + "X"], defaultValues[mayaAnimationProperty + "Y"], defaultValues[mayaAnimationProperty + "Z"] }; // Compute all values for this property List <BabylonAnimationKey> babylonAnimationKeys = new List <BabylonAnimationKey>(); foreach (var frameProperty in framesProperty) { BabylonAnimationKey babylonAnimationKey = new BabylonAnimationKey(); babylonAnimationKeys.Add(babylonAnimationKey); // Frame babylonAnimationKey.frame = frameProperty; // Values float[] valuesProperty = new float[3]; for (int indexAxis = 0; indexAxis < axis.Length; indexAxis++) { AnimCurvData animCurvDataAxis = animDataProperty.Find(data => data.animCurv.EndsWith(axis[indexAxis])); float value; if (animCurvDataAxis != null && animCurvDataAxis.valuePerFrame.ContainsKey(frameProperty)) { value = animCurvDataAxis.valuePerFrame[frameProperty]; } else { value = lastBabylonAnimationKey.values[indexAxis]; } valuesProperty[indexAxis] = value; } babylonAnimationKey.values = valuesProperty.ToArray(); // Update last known values lastBabylonAnimationKey = babylonAnimationKey; } // Convert euler to quaternion angles if (indexAnimationProperty == 1) // Rotation { foreach (var babylonAnimationKey in babylonAnimationKeys) { BabylonVector3 eulerAngles = BabylonVector3.FromArray(babylonAnimationKey.values); BabylonQuaternion quaternionAngles = eulerAngles.toQuaternion(); babylonAnimationKey.values = quaternionAngles.ToArray(); } } var keysFull = new List <BabylonAnimationKey>(babylonAnimationKeys); // Optimization OptimizeAnimations(babylonAnimationKeys, true); // Ensure animation has at least 2 frames if (IsAnimationKeysRelevant(keys)) { // Create BabylonAnimation string babylonAnimationProperty = babylonAnimationProperties[indexAnimationProperty]; animationsObject.Add(new BabylonAnimation() { dataType = indexAnimationProperty == 1 ? (int)BabylonAnimation.DataType.Quaternion : (int)BabylonAnimation.DataType.Vector3, name = babylonAnimationProperty + " animation", framePerSecond = 30, loopBehavior = (int)BabylonAnimation.LoopBehavior.Cycle, property = babylonAnimationProperty, keys = babylonAnimationKeys.ToArray(), keysFull = keysFull }); } } return(animationsObject); }
/// <summary> /// /// </summary> /// <param name="mDagPath">DAG path to the transform above mesh</param> /// <param name="babylonScene"></param> /// <returns></returns> private BabylonNode ExportMesh(MDagPath mDagPath, BabylonScene babylonScene) { RaiseMessage(mDagPath.partialPathName, 1); // Transform above mesh mFnTransform = new MFnTransform(mDagPath); // Mesh direct child of the transform // TODO get the original one rather than the modified? MFnMesh mFnMesh = null; for (uint i = 0; i < mFnTransform.childCount; i++) { MObject childObject = mFnTransform.child(i); if (childObject.apiType == MFn.Type.kMesh) { var _mFnMesh = new MFnMesh(childObject); if (!_mFnMesh.isIntermediateObject) { mFnMesh = _mFnMesh; } } } if (mFnMesh == null) { RaiseError("No mesh found has child of " + mDagPath.fullPathName); return(null); } RaiseMessage("mFnMesh.fullPathName=" + mFnMesh.fullPathName, 2); // --- prints --- #region prints Action <MFnDagNode> printMFnDagNode = (MFnDagNode mFnDagNode) => { RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.name=" + mFnDagNode.name, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.absoluteName=" + mFnDagNode.absoluteName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.fullPathName=" + mFnDagNode.fullPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.partialPathName=" + mFnDagNode.partialPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.activeColor=" + mFnDagNode.activeColor.toString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.attributeCount=" + mFnDagNode.attributeCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.childCount=" + mFnDagNode.childCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dormantColor=" + mFnDagNode.dormantColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.hasUniqueName=" + mFnDagNode.hasUniqueName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.inUnderWorld=" + mFnDagNode.inUnderWorld, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isDefaultNode=" + mFnDagNode.isDefaultNode, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanceable=" + mFnDagNode.isInstanceable, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(true)=" + mFnDagNode.isInstanced(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(false)=" + mFnDagNode.isInstanced(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced()=" + mFnDagNode.isInstanced(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(true)=" + mFnDagNode.instanceCount(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(false)=" + mFnDagNode.instanceCount(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isIntermediateObject=" + mFnDagNode.isIntermediateObject, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isShared=" + mFnDagNode.isShared, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.objectColor=" + mFnDagNode.objectColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentCount=" + mFnDagNode.parentCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentNamespace=" + mFnDagNode.parentNamespace, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.uuid().asString()=" + mFnDagNode.uuid().asString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dagRoot().apiType=" + mFnDagNode.dagRoot().apiType, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.model.equalEqual(mFnDagNode.objectProperty)=" + mFnDagNode.model.equalEqual(mFnDagNode.objectProperty), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.transformationMatrix.toString()=" + mFnDagNode.transformationMatrix.toString(), 3); }; Action <MFnMesh> printMFnMesh = (MFnMesh _mFnMesh) => { printMFnDagNode(mFnMesh); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numVertices=" + _mFnMesh.numVertices, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numEdges=" + _mFnMesh.numEdges, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numPolygons=" + _mFnMesh.numPolygons, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numFaceVertices=" + _mFnMesh.numFaceVertices, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numNormals=" + _mFnMesh.numNormals, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVSets=" + _mFnMesh.numUVSets, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVsProperty=" + _mFnMesh.numUVsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.displayColors=" + _mFnMesh.displayColors, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorSets=" + _mFnMesh.numColorSets, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorsProperty=" + _mFnMesh.numColorsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.currentUVSetName()=" + _mFnMesh.currentUVSetName(), 3); var _uvSetNames = new MStringArray(); mFnMesh.getUVSetNames(_uvSetNames); foreach (var uvSetName in _uvSetNames) { RaiseVerbose("BabylonExporter.Mesh | uvSetName=" + uvSetName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVs(uvSetName)=" + mFnMesh.numUVs(uvSetName), 4); MFloatArray us = new MFloatArray(); MFloatArray vs = new MFloatArray(); mFnMesh.getUVs(us, vs, uvSetName); RaiseVerbose("BabylonExporter.Mesh | us.Count=" + us.Count, 4); } }; Action <MFnTransform> printMFnTransform = (MFnTransform _mFnMesh) => { printMFnDagNode(mFnMesh); }; RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2); printMFnMesh(mFnMesh); RaiseVerbose("BabylonExporter.Mesh | mFnTransform data", 2); printMFnTransform(mFnTransform); Print(mFnTransform, 2, "Print ExportMesh mFnTransform"); Print(mFnMesh, 2, "Print ExportMesh mFnMesh"); //// Geometry //MIntArray triangleCounts = new MIntArray(); //MIntArray trianglesVertices = new MIntArray(); //mFnMesh.getTriangles(triangleCounts, trianglesVertices); //RaiseVerbose("BabylonExporter.Mesh | triangleCounts.ToArray()=" + triangleCounts.ToArray().toString(), 3); //RaiseVerbose("BabylonExporter.Mesh | trianglesVertices.ToArray()=" + trianglesVertices.ToArray().toString(), 3); //int[] polygonsVertexCount = new int[mFnMesh.numPolygons]; //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) //{ // polygonsVertexCount[polygonId] = mFnMesh.polygonVertexCount(polygonId); //} //RaiseVerbose("BabylonExporter.Mesh | polygonsVertexCount=" + polygonsVertexCount.toString(), 3); ////MFloatPointArray points = new MFloatPointArray(); ////mFnMesh.getPoints(points); ////RaiseVerbose("BabylonExporter.Mesh | points.ToArray()=" + points.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); ////MFloatVectorArray normals = new MFloatVectorArray(); ////mFnMesh.getNormals(normals); ////RaiseVerbose("BabylonExporter.Mesh | normals.ToArray()=" + normals.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) //{ // MIntArray verticesId = new MIntArray(); // RaiseVerbose("BabylonExporter.Mesh | polygonId=" + polygonId, 3); // int nbTriangles = triangleCounts[polygonId]; // RaiseVerbose("BabylonExporter.Mesh | nbTriangles=" + nbTriangles, 3); // for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++) // { // RaiseVerbose("BabylonExporter.Mesh | triangleIndex=" + triangleIndex, 3); // int[] triangleVertices = new int[3]; // mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices); // RaiseVerbose("BabylonExporter.Mesh | triangleVertices=" + triangleVertices.toString(), 3); // foreach (int vertexId in triangleVertices) // { // RaiseVerbose("BabylonExporter.Mesh | vertexId=" + vertexId, 3); // MPoint point = new MPoint(); // mFnMesh.getPoint(vertexId, point); // RaiseVerbose("BabylonExporter.Mesh | point=" + point.toString(), 3); // MVector normal = new MVector(); // mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal); // RaiseVerbose("BabylonExporter.Mesh | normal=" + normal.toString(), 3); // } // } //} #endregion if (IsMeshExportable(mFnMesh, mDagPath) == false) { return(null); } var babylonMesh = new BabylonMesh { name = mFnTransform.name, id = mFnTransform.uuid().asString(), visibility = Loader.GetVisibility(mFnTransform.fullPathName) }; // Instance // For a mesh with instances, we distinguish between master and instance meshes: // - a master mesh stores all the info of the mesh (transform, hierarchy, animations + vertices, indices, materials, bones...) // - an instance mesh only stores the info of the node (transform, hierarchy, animations) // Check if this mesh has already been exported as a master mesh BabylonMesh babylonMasterMesh = GetMasterMesh(mFnMesh, babylonMesh); if (babylonMasterMesh != null) { RaiseMessage($"The master mesh {babylonMasterMesh.name} was already exported. This one will be exported as an instance.", 2); // Export this node as instance var babylonInstanceMesh = new BabylonAbstractMesh { name = mFnTransform.name, id = mFnTransform.uuid().asString() }; //// Add instance to master mesh List <BabylonAbstractMesh> instances = babylonMasterMesh.instances != null?babylonMasterMesh.instances.ToList() : new List <BabylonAbstractMesh>(); instances.Add(babylonInstanceMesh); babylonMasterMesh.instances = instances.ToArray(); // Export transform / hierarchy / animations ExportNode(babylonInstanceMesh, mFnTransform, babylonScene); // Animations ExportNodeAnimation(babylonInstanceMesh, mFnTransform); return(babylonInstanceMesh); } // Position / rotation / scaling / hierarchy ExportNode(babylonMesh, mFnTransform, babylonScene); // Misc. // TODO - Retreive from Maya //babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; //babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; if (mFnMesh.numPolygons < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (mFnMesh.numVertices < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (mFnMesh.numVertices >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has more than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2); } // Animations ExportNodeAnimation(babylonMesh, mFnTransform); // Material MObjectArray shaders = new MObjectArray(); mFnMesh.getConnectedShaders(0, shaders, new MIntArray()); if (shaders.Count > 0) { List <MFnDependencyNode> materials = new List <MFnDependencyNode>(); foreach (MObject shader in shaders) { // Retreive material MFnDependencyNode shadingEngine = new MFnDependencyNode(shader); MPlug mPlugSurfaceShader = shadingEngine.findPlug("surfaceShader"); MObject materialObject = mPlugSurfaceShader.source.node; MFnDependencyNode material = new MFnDependencyNode(materialObject); materials.Add(material); } if (shaders.Count == 1) { MFnDependencyNode material = materials[0]; // Material is referenced by id babylonMesh.materialId = material.uuid().asString(); // Register material for export if not already done if (!referencedMaterials.Contains(material, new MFnDependencyNodeEqualityComparer())) { referencedMaterials.Add(material); } } else { // Create a new id for the group of sub materials string uuidMultiMaterial = GetMultimaterialUUID(materials); // Multi material is referenced by id babylonMesh.materialId = uuidMultiMaterial; // Register multi material for export if not already done if (!multiMaterials.ContainsKey(uuidMultiMaterial)) { multiMaterials.Add(uuidMultiMaterial, materials); } } } var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var uvSetNames = new MStringArray(); mFnMesh.getUVSetNames(uvSetNames); bool[] isUVExportSuccess = new bool[Math.Min(uvSetNames.Count, 2)]; for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++) { isUVExportSuccess[indexUVSet] = true; } // skin if (_exportSkin) { mFnSkinCluster = getMFnSkinCluster(mFnMesh); } int maxNbBones = 0; if (mFnSkinCluster != null) { RaiseMessage($"mFnSkinCluster.name | {mFnSkinCluster.name}", 2); Print(mFnSkinCluster, 3, $"Print {mFnSkinCluster.name}"); // Get the bones dictionary<name, index> => it represents all the bones in the skeleton indexByNodeName = GetIndexByFullPathNameDictionary(mFnSkinCluster); // Get the joint names that influence this mesh allMayaInfluenceNames = GetBoneFullPathName(mFnSkinCluster, mFnTransform); // Get the max number of joints acting on a vertex int maxNumInfluences = GetMaxInfluence(mFnSkinCluster, mFnTransform, mFnMesh); RaiseMessage($"Max influences : {maxNumInfluences}", 2); if (maxNumInfluences > 8) { RaiseWarning($"Too many bones influences per vertex: {maxNumInfluences}. Babylon.js only support up to 8 bones influences per vertex.", 2); RaiseWarning("The result may not be as expected.", 2); } maxNbBones = Math.Min(maxNumInfluences, 8); if (indexByNodeName != null && allMayaInfluenceNames != null) { babylonMesh.skeletonId = GetSkeletonIndex(mFnSkinCluster); } else { mFnSkinCluster = null; } } // Export tangents if option is checked and mesh have tangents bool isTangentExportSuccess = _exportTangents; // TODO - color, alpha //var hasColor = unskinnedMesh.NumberOfColorVerts > 0; //var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; // TODO - Add custom properties //var optimizeVertices = false; // meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); var optimizeVertices = _optimizeVertices; // global option // Compute normals var subMeshes = new List <BabylonSubMesh>(); ExtractGeometry(mFnMesh, vertices, indices, subMeshes, uvSetNames, ref isUVExportSuccess, ref isTangentExportSuccess, optimizeVertices); if (vertices.Count >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++) { string uvSetName = uvSetNames[indexUVSet]; // If at least one vertex is mapped to an UV coordinate but some have failed to be exported if (isUVExportSuccess[indexUVSet] == false && mFnMesh.numUVs(uvSetName) > 0) { RaiseWarning($"Failed to export UV set named {uvSetName}. Ensure all vertices are mapped to a UV coordinate.", 2); } } RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => v.Position).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal).ToArray(); // export the skin if (mFnSkinCluster != null) { babylonMesh.matricesWeights = vertices.SelectMany(v => v.Weights.ToArray()).ToArray(); babylonMesh.matricesIndices = vertices.Select(v => v.BonesIndices).ToArray(); babylonMesh.numBoneInfluencers = maxNbBones; if (maxNbBones > 4) { babylonMesh.matricesWeightsExtra = vertices.SelectMany(v => v.WeightsExtra != null ? v.WeightsExtra.ToArray() : new[] { 0.0f, 0.0f, 0.0f, 0.0f }).ToArray(); babylonMesh.matricesIndicesExtra = vertices.Select(v => v.BonesIndicesExtra).ToArray(); } } // Tangent if (isTangentExportSuccess) { babylonMesh.tangents = vertices.SelectMany(v => v.Tangent).ToArray(); } // Color string colorSetName; mFnMesh.getCurrentColorSetName(out colorSetName); if (mFnMesh.numColors(colorSetName) > 0) { babylonMesh.colors = vertices.SelectMany(v => v.Color).ToArray(); } // UVs if (uvSetNames.Count > 0 && isUVExportSuccess[0]) { babylonMesh.uvs = vertices.SelectMany(v => v.UV).ToArray(); } if (uvSetNames.Count > 1 && isUVExportSuccess[1]) { babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2).ToArray(); } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); babylonScene.MeshesList.Add(babylonMesh); RaiseMessage("BabylonExporter.Mesh | done", 2); return(babylonMesh); }
/// <summary> /// Faces and vertices UVs, positions and normals. /// </summary> private static void ExtractMeshGeometry(MayaM2Mesh mesh, MDagPath meshPath, Dictionary <string, MayaM2Bone> jointMap, List <MayaM2Vertex> globalVertexList) { // ***Data Tables*** // UV Sets var uvsets = new MStringArray(); var meshFunctions = new MFnMesh(meshPath); meshFunctions.getUVSetNames(uvsets); //Bone Weights var vertexWeights = new List <MDoubleArray>(); var influenceObjects = new MDagPathArray(); GetMeshWeightData(vertexWeights, influenceObjects, meshPath); //Positions var positions = new MFloatPointArray(); meshFunctions.getPoints(positions, MSpace.Space.kWorld); //Normals var normals = new MFloatVectorArray(); meshFunctions.getVertexNormals(false, normals, MSpace.Space.kWorld); var polygonIter = new MItMeshPolygon(meshPath); while (!polygonIter.isDone) { //Divide face into triangles var polyMeshRelative = new MIntArray(); polygonIter.getVertices(polyMeshRelative); int numTriangles; polygonIter.numTriangles(out numTriangles); for (var i = 0; i < numTriangles; i++) { var triangle = new MayaM2Triangle(); var triangleMeshRelative = new MIntArray(); polygonIter.getTriangle(i, new MPointArray(), triangleMeshRelative); var triangleFaceRelative = GetLocalTriangle(polyMeshRelative, triangleMeshRelative); for (var v = 0; v < 3; v++) { var meshIndex = triangleMeshRelative[v]; var faceIndex = triangleFaceRelative[v]; //Bone weights var weights = new List <Tuple <MayaM2Bone, double> >(); for (var b = 0; b < influenceObjects.length; b++) //for each joint { var kJointPath = influenceObjects[b]; if (!kJointPath.hasFn(MFn.Type.kJoint)) { continue; } Debug.Assert(b < vertexWeights[meshIndex].Count, "vertexWeights size : " + vertexWeights.Count + " " + "\njointWeights for this vertex : " + vertexWeights[meshIndex].Count); //Here are a joint&weight for this vertex if (vertexWeights[meshIndex][b] > Epsilon) { weights.Add(new Tuple <MayaM2Bone, double>(jointMap[kJointPath.fullPathName], vertexWeights[meshIndex][b])); } } //Position & normals var position = positions[(int)polygonIter.vertexIndex(faceIndex)]; var normal = normals[(int)polygonIter.normalIndex(faceIndex)]; //UV coordinates var uvCoordinates = new List <Tuple <float, float> >(); if (uvsets.length > 0 && meshFunctions.numUVs(uvsets[0]) > 0) { foreach (var uvset in uvsets) { var uvCoords = new float[2]; polygonIter.getUV(faceIndex, uvCoords, uvset); uvCoordinates.Add(new Tuple <float, float>(uvCoords[0], uvCoords[1])); } } var vert = VertexFactory.Create(position, normal, uvCoordinates, weights, globalVertexList); triangle.Vertices.Add(vert); } mesh.Faces.Add(triangle); } polygonIter.next(); } }
private static List <MayaM2Sequence> ExtractSequences() { // Sequences List <MayaM2Sequence> seqList = new List <MayaM2Sequence>(); var editorNames = new MStringArray(); MGlobal.executeCommand("ls -type m2Editor", editorNames); var editorName = editorNames[0]; MGlobal.displayInfo("Searching editor node... Result : " + editorName); if (!string.IsNullOrEmpty(editorName)) { MGlobal.displayInfo("Editor data found."); int numberOfClips; MGlobal.executeCommand("getAttr -size " + editorName + ".animClips", out numberOfClips); MGlobal.displayInfo("\tExtracting " + numberOfClips + " clips."); for (var i = 0; i < numberOfClips; i++) { int start; int end; int type; bool looping; bool lowPriority; int repMin; int repMax; bool blending; int blendTimeStart; int blendTimeEnd; double rarity; M2EditorNode.GetClipAttribute(editorName, i, "animClipStart", out start); M2EditorNode.GetClipAttribute(editorName, i, "animClipEnd", out end); M2EditorNode.GetClipAttribute(editorName, i, "animClipType", out type); M2EditorNode.GetClipAttribute(editorName, i, "animClipLooping", out looping); M2EditorNode.GetClipAttribute(editorName, i, "animClipLowPriority", out lowPriority); M2EditorNode.GetClipAttribute(editorName, i, "animClipRepMin", out repMin); M2EditorNode.GetClipAttribute(editorName, i, "animClipRepMax", out repMax); M2EditorNode.GetClipAttribute(editorName, i, "animClipBlending", out blending); M2EditorNode.GetClipAttribute(editorName, i, "animClipBlendTimeStart", out blendTimeStart); M2EditorNode.GetClipAttribute(editorName, i, "animClipBlendTimeEnd", out blendTimeEnd); M2EditorNode.GetClipAttribute(editorName, i, "animClipRarity", out rarity); var mayaSeq = new MayaM2Sequence { Start = start, End = end, Type = type, IsLoop = looping, IsLowPriority = lowPriority, MinimumRepetitions = repMin, MaximumRepetitions = repMax, IsBlending = blending, BlendTimeStart = blendTimeStart, BlendTimeEnd = blendTimeEnd, Probability = (short)(rarity * short.MaxValue) }; seqList.Add(mayaSeq); } } //Default when no animation clip, tries to get slider range. if (seqList.Count != 0) { var mayaSeq = new MayaM2Sequence { Start = (int)MAnimControl.minTime.asUnits(MTime.Unit.kMilliseconds), End = (int)MAnimControl.maxTime.asUnits(MTime.Unit.kMilliseconds), }; if (mayaSeq.Start != mayaSeq.End) { mayaSeq.IsLoop = true; } seqList.Add(mayaSeq); } return(seqList); }
public static void draw(MDrawContext context, MUserData userData) { // This function is called by maya internal, .Net SDK has transfered MUserData to the derived one // Users don't need do the MUserData.getData(oldData) by themselves FootPrintData footData = MUserData.getData(userData) as FootPrintData; if (footData == null) { return; } MDAGDrawOverrideInfo objectOverrideInfo = footData.fDrawOV; if (objectOverrideInfo.fOverrideEnabled && !objectOverrideInfo.fEnableVisible) { return; } uint displayStyle = context.getDisplayStyle(); bool drawAsBoundingBox = (displayStyle & (uint)MFrameContext.DisplayStyle.kBoundingBox) != 0 || (footData.fDrawOV.fLOD == MDAGDrawOverrideInfo.DrawOverrideLOD.kLODBoundingBox); if (drawAsBoundingBox && !footData.fCustomBoxDraw) { return; } // get renderer Autodesk.Maya.OpenMayaRender.MHWRender.MRenderer theRenderer = Autodesk.Maya.OpenMayaRender.MHWRender.MRenderer.theRenderer(); if (theRenderer == null) { return; } // get state data MMatrix transform = context.getMatrix(MDrawContext.MatrixType.kWorldViewMtx); MMatrix projection = context.getMatrix(MDrawContext.MatrixType.kProjectionMtx); // Check to see if we are drawing in a shadow pass. // If so then we keep the shading simple which in this // example means to disable any extra blending state changes // MPassContext passCtx = context.passContext; MStringArray passSem = passCtx.passSemantics; bool castingShadows = false; for (int i = 0; i < passSem.length; i++) { if (passSem[i] == MPassContext.kShadowPassSemantic) { castingShadows = true; } } bool debugPassInformation = false; if (debugPassInformation) { string passId = passCtx.passIdentifier; MGlobal.displayInfo("footprint node drawing in pass[" + passId + "], semantic["); for (int i = 0; i < passSem.length; i++) { MGlobal.displayInfo(passSem[i]); } MGlobal.displayInfo("\n"); } // get cached data float multiplier = footData.fMultiplier; float[] color = new float[4] { footData.fColor[0], footData.fColor[1], footData.fColor[2], 1.0f }; bool requireBlending = false; // If we're not casting shadows then do extra work // for display styles if (!castingShadows) { // Use some monotone version of color to show "default material mode" // if ((displayStyle & (uint)MFrameContext.DisplayStyle.kDefaultMaterial) != 0) { color[0] = color[1] = color[2] = (color[0] + color[1] + color[2]) / 3.0f; } // Do some alpha blending if in x-ray mode // else if ((displayStyle & (uint)MFrameContext.DisplayStyle.kXray) != 0) { requireBlending = true; color[3] = 0.3f; } } // Set blend and raster state // MStateManager stateMgr = context.stateManager; MBlendState pOldBlendState = null; MRasterizerState pOldRasterState = null; bool rasterStateModified = false; if ((stateMgr != null) && ((displayStyle & (uint)MFrameContext.DisplayStyle.kGouraudShaded) != 0)) { // draw filled, and with blending if required if (requireBlending) { if (blendState == null) { MBlendStateDesc desc = new MBlendStateDesc(); desc.targetBlends.blendEnable = true; desc.targetBlends.destinationBlend = MBlendState.BlendOption.kInvSourceAlpha; desc.targetBlends.alphaDestinationBlend = MBlendState.BlendOption.kInvSourceAlpha; blendState = MStateManager.acquireBlendState(desc); } if (blendState != null) { pOldBlendState = stateMgr.blendState; stateMgr.blendState = blendState; } } // Override culling mode since we always want double-sided // pOldRasterState = (stateMgr != null) ? stateMgr.rasterizerState : null; if (pOldRasterState != null) { MRasterizerStateDesc desc = new MRasterizerStateDesc(pOldRasterState.desc); // It's also possible to change this to kCullFront or kCullBack if we // wanted to set it to that. MRasterizerState.CullMode cullMode = MRasterizerState.CullMode.kCullNone; if (desc.cullMode != cullMode) { if (rasterState != null) { // Just override the cullmode desc.cullMode = cullMode; rasterState = MStateManager.acquireRasterizerState(desc); } if (rasterState == null) { rasterStateModified = true; stateMgr.rasterizerState = rasterState; } } } } //======================== // Start the draw work //======================== // Prepare draw agent, default using OpenGL FootPrintDrawAgentGL drawAgentRef = FootPrintDrawAgentGL.getDrawAgent(); FootPrintDrawAgent drawAgentPtr = drawAgentRef; if (drawAgentPtr != null) { // Set color drawAgentPtr.setColor(new MColor(color[0], color[1], color[2], color[3])); // Set matrix drawAgentPtr.setMatrix(transform, projection); drawAgentPtr.beginDraw(); if (drawAsBoundingBox) { // If it is in bounding bode, draw only bounding box wireframe, nothing else MPoint min = footData.fCurrentBoundingBox.min; MPoint max = footData.fCurrentBoundingBox.max; drawAgentPtr.drawBoundingBox(min, max); } else { // Templated, only draw wirefame and it is not selectale bool overideTemplated = objectOverrideInfo.fOverrideEnabled && (objectOverrideInfo.fDisplayType == MDAGDrawOverrideInfo.DrawOverrideDisplayType.kDisplayTypeTemplate); // Override no shaded, only show wireframe bool overrideNoShaded = objectOverrideInfo.fOverrideEnabled && !objectOverrideInfo.fEnableShading; if (overideTemplated || overrideNoShaded) { drawAgentPtr.drawWireframe(multiplier); } else { if (((displayStyle & (uint)MFrameContext.DisplayStyle.kGouraudShaded) != 0) || ((displayStyle & (uint)MFrameContext.DisplayStyle.kTextured)) != 0) { drawAgentPtr.drawShaded(multiplier); } if ((displayStyle & (uint)MFrameContext.DisplayStyle.kWireFrame) != 0) { drawAgentPtr.drawWireframe(multiplier); } } } drawAgentPtr.endDraw(); } //======================== // End the draw work //======================== // Restore old blend state and old raster state if ((stateMgr != null) && ((displayStyle & (uint)MFrameContext.DisplayStyle.kGouraudShaded) != 0)) { if ((stateMgr != null) && (pOldBlendState != null)) { stateMgr.setBlendState(pOldBlendState); } if (rasterStateModified && (pOldBlendState != null)) { stateMgr.setRasterizerState(pOldRasterState); } } }
/// <summary> /// Extract geometry (position, normal, UVs...) for a specific vertex /// </summary> /// <param name="mFnMesh"></param> /// <param name="polygonId">The polygon (face) to examine</param> /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param> /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param> /// <param name="uvSetNames"></param> /// <param name="isUVExportSuccess"></param> /// <returns></returns> private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, MStringArray uvSetNames, ref bool[] isUVExportSuccess) { MPoint point = new MPoint(); mFnMesh.getPoint(vertexIndexGlobal, point); MVector normal = new MVector(); mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal); MVector tangent = new MVector(); mFnMesh.getFaceVertexTangent(polygonId, vertexIndexGlobal, tangent); // Switch coordinate system at object level point.z *= -1; normal.z *= -1; tangent.z *= -1; float[] tangentVec4 = new float[] { (float)tangent.x, (float)tangent.y, (float)tangent.z, -1 }; var vertex = new GlobalVertex { BaseIndex = vertexIndexGlobal, Position = point.toArray(), Normal = normal.toArray(), Tangent = tangentVec4, }; // Color int colorIndex; string colorSetName; float[] defaultColor = new float[] { 1, 1, 1, 0 }; MColor color = new MColor(); mFnMesh.getCurrentColorSetName(out colorSetName); if (mFnMesh.numColors(colorSetName) > 0) { //Get the color index mFnMesh.getColorIndex(polygonId, vertexIndexLocal, out colorIndex); //if a color is set if (colorIndex != -1) { mFnMesh.getColor(colorIndex, color); vertex.Color = color.toArray(); } //else set the default color else { vertex.Color = defaultColor; } } // UV int indexUVSet = 0; if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet]) { try { float u = 0, v = 0; mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]); vertex.UV = new float[] { u, v }; } catch (Exception e) { // An exception is raised when a vertex isn't mapped to an UV coordinate isUVExportSuccess[indexUVSet] = false; } } indexUVSet = 1; if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet]) { try { float u = 0, v = 0; mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]); vertex.UV2 = new float[] { u, v }; } catch (Exception e) { // An exception is raised when a vertex isn't mapped to an UV coordinate isUVExportSuccess[indexUVSet] = false; } } return(vertex); }
public override bool getAvailableImages( ShaderContext context, string uvSetName, MStringArray imageNames) { MGlobal.displayInfo("testHLSLShader::getAvailableImages"); return base.getAvailableImages(context, uvSetName, imageNames); }
/// <summary> /// /// </summary> /// <param name="mDagPath">DAG path to the transform above light</param> /// <param name="babylonScene"></param> /// <returns></returns> private BabylonNode ExportLight(MDagPath mDagPath, BabylonScene babylonScene) { RaiseMessage(mDagPath.partialPathName, 1); // Transform above light MFnTransform mFnTransform = new MFnTransform(mDagPath); // Light direct child of the transform MFnLight mFnLight = null; for (uint i = 0; i < mFnTransform.childCount; i++) { MObject childObject = mFnTransform.child(i); if (childObject.hasFn(MFn.Type.kLight)) { var _mFnLight = new MFnLight(childObject); if (!_mFnLight.isIntermediateObject) { mFnLight = _mFnLight; } } } if (mFnLight == null) { RaiseError("No light found has child of " + mDagPath.fullPathName); return(null); } RaiseMessage("mFnLight.fullPathName=" + mFnLight.fullPathName, 2); // --- prints --- #region prints // MFnLight RaiseVerbose("BabylonExporter.Light | mFnLight data", 2); RaiseVerbose("BabylonExporter.Light | mFnLight.color.toString()=" + mFnLight.color.toString(), 3); RaiseVerbose("BabylonExporter.Light | mFnLight.intensity=" + mFnLight.intensity, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.useRayTraceShadows=" + mFnLight.useRayTraceShadows, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.shadowColor.toString()=" + mFnLight.shadowColor.toString(), 3); RaiseVerbose("BabylonExporter.Light | mFnLight.centerOfIllumination=" + mFnLight.centerOfIllumination, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.numShadowSamples=" + mFnLight.numShadowSamples, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.rayDepthLimit=" + mFnLight.rayDepthLimit, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.opticalFXvisibility.toString()=" + mFnLight.opticalFXvisibility.toString(), 3); RaiseVerbose("BabylonExporter.Light | mFnLight.lightIntensity.toString()=" + mFnLight.lightIntensity.toString(), 3); RaiseVerbose("BabylonExporter.Light | mFnLight.instanceCount(true)=" + mFnLight.instanceCount(true), 3); RaiseVerbose("BabylonExporter.Light | mFnLight.lightDirection(0).toString()=" + mFnLight.lightDirection(0).toString(), 3); RaiseVerbose("BabylonExporter.Light | mFnLight.lightAmbient=" + mFnLight.lightAmbient, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.lightDiffuse=" + mFnLight.lightDiffuse, 3); RaiseVerbose("BabylonExporter.Light | mFnLight.lightSpecular=" + mFnLight.lightSpecular, 3); switch (mFnLight.objectProperty.apiType) { case MFn.Type.kSpotLight: MFnSpotLight mFnSpotLight = new MFnSpotLight(mFnLight.objectProperty); // MFnNonAmbientLight RaiseVerbose("BabylonExporter.Light | mFnSpotLight.decayRate=" + mFnSpotLight.decayRate, 3); // dropdown enum value // MFnNonExtendedLight RaiseVerbose("BabylonExporter.Light | mFnSpotLight.shadowRadius=" + mFnSpotLight.shadowRadius, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.castSoftShadows=" + mFnSpotLight.castSoftShadows, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.useDepthMapShadows=" + mFnSpotLight.useDepthMapShadows, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.depthMapFilterSize()=" + mFnSpotLight.depthMapFilterSize(), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.depthMapResolution()=" + mFnSpotLight.depthMapResolution(), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.depthMapBias()=" + mFnSpotLight.depthMapBias(), 3); // MFnSpotLight RaiseVerbose("BabylonExporter.Light | mFnSpotLight.coneAngle=" + mFnSpotLight.coneAngle, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.penumbraAngle=" + mFnSpotLight.penumbraAngle, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.dropOff=" + mFnSpotLight.dropOff, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.barnDoors=" + mFnSpotLight.barnDoors, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.useDecayRegions=" + mFnSpotLight.useDecayRegions, 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kFirst)=" + mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kFirst), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kFirst)=" + mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kFirst), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kSecond)=" + mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kSecond), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kSecond)=" + mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kSecond), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kThird)=" + mFnSpotLight.startDistance(MFnSpotLight.MDecayRegion.kThird), 3); RaiseVerbose("BabylonExporter.Light | mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kThird)=" + mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kThird), 3); break; } Print(mFnTransform, 2, "Print ExportLight mFnTransform"); Print(mFnLight, 2, "Print ExportLight mFnLight"); #endregion if (IsLightExportable(mFnLight, mDagPath) == false) { return(null); } var babylonLight = new BabylonLight { name = mFnTransform.name, id = mFnTransform.uuid().asString() }; // Hierarchy ExportHierarchy(babylonLight, mFnTransform); // User custom attributes babylonLight.metadata = ExportCustomAttributeFromTransform(mFnTransform); // Position //RaiseVerbose("BabylonExporter.Light | ExportTransform", 2); float[] position = null; GetTransform(mFnTransform, ref position); babylonLight.position = position; // Direction var vDir = new MVector(0, 0, -1); var transformationMatrix = new MTransformationMatrix(mFnTransform.transformationMatrix); vDir = vDir.multiply(transformationMatrix.asMatrixProperty); vDir.normalize(); babylonLight.direction = new[] { (float)vDir.x, (float)vDir.y, -(float)vDir.z }; // Common fields babylonLight.intensity = mFnLight.intensity; babylonLight.diffuse = mFnLight.lightDiffuse ? mFnLight.color.toArrayRGB() : new float[] { 0, 0, 0 }; babylonLight.specular = mFnLight.lightSpecular ? mFnLight.color.toArrayRGB() : new float[] { 0, 0, 0 }; // Type switch (mFnLight.objectProperty.apiType) { case MFn.Type.kPointLight: babylonLight.type = 0; break; case MFn.Type.kSpotLight: MFnSpotLight mFnSpotLight = new MFnSpotLight(mFnLight.objectProperty); babylonLight.type = 2; babylonLight.angle = (float)mFnSpotLight.coneAngle; babylonLight.exponent = 1; if (mFnSpotLight.useDecayRegions) { babylonLight.range = mFnSpotLight.endDistance(MFnSpotLight.MDecayRegion.kThird); // Max distance } break; case MFn.Type.kDirectionalLight: babylonLight.type = 1; break; case MFn.Type.kAmbientLight: babylonLight.type = 3; babylonLight.groundColor = new float[] { 0, 0, 0 }; // No emit diffuse /specular checkbox for ambient light babylonLight.diffuse = mFnLight.color.toArrayRGB(); babylonLight.specular = babylonLight.diffuse; // Direction vDir = new MVector(0, 1, 0); transformationMatrix = new MTransformationMatrix(mFnTransform.transformationMatrix); vDir = vDir.multiply(transformationMatrix.asMatrixProperty); vDir.normalize(); babylonLight.direction = new[] { (float)vDir.x, (float)vDir.y, -(float)vDir.z }; break; case MFn.Type.kAreaLight: case MFn.Type.kVolumeLight: RaiseError("Unsupported light type '" + mFnLight.objectProperty.apiType + "' for DAG path '" + mFnLight.fullPathName + "'. Light is ignored. Supported light types are: ambient, directional, point and spot.", 1); return(null); default: RaiseWarning("Unknown light type '" + mFnLight.objectProperty.apiType + "' for DAG path '" + mFnLight.fullPathName + "'. Light is ignored.", 1); return(null); } // TODO - Shadows //Variable declaration MStringArray enlightedMeshesFullPathNames = new MStringArray(); List <string> includeMeshesIds = new List <string>(); MStringArray kTransMesh = new MStringArray(); String typeMesh = null; MStringArray UUIDMesh = new MStringArray(); //MEL Command that get the enlighted mesh for a given light MGlobal.executeCommand($@"lightlink -query -light {mFnTransform.fullPathName};", enlightedMeshesFullPathNames); //For each enlighted mesh foreach (String Mesh in enlightedMeshesFullPathNames) { //MEL Command use to get the type of each mesh typeMesh = MGlobal.executeCommandStringResult($@"nodeType -api {Mesh};"); //We are targeting the type kMesh and not kTransform (for parenting) if (typeMesh == "kMesh") { MGlobal.executeCommand($@"listRelatives -parent -fullPath {Mesh};", kTransMesh); //And finally the MEL Command for the uuid of each mesh MGlobal.executeCommand($@"ls -uuid {kTransMesh[0]};", UUIDMesh); includeMeshesIds.Add(UUIDMesh[0]); } } babylonLight.includedOnlyMeshesIds = includeMeshesIds.ToArray(); // Animations if (exportParameters.bakeAnimationFrames) { ExportNodeAnimationFrameByFrame(babylonLight, mFnTransform); } else { ExportNodeAnimation(babylonLight, mFnTransform); } babylonScene.LightsList.Add(babylonLight); return(babylonLight); }
private void parseArgs(MArgList args) { const string kMessageFlag = "m"; MArgDatabase argData = new MArgDatabase(syntax, args); if (argData.isFlagSet(kMessageFlag)) { bool flag = false; try { flag = argData.flagArgumentBool(kMessageFlag, 0); } catch (Exception) { throw new ArgumentException("could not parse message flag", "args"); } if (flag) { addMessage = true; } else { delMessage = true; } } try { argData.getObjects(conditions); } catch(Exception) { displayError("could not parse condition names"); } // If there are no conditions specified, operate on all of them // if (conditions.length == 0) { // conditionNames is set in initializePlugin to all the // currently available condition names. // conditions = conditionNames; } }
/// <summary> /// /// </summary> /// <param name="mDagPath">DAG path to the transform above mesh</param> /// <param name="babylonScene"></param> /// <returns></returns> private BabylonNode ExportMesh(MDagPath mDagPath, BabylonScene babylonScene) { RaiseMessage(mDagPath.partialPathName, 1); // Transform above mesh MFnTransform mFnTransform = new MFnTransform(mDagPath); // Mesh direct child of the transform MFnMesh mFnMesh = null; for (uint i = 0; i < mFnTransform.childCount; i++) { MObject childObject = mFnTransform.child(i); if (childObject.apiType == MFn.Type.kMesh) { var _mFnMesh = new MFnMesh(childObject); if (!_mFnMesh.isIntermediateObject) { mFnMesh = _mFnMesh; } } } if (mFnMesh == null) { RaiseError("No mesh found has child of " + mDagPath.fullPathName); return(null); } RaiseMessage("mFnMesh.fullPathName=" + mFnMesh.fullPathName, 2); // --- prints --- #region prints Action <MFnDagNode> printMFnDagNode = (MFnDagNode mFnDagNode) => { RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.name=" + mFnDagNode.name, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.absoluteName=" + mFnDagNode.absoluteName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.fullPathName=" + mFnDagNode.fullPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.partialPathName=" + mFnDagNode.partialPathName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.activeColor=" + mFnDagNode.activeColor.toString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.attributeCount=" + mFnDagNode.attributeCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.childCount=" + mFnDagNode.childCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dormantColor=" + mFnDagNode.dormantColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.hasUniqueName=" + mFnDagNode.hasUniqueName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.inUnderWorld=" + mFnDagNode.inUnderWorld, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isDefaultNode=" + mFnDagNode.isDefaultNode, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanceable=" + mFnDagNode.isInstanceable, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(true)=" + mFnDagNode.isInstanced(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced(false)=" + mFnDagNode.isInstanced(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isInstanced()=" + mFnDagNode.isInstanced(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(true)=" + mFnDagNode.instanceCount(true), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.instanceCount(false)=" + mFnDagNode.instanceCount(false), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isIntermediateObject=" + mFnDagNode.isIntermediateObject, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.isShared=" + mFnDagNode.isShared, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.objectColor=" + mFnDagNode.objectColor, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentCount=" + mFnDagNode.parentCount, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.parentNamespace=" + mFnDagNode.parentNamespace, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.uuid().asString()=" + mFnDagNode.uuid().asString(), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.dagRoot().apiType=" + mFnDagNode.dagRoot().apiType, 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.model.equalEqual(mFnDagNode.objectProperty)=" + mFnDagNode.model.equalEqual(mFnDagNode.objectProperty), 3); RaiseVerbose("BabylonExporter.Mesh | mFnDagNode.transformationMatrix.toString()=" + mFnDagNode.transformationMatrix.toString(), 3); }; Action <MFnMesh> printMFnMesh = (MFnMesh _mFnMesh) => { printMFnDagNode(mFnMesh); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numVertices=" + _mFnMesh.numVertices, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numEdges=" + _mFnMesh.numEdges, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numPolygons=" + _mFnMesh.numPolygons, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numFaceVertices=" + _mFnMesh.numFaceVertices, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numNormals=" + _mFnMesh.numNormals, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVSets=" + _mFnMesh.numUVSets, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numUVsProperty=" + _mFnMesh.numUVsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.displayColors=" + _mFnMesh.displayColors, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorSets=" + _mFnMesh.numColorSets, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.numColorsProperty=" + _mFnMesh.numColorsProperty, 3); RaiseVerbose("BabylonExporter.Mesh | _mFnMesh.currentUVSetName()=" + _mFnMesh.currentUVSetName(), 3); var _uvSetNames = new MStringArray(); mFnMesh.getUVSetNames(_uvSetNames); foreach (var uvSetName in _uvSetNames) { RaiseVerbose("BabylonExporter.Mesh | uvSetName=" + uvSetName, 3); RaiseVerbose("BabylonExporter.Mesh | mFnMesh.numUVs(uvSetName)=" + mFnMesh.numUVs(uvSetName), 4); MFloatArray us = new MFloatArray(); MFloatArray vs = new MFloatArray(); mFnMesh.getUVs(us, vs, uvSetName); RaiseVerbose("BabylonExporter.Mesh | us.Count=" + us.Count, 4); } }; Action <MFnTransform> printMFnTransform = (MFnTransform _mFnMesh) => { printMFnDagNode(mFnMesh); }; RaiseVerbose("BabylonExporter.Mesh | mFnMesh data", 2); printMFnMesh(mFnMesh); RaiseVerbose("BabylonExporter.Mesh | mFnTransform data", 2); printMFnTransform(mFnTransform); Print(mFnTransform, 2, "Print ExportMesh mFnTransform"); Print(mFnMesh, 2, "Print ExportMesh mFnMesh"); //// Geometry //MIntArray triangleCounts = new MIntArray(); //MIntArray trianglesVertices = new MIntArray(); //mFnMesh.getTriangles(triangleCounts, trianglesVertices); //RaiseVerbose("BabylonExporter.Mesh | triangleCounts.ToArray()=" + triangleCounts.ToArray().toString(), 3); //RaiseVerbose("BabylonExporter.Mesh | trianglesVertices.ToArray()=" + trianglesVertices.ToArray().toString(), 3); //int[] polygonsVertexCount = new int[mFnMesh.numPolygons]; //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) //{ // polygonsVertexCount[polygonId] = mFnMesh.polygonVertexCount(polygonId); //} //RaiseVerbose("BabylonExporter.Mesh | polygonsVertexCount=" + polygonsVertexCount.toString(), 3); ////MFloatPointArray points = new MFloatPointArray(); ////mFnMesh.getPoints(points); ////RaiseVerbose("BabylonExporter.Mesh | points.ToArray()=" + points.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); ////MFloatVectorArray normals = new MFloatVectorArray(); ////mFnMesh.getNormals(normals); ////RaiseVerbose("BabylonExporter.Mesh | normals.ToArray()=" + normals.ToArray().Select(mFloatPoint => mFloatPoint.toString()), 3); //for (int polygonId = 0; polygonId < mFnMesh.numPolygons; polygonId++) //{ // MIntArray verticesId = new MIntArray(); // RaiseVerbose("BabylonExporter.Mesh | polygonId=" + polygonId, 3); // int nbTriangles = triangleCounts[polygonId]; // RaiseVerbose("BabylonExporter.Mesh | nbTriangles=" + nbTriangles, 3); // for (int triangleIndex = 0; triangleIndex < triangleCounts[polygonId]; triangleIndex++) // { // RaiseVerbose("BabylonExporter.Mesh | triangleIndex=" + triangleIndex, 3); // int[] triangleVertices = new int[3]; // mFnMesh.getPolygonTriangleVertices(polygonId, triangleIndex, triangleVertices); // RaiseVerbose("BabylonExporter.Mesh | triangleVertices=" + triangleVertices.toString(), 3); // foreach (int vertexId in triangleVertices) // { // RaiseVerbose("BabylonExporter.Mesh | vertexId=" + vertexId, 3); // MPoint point = new MPoint(); // mFnMesh.getPoint(vertexId, point); // RaiseVerbose("BabylonExporter.Mesh | point=" + point.toString(), 3); // MVector normal = new MVector(); // mFnMesh.getFaceVertexNormal(polygonId, vertexId, normal); // RaiseVerbose("BabylonExporter.Mesh | normal=" + normal.toString(), 3); // } // } //} #endregion if (IsMeshExportable(mFnMesh, mDagPath) == false) { return(null); } var babylonMesh = new BabylonMesh { name = mFnTransform.name, id = mFnTransform.uuid().asString() }; // Position / rotation / scaling / hierarchy ExportNode(babylonMesh, mFnTransform, babylonScene); // Misc. // TODO - Retreive from Maya // TODO - What is the difference between isVisible and visibility? // TODO - Fix fatal error: Attempting to save in C:/Users/Fabrice/AppData/Local/Temp/Fabrice.20171205.1613.ma //babylonMesh.isVisible = mDagPath.isVisible; //babylonMesh.visibility = meshNode.MaxNode.GetVisibility(0, Tools.Forever); //babylonMesh.receiveShadows = meshNode.MaxNode.RcvShadows == 1; //babylonMesh.applyFog = meshNode.MaxNode.ApplyAtmospherics == 1; if (mFnMesh.numPolygons < 1) { RaiseError($"Mesh {babylonMesh.name} has no face", 2); } if (mFnMesh.numVertices < 3) { RaiseError($"Mesh {babylonMesh.name} has not enough vertices", 2); } if (mFnMesh.numVertices >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has more than 65536 vertices which means that it will require specific WebGL extension to be rendered. This may impact portability of your scene on low end devices.", 2); } // Material MObjectArray shaders = new MObjectArray(); mFnMesh.getConnectedShaders(0, shaders, new MIntArray()); if (shaders.Count > 0) { List <MFnDependencyNode> materials = new List <MFnDependencyNode>(); foreach (MObject shader in shaders) { // Retreive material MFnDependencyNode shadingEngine = new MFnDependencyNode(shader); MPlug mPlugSurfaceShader = shadingEngine.findPlug("surfaceShader"); MObject materialObject = mPlugSurfaceShader.source.node; MFnDependencyNode material = new MFnDependencyNode(materialObject); materials.Add(material); } if (shaders.Count == 1) { MFnDependencyNode material = materials[0]; // Material is referenced by id babylonMesh.materialId = material.uuid().asString(); // Register material for export if not already done if (!referencedMaterials.Contains(material, new MFnDependencyNodeEqualityComparer())) { referencedMaterials.Add(material); } } else { // Create a new id for the group of sub materials string uuidMultiMaterial = GetMultimaterialUUID(materials); // Multi material is referenced by id babylonMesh.materialId = uuidMultiMaterial; // Register multi material for export if not already done if (!multiMaterials.ContainsKey(uuidMultiMaterial)) { multiMaterials.Add(uuidMultiMaterial, materials); } } } var vertices = new List <GlobalVertex>(); var indices = new List <int>(); var uvSetNames = new MStringArray(); mFnMesh.getUVSetNames(uvSetNames); bool[] isUVExportSuccess = new bool[Math.Min(uvSetNames.Count, 2)]; for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++) { isUVExportSuccess[indexUVSet] = true; } // TODO - color, alpha //var hasColor = unskinnedMesh.NumberOfColorVerts > 0; //var hasAlpha = unskinnedMesh.GetNumberOfMapVerts(-2) > 0; // TODO - Add custom properties var optimizeVertices = false; // meshNode.MaxNode.GetBoolProperty("babylonjs_optimizevertices"); // Compute normals var subMeshes = new List <BabylonSubMesh>(); ExtractGeometry(mFnMesh, vertices, indices, subMeshes, uvSetNames, ref isUVExportSuccess, optimizeVertices); if (vertices.Count >= 65536) { RaiseWarning($"Mesh {babylonMesh.name} has {vertices.Count} vertices. This may prevent your scene to work on low end devices where 32 bits indice are not supported", 2); if (!optimizeVertices) { RaiseError("You can try to optimize your object using [Try to optimize vertices] option", 2); } } for (int indexUVSet = 0; indexUVSet < isUVExportSuccess.Length; indexUVSet++) { string uvSetName = uvSetNames[indexUVSet]; // If at least one vertex is mapped to an UV coordinate but some have failed to be exported if (isUVExportSuccess[indexUVSet] == false && mFnMesh.numUVs(uvSetName) > 0) { RaiseWarning($"Failed to export UV set named {uvSetName}. Ensure all vertices are mapped to a UV coordinate.", 2); } } RaiseMessage($"{vertices.Count} vertices, {indices.Count / 3} faces", 2); // Buffers babylonMesh.positions = vertices.SelectMany(v => v.Position).ToArray(); babylonMesh.normals = vertices.SelectMany(v => v.Normal).ToArray(); // TODO - Export colors ? //babylonMesh.colors = vertices.SelectMany(v => v.Color).ToArray(); if (uvSetNames.Count > 0 && isUVExportSuccess[0]) { babylonMesh.uvs = vertices.SelectMany(v => v.UV).ToArray(); } if (uvSetNames.Count > 1 && isUVExportSuccess[1]) { babylonMesh.uvs2 = vertices.SelectMany(v => v.UV2).ToArray(); } babylonMesh.subMeshes = subMeshes.ToArray(); // Buffers - Indices babylonMesh.indices = indices.ToArray(); babylonScene.MeshesList.Add(babylonMesh); RaiseMessage("BabylonExporter.Mesh | done", 2); return(babylonMesh); }
protected void getAddAttrCmds(MObject node, MStringArray cmds) { // // Run through the node's attributes. // MFnDependencyNode nodeFn = new MFnDependencyNode(node); uint numAttrs = nodeFn.attributeCount; uint i; for (i = 0; i < numAttrs; i++) { // // Use the attribute ordering which Maya uses when doing I/O. // MObject attr = nodeFn.reorderedAttribute(i); // // If this attribute has been added since the node was created, // then we may want to write out an addAttr statement for it. // if (nodeFn.isNewAttribute(attr)) { MFnAttribute attrFn = new MFnAttribute(attr); // // If the attribute has a parent then ignore it because it will // be processed when we process the parent. // bool bFound; attrFn.parent(out bFound); if ( !bFound ) { // // If the attribute is a compound, then we can do its entire // tree at once. // try { MFnCompoundAttribute cAttrFn = new MFnCompoundAttribute(attr); MStringArray newCmds = new MStringArray(); cAttrFn.getAddAttrCmds(newCmds); uint numCommands = newCmds.length; int c; for (c = 0; c < numCommands; c++) { if (newCmds[c] != "") cmds.append(newCmds[c]); } } catch (Exception) { string newCmd = attrFn.getAddAttrCmd(); if (newCmd != "") cmds.append(newCmd); } } } } }
private void computeMeshData() { foreach( MayaMesh mesh in allMeshes ) { // Get the Maya mesh MFnMesh mayaMesh = new MFnMesh(mesh.mayaObjectPath); // Does the maya mesh have UVs? MStringArray uvSetNames = new MStringArray(); mayaMesh.getUVSetNames(uvSetNames); bool hasUvs = (uvSetNames.length > 0) && (mayaMesh.numUVs(uvSetNames[0]) > 0); // Iterate through all of the vertices and build the data. MItMeshFaceVertex it = new MItMeshFaceVertex(mesh.mayaObjectPath); while( !it.isDone ) { // Create a new vertex and populate its data. Vertex vert = new Vertex(); // Get the local position relative to the world origin. MPoint mayaPos = it.position(MSpace.Space.kObject); vert.position = new Vector3((float)mayaPos.x, (float)mayaPos.y, (float)mayaPos.z); //vert.position = new Vector3((float)mayaPos.x - mesh.sourceXForm.mayaWorldPosition.x, //(float)mayaPos.y - mesh.sourceXForm.mayaWorldPosition.y, //(float)mayaPos.z - mesh.sourceXForm.mayaWorldPosition.z); // Get the normal. MVector mayaNrm = new MVector(); it.getNormal(mayaNrm, MSpace.Space.kObject); vert.normal = new Vector3((float)mayaNrm.x, (float)mayaNrm.y, (float)mayaNrm.z); // Texcoords. if( hasUvs && it.hasUVsProperty ) { float[] mayaUvs = new float[2]; it.getUV(mayaUvs, uvSetNames[0]); vert.texcoord = new Vector2(mayaUvs[0], mayaUvs[1]); } // Append the vertex. mesh.vertices.Add(vert); it.next(); } // Get all index data. MIntArray mia1 = new MIntArray(); MIntArray mia2 = new MIntArray(); mayaMesh.getTriangleOffsets(mia1, mia2); foreach( int idx in mia2 ) { mesh.indices.Add((uint)idx); } } }
// // Write out the "fileInfo" command for the freeform information associated // with the scene. // protected void writeFileInfo(FileStream f) { // // There's no direct access to the scene's fileInfo from within the API, // so we have to call MEL's 'fileInfo' command. // MStringArray fileInfo = new MStringArray(); try { MGlobal.executeCommand("fileInfo -q", fileInfo); uint numEntries = fileInfo.length; int i; string result = ""; for (i = 0; i < numEntries; i += 2) { result += ("fileInfo " + quote(fileInfo[i]) + " " + quote(fileInfo[i+1]) + ";" + "\n"); } writeString(f, result); } catch (Exception) { MGlobal.displayWarning("Could not get scene's fileInfo."); } }
public override void doIt(MArgList args) { MStringArray referenceFiles = new MStringArray(); MFileIO.getReferences(referenceFiles); string log = ""; for (int i = 0; i < referenceFiles.length; i++) { MStringArray connectionsMade = new MStringArray(); MFileIO.getReferenceConnectionsMade(referenceFiles[i], connectionsMade); log = string.Format("{0} Referenced File: {1}:\nConnections Made:\n", log, referenceFiles[i]); int j; for (j = 0; j < connectionsMade.length; j += 2) { log = string.Format(" {0} -> ", connectionsMade[j]); if (j + 1 < connectionsMade.length) { log = log + connectionsMade[j + 1]; } log += "\n"; } MStringArray connectionsBroken = new MStringArray(); MFileIO.getReferenceConnectionsBroken(referenceFiles[i], connectionsBroken); log += "\n Connections Broken: \n"; for (j = 0; j < connectionsBroken.length; j += 2) { log = string.Format("{0} {1} -> ", log, connectionsBroken[j]); if (j + 1 < connectionsBroken.length) { log += connectionsBroken[j + 1]; } log += "\n"; } log += "\n"; MStringArray referencedNodes = new MStringArray(); log += " Attrs Changed Since File Open:\n"; MFileIO.getReferenceNodes(referenceFiles[i], referencedNodes); for (j = 0; j < referencedNodes.length; j++) { // For each node, call a MEL command to get its // attributes. Say we're only interested in scalars. // string cmd = string.Format("listAttr -s -cfo {0}", referencedNodes[j]); MStringArray referencedAttributes = new MStringArray(); MGlobal.executeCommand(cmd, referencedAttributes); for (int k = 0; k < referencedAttributes.length; k++) { log += string.Format(" {0}.{1}\n", referencedNodes[j], referencedAttributes[k]); } } log += "\n"; } // End of output // log += "====================================="; MGlobal.displayInfo(log); return; }
// // Write out the "requires" lines which specify the plugins needed by the // scene. // protected void writeRequirements(FileStream f) { // // Every scene requires Maya itself. // string result = ""; result += ("requires maya \"" + fFileVersion + "\";\n"); // // Write out requirements for each plugin. // MStringArray pluginsUsed = new MStringArray(); try { MGlobal.executeCommand("pluginInfo -q -pluginsInUse", pluginsUsed); uint numPlugins = pluginsUsed.length; int i; for (i = 0; i < numPlugins; i += 2) { result += ("requires " + quote(pluginsUsed[i]) + " " + quote(pluginsUsed[i+1]) + ";" + "\n"); } } catch (Exception) { MGlobal.displayWarning( "Could not get list of plugins currently in use." ); } writeString(f, result); }
private Dictionary <string, object> _ExportCustomUserAttributes(BaseObject baseObject) { var objectName = ""; if (baseObject.mFnTransform != null) { objectName = baseObject.mFnTransform.name; } else if (baseObject.babylonMaterial != null) { objectName = baseObject.babylonMaterial.name; } MStringArray customAttributeNamesMStringArray = new MStringArray(); Dictionary <string, object> customsAttributes = new Dictionary <string, object>(); MGlobal.executeCommand($"listAttr -ud {objectName}", customAttributeNamesMStringArray); var customAttributeNames = customAttributeNamesMStringArray.Where((attributeName) => { return(!_DisallowedCustomAttributeNames.Contains(attributeName)); }); foreach (string name in customAttributeNames) { MStringArray type = new MStringArray(); MGlobal.executeCommand($"getAttr -type {objectName}.{name}", type); switch (type[0]) { case "double": double floatValue = 0; MGlobal.executeCommand($"getAttr {objectName}.{name}", out floatValue); customsAttributes.Add(name, floatValue); break; case "bool": int boolBinValue = 0; MGlobal.executeCommand($"getAttr {objectName}.{name}", out boolBinValue); customsAttributes.Add(name, boolBinValue); break; case "long": int intValue = 0; MGlobal.executeCommand($"getAttr {objectName}.{name}", out intValue); customsAttributes.Add(name, intValue); break; case "string": string stringValue = ""; MGlobal.executeCommand($"getAttr {objectName}.{name}", out stringValue); customsAttributes.Add(name, stringValue); break; case "enum": int enumValue = 0; MGlobal.executeCommand($"getAttr {objectName}.{name}", out enumValue); customsAttributes.Add(name, enumValue); break; case "double3": MDoubleArray vectorValue = new MDoubleArray(); MGlobal.executeCommand($"getAttr {objectName}.{name}", vectorValue); customsAttributes.Add(name, vectorValue); break; default: MCommandResult attrValue = new MCommandResult(); MGlobal.executeCommand($"getAttr {objectName}.{name}", attrValue); customsAttributes.Add(name, attrValue); break; } } foreach (string name in customAttributeNames) { if (customsAttributes.ContainsKey(name + "X") && customsAttributes.ContainsKey(name + "Y") && customsAttributes.ContainsKey(name + "Z")) { customsAttributes.Remove(name + "X"); customsAttributes.Remove(name + "Y"); customsAttributes.Remove(name + "Z"); } } return(customsAttributes); }
/// <summary> /// Write "ScriptToBabylon" in the Maya console to export with MEL /// </summary> /// <param name="argl"></param> /// public override void doIt(MArgList argl) { uint index = 1; MStringArray argExportParameters = argl.asStringArray(ref index); string errorMessage = null; for (var i = 0; i < argExportParameters.length; i++) { switch (i) { case 0: if (argExportParameters[i] != "") { ScriptExportParameters.outputPath = argExportParameters[i]; } else { errorMessage = "The specified path is not valid"; } break; case 1: if (argExportParameters[i] != "babylon" || argExportParameters[i] != "gltf" || argExportParameters[i] != "glb" || argExportParameters[i] != "binary babylon") { ScriptExportParameters.outputFormat = argExportParameters[i]; } else { errorMessage = "The specified output format is not valid"; } break; case 2: ScriptExportParameters.textureFolder = argExportParameters[i]; break; case 3: ScriptExportParameters.scaleFactor = float.Parse(argExportParameters[i]); break; case 4: ScriptExportParameters.writeTextures = bool.Parse(argExportParameters[i]); break; case 5: ScriptExportParameters.overwriteTextures = bool.Parse(argExportParameters[i]); break; case 6: ScriptExportParameters.exportHiddenObjects = bool.Parse(argExportParameters[i]); break; case 7: ScriptExportParameters.exportMaterials = bool.Parse(argExportParameters[i]); break; case 8: ScriptExportParameters.exportOnlySelected = bool.Parse(argExportParameters[i]); break; case 9: ScriptExportParameters.bakeAnimationFrames = bool.Parse(argExportParameters[i]); break; case 10: ScriptExportParameters.optimizeAnimations = bool.Parse(argExportParameters[i]); break; case 11: ScriptExportParameters.optimizeVertices = bool.Parse(argExportParameters[i]); break; case 12: ScriptExportParameters.animgroupExportNonAnimated = bool.Parse(argExportParameters[i]); break; case 13: ScriptExportParameters.generateManifest = bool.Parse(argExportParameters[i]); break; case 14: ScriptExportParameters.autoSaveSceneFile = bool.Parse(argExportParameters[i]); break; case 15: ScriptExportParameters.exportTangents = bool.Parse(argExportParameters[i]); break; case 16: ScriptExportParameters.exportSkins = bool.Parse(argExportParameters[i]); break; case 17: ScriptExportParameters.exportMorphTangents = bool.Parse(argExportParameters[i]); break; case 18: ScriptExportParameters.exportMorphNormals = bool.Parse(argExportParameters[i]); break; case 19: ScriptExportParameters.txtQuality = long.Parse(argExportParameters[i]); break; case 20: ScriptExportParameters.mergeAOwithMR = bool.Parse(argExportParameters[i]); break; case 21: ScriptExportParameters.dracoCompression = bool.Parse(argExportParameters[i]); break; case 22: ScriptExportParameters.enableKHRLightsPunctual = bool.Parse(argExportParameters[i]); break; case 23: ScriptExportParameters.enableKHRTextureTransform = bool.Parse(argExportParameters[i]); break; case 24: ScriptExportParameters.enableKHRMaterialsUnlit = bool.Parse(argExportParameters[i]); break; case 25: ScriptExportParameters.pbrFull = bool.Parse(argExportParameters[i]); break; case 26: ScriptExportParameters.pbrNoLight = bool.Parse(argExportParameters[i]); break; case 27: ScriptExportParameters.createDefaultSkybox = bool.Parse(argExportParameters[i]); break; case 28: ScriptExportParameters.pbrEnvironment = argExportParameters[i]; break; } } if (errorMessage == null) { try { BabylonExporter exporterInstance = new BabylonExporter(); exporterInstance.OnError += (error, rank) => { try { displayError(error); } catch { } Application.DoEvents(); }; exporterInstance.OnWarning += (error, rank) => { try { displayWarning(error); } catch { } Application.DoEvents(); }; exporterInstance.OnMessage += (message, color, rank, emphasis) => { try { displayInfo(message); } catch { } Application.DoEvents(); }; exporterInstance.Export(ScriptExportParameters); } catch (Exception ex) { displayError("Export cancelled: " + ex.Message); } } else { displayError(errorMessage); } }
public override bool getSourceStreams(MObject objPath, MStringArray sourceStreams) { // No source stream needed return(false); }
/// <summary> /// Extract geometry (position, normal, UVs...) for a specific vertex /// </summary> /// <param name="mFnMesh"></param> /// <param name="polygonId">The polygon (face) to examine</param> /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param> /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param> /// <param name="uvSetNames"></param> /// <param name="isUVExportSuccess"></param> /// <returns></returns> private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, MStringArray uvSetNames, ref bool[] isUVExportSuccess, ref bool isTangentExportSuccess) { MPoint point = new MPoint(); mFnMesh.getPoint(vertexIndexGlobal, point); MVector normal = new MVector(); mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal); // Switch coordinate system at object level point.z *= -1; normal.z *= -1; var vertex = new GlobalVertex { BaseIndex = vertexIndexGlobal, Position = point.toArray(), Normal = normal.toArray() }; // Tangent if (isTangentExportSuccess) { try { MVector tangent = new MVector(); mFnMesh.getFaceVertexTangent(polygonId, vertexIndexGlobal, tangent); // Switch coordinate system at object level tangent.z *= -1; int tangentId = mFnMesh.getTangentId(polygonId, vertexIndexGlobal); bool isRightHandedTangent = mFnMesh.isRightHandedTangent(tangentId); // Invert W to switch to left handed system vertex.Tangent = new float[] { (float)tangent.x, (float)tangent.y, (float)tangent.z, isRightHandedTangent ? -1 : 1 }; } catch { // Exception raised when mesh don't have tangents isTangentExportSuccess = false; } } // Color int colorIndex; string colorSetName; float[] defaultColor = new float[] { 1, 1, 1, 0 }; MColor color = new MColor(); mFnMesh.getCurrentColorSetName(out colorSetName); if (mFnMesh.numColors(colorSetName) > 0) { //Get the color index mFnMesh.getColorIndex(polygonId, vertexIndexLocal, out colorIndex); //if a color is set if (colorIndex != -1) { mFnMesh.getColor(colorIndex, color); vertex.Color = color.toArray(); } //else set the default color else { vertex.Color = defaultColor; } } // UV int indexUVSet = 0; if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet]) { try { float u = 0, v = 0; mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]); vertex.UV = new float[] { u, v }; } catch { // An exception is raised when a vertex isn't mapped to an UV coordinate isUVExportSuccess[indexUVSet] = false; } } indexUVSet = 1; if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet]) { try { float u = 0, v = 0; mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]); vertex.UV2 = new float[] { u, v }; } catch { // An exception is raised when a vertex isn't mapped to an UV coordinate isUVExportSuccess[indexUVSet] = false; } } // skin if (mFnSkinCluster != null) { // Filter null weights Dictionary <int, double> weightByInfluenceIndex = new Dictionary <int, double>(); // contains the influence indices with weight > 0 // Export Weights and BonesIndices for the vertex // Get the weight values of all the influences for this vertex allMayaInfluenceWeights = new MDoubleArray(); MGlobal.executeCommand($"skinPercent -query -value {mFnSkinCluster.name} {mFnTransform.name}.vtx[{vertexIndexGlobal}]", allMayaInfluenceWeights); allMayaInfluenceWeights.get(out double[] allInfluenceWeights); for (int influenceIndex = 0; influenceIndex < allInfluenceWeights.Length; influenceIndex++) { double weight = allInfluenceWeights[influenceIndex]; if (weight > 0) { try { // add indice and weight in the local lists weightByInfluenceIndex.Add(indexByNodeName[allMayaInfluenceNames[influenceIndex]], weight); } catch (Exception e) { RaiseError(e.Message, 2); RaiseError(e.StackTrace, 3); } } } // normalize weights => Sum weights == 1 Normalize(ref weightByInfluenceIndex); // decreasing sort OrderByDescending(ref weightByInfluenceIndex); int bonesCount = indexByNodeName.Count; // number total of bones/influences for the mesh float weight0 = 0; float weight1 = 0; float weight2 = 0; float weight3 = 0; int bone0 = bonesCount; int bone1 = bonesCount; int bone2 = bonesCount; int bone3 = bonesCount; int nbBones = weightByInfluenceIndex.Count; // number of bones/influences for this vertex if (nbBones == 0) { weight0 = 1.0f; bone0 = bonesCount; } if (nbBones > 0) { bone0 = weightByInfluenceIndex.ElementAt(0).Key; weight0 = (float)weightByInfluenceIndex.ElementAt(0).Value; if (nbBones > 1) { bone1 = weightByInfluenceIndex.ElementAt(1).Key; weight1 = (float)weightByInfluenceIndex.ElementAt(1).Value; if (nbBones > 2) { bone2 = weightByInfluenceIndex.ElementAt(2).Key; weight2 = (float)weightByInfluenceIndex.ElementAt(2).Value; if (nbBones > 3) { bone3 = weightByInfluenceIndex.ElementAt(3).Key; weight3 = (float)weightByInfluenceIndex.ElementAt(3).Value; } } } } float[] weights = { weight0, weight1, weight2, weight3 }; vertex.Weights = weights; vertex.BonesIndices = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; if (nbBones > 4) { bone0 = weightByInfluenceIndex.ElementAt(4).Key; weight0 = (float)weightByInfluenceIndex.ElementAt(4).Value; weight1 = 0; weight2 = 0; weight3 = 0; if (nbBones > 5) { bone1 = weightByInfluenceIndex.ElementAt(5).Key; weight1 = (float)weightByInfluenceIndex.ElementAt(4).Value; if (nbBones > 6) { bone2 = weightByInfluenceIndex.ElementAt(6).Key; weight2 = (float)weightByInfluenceIndex.ElementAt(4).Value; if (nbBones > 7) { bone3 = weightByInfluenceIndex.ElementAt(7).Key; weight3 = (float)weightByInfluenceIndex.ElementAt(7).Value; } } } float[] weightsExtra = { weight0, weight1, weight2, weight3 }; vertex.WeightsExtra = weightsExtra; vertex.BonesIndicesExtra = (bone3 << 24) | (bone2 << 16) | (bone1 << 8) | bone0; } } return(vertex); }
/// <summary> /// Extract ordered indices on a triangle basis /// Extract position and normal of each vertex per face /// </summary> /// <param name="mFnMesh"></param> /// <param name="vertices"></param> /// <param name="indices"></param> /// <param name="subMeshes"></param> /// <param name="uvSetNames"></param> /// <param name="isUVExportSuccess"></param> /// <param name="optimizeVertices"></param> private void ExtractGeometry(MFnMesh mFnMesh, List <GlobalVertex> vertices, List <int> indices, List <BabylonSubMesh> subMeshes, MStringArray uvSetNames, ref bool[] isUVExportSuccess, bool optimizeVertices) { // TODO - optimizeVertices MIntArray triangleCounts = new MIntArray(); MIntArray trianglesVertices = new MIntArray(); mFnMesh.getTriangles(triangleCounts, trianglesVertices); MObjectArray shaders = new MObjectArray(); MIntArray faceMatIndices = new MIntArray(); // given a face index => get a shader index mFnMesh.getConnectedShaders(0, shaders, faceMatIndices); // Export geometry even if an error occured with shaders // This is a fix for Maya test files // TODO - Find the reason why shaders.count = 0 int nbShaders = Math.Max(1, shaders.Count); bool checkShader = nbShaders == shaders.Count; RaiseVerbose("shaders.Count=" + shaders.Count, 2); // For each material of this mesh for (int indexShader = 0; indexShader < nbShaders; indexShader++) { var nbIndicesSubMesh = 0; var minVertexIndexSubMesh = int.MaxValue; var maxVertexIndexSubMesh = int.MinValue; var subMesh = new BabylonSubMesh { indexStart = indices.Count, materialIndex = indexShader }; // For each polygon of this mesh for (int polygonId = 0; polygonId < faceMatIndices.Count; polygonId++) { if (checkShader && faceMatIndices[polygonId] != indexShader) { continue; } // The object-relative (mesh-relative/global) vertex indices for this face MIntArray polygonVertices = new MIntArray(); mFnMesh.getPolygonVertices(polygonId, polygonVertices); // For each triangle of this polygon for (int triangleId = 0; triangleId < triangleCounts[polygonId]; triangleId++) { int[] polygonTriangleVertices = new int[3]; mFnMesh.getPolygonTriangleVertices(polygonId, triangleId, polygonTriangleVertices); /* * Switch coordinate system at global level * * Piece of code kept just in case * See BabylonExporter for more information */ //// Inverse winding order to flip faces //var tmp = triangleVertices[1]; //triangleVertices[1] = triangleVertices[2]; //triangleVertices[2] = tmp; // For each vertex of this triangle (3 vertices per triangle) foreach (int vertexIndexGlobal in polygonTriangleVertices) { // Get the face-relative (local) vertex id int vertexIndexLocal = 0; for (vertexIndexLocal = 0; vertexIndexLocal < polygonVertices.Count; vertexIndexLocal++) { if (polygonVertices[vertexIndexLocal] == vertexIndexGlobal) { break; } } GlobalVertex vertex = ExtractVertex(mFnMesh, polygonId, vertexIndexGlobal, vertexIndexLocal, uvSetNames, ref isUVExportSuccess); vertex.CurrentIndex = vertices.Count; indices.Add(vertex.CurrentIndex); vertices.Add(vertex); minVertexIndexSubMesh = Math.Min(minVertexIndexSubMesh, vertex.CurrentIndex); maxVertexIndexSubMesh = Math.Max(maxVertexIndexSubMesh, vertex.CurrentIndex); nbIndicesSubMesh++; } } } if (nbIndicesSubMesh != 0) { subMesh.indexCount = nbIndicesSubMesh; subMesh.verticesStart = minVertexIndexSubMesh; subMesh.verticesCount = maxVertexIndexSubMesh - minVertexIndexSubMesh + 1; subMeshes.Add(subMesh); } } }
//! Ensure that valid geometry is selected bool validGeometrySelected() { MSelectionList list = new MSelectionList(); MGlobal.getActiveSelectionList(list); MItSelectionList iter = new MItSelectionList(list, MFn.Type.kInvalid); for (; !iter.isDone; iter.next()) { MObject dependNode = new MObject(); iter.getDependNode(dependNode); if (dependNode.isNull || !dependNode.hasFn(MFn.Type.kTransform)) { MGlobal.displayWarning("Object in selection list is not right type of node"); return false; } MFnDependencyNode dependNodeFn = new MFnDependencyNode(dependNode); MStringArray attributeNames = new MStringArray(); attributeNames.append("scaleX"); attributeNames.append("translateX"); int i; for ( i = 0; i < attributeNames.length; i++ ) { MPlug plug = dependNodeFn.findPlug(attributeNames[i]); if ( plug.isNull ) { MGlobal.displayWarning("Object cannot be manipulated: " + dependNodeFn.name); return false; } } } return true; }
/// <summary> /// Extract geometry (position, normal, UVs...) for a specific vertex /// </summary> /// <param name="mFnMesh"></param> /// <param name="polygonId">The polygon (face) to examine</param> /// <param name="vertexIndexGlobal">The object-relative (mesh-relative/global) vertex index</param> /// <param name="vertexIndexLocal">The face-relative (local) vertex id to examine</param> /// <param name="uvSetNames"></param> /// <param name="isUVExportSuccess"></param> /// <returns></returns> private GlobalVertex ExtractVertex(MFnMesh mFnMesh, int polygonId, int vertexIndexGlobal, int vertexIndexLocal, MStringArray uvSetNames, ref bool[] isUVExportSuccess) { MPoint point = new MPoint(); mFnMesh.getPoint(vertexIndexGlobal, point); MVector normal = new MVector(); mFnMesh.getFaceVertexNormal(polygonId, vertexIndexGlobal, normal); // Switch coordinate system at object level point.z *= -1; normal.z *= -1; var vertex = new GlobalVertex { BaseIndex = vertexIndexGlobal, Position = point.toArray(), Normal = normal.toArray(), }; // TODO - Export colors ? //// Color //int colorIndex; //string colorSetName; //float[] defaultColor = new float[] { 0.5f, 0.5f, 0.5f, 1 }; //MColor color = new MColor(); //mFnMesh.getCurrentColorSetName(out colorSetName); //if (mFnMesh.numColors(colorSetName) > 0) //{ // //Get the color index // mFnMesh.getColorIndex(polygonId, vertexIndexLocal, out colorIndex); // //if a color is set // if (colorIndex != -1) // { // mFnMesh.getColor(colorIndex, color); // vertex.Color = color.toArray(); // } // //else set the color to the default one of Maya // else // { // vertex.Color = defaultColor; // } //} //else //{ // vertex.Color = defaultColor; //} // UV int indexUVSet = 0; if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet]) { try { float u = 0, v = 0; mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]); vertex.UV = new float[] { u, v }; } catch (Exception e) { // An exception is raised when a vertex isn't mapped to an UV coordinate isUVExportSuccess[indexUVSet] = false; } } indexUVSet = 1; if (uvSetNames.Count > indexUVSet && isUVExportSuccess[indexUVSet]) { try { float u = 0, v = 0; mFnMesh.getPolygonUV(polygonId, vertexIndexLocal, ref u, ref v, uvSetNames[indexUVSet]); vertex.UV2 = new float[] { u, v }; } catch (Exception e) { // An exception is raised when a vertex isn't mapped to an UV coordinate isUVExportSuccess[indexUVSet] = false; } } return(vertex); }
private void _exportUV(MFnDependencyNode textureDependencyNode, BabylonTexture babylonTexture, List <MFnDependencyNode> textureModifiers = null, bool updateCoordinatesMode = false) { // Coordinates mode if (updateCoordinatesMode) { // Default is spherical babylonTexture.coordinatesMode = BabylonTexture.CoordinatesMode.SPHERICAL_MODE; if (textureModifiers != null) { MFnDependencyNode lastProjectionTextureModifier = textureModifiers.FindLast(textureModifier => textureModifier.objectProperty.hasFn(MFn.Type.kProjection)); if (lastProjectionTextureModifier != null) { var projType = lastProjectionTextureModifier.findPlug("projType").asIntProperty; switch (projType) { case 1: // Planar babylonTexture.coordinatesMode = BabylonTexture.CoordinatesMode.PLANAR_MODE; break; case 2: // Spherical babylonTexture.coordinatesMode = BabylonTexture.CoordinatesMode.SPHERICAL_MODE; break; } } } } // UV set MStringArray uvLinks = new MStringArray(); MGlobal.executeCommand($@"uvLink -query -texture {textureDependencyNode.name};", uvLinks); if (uvLinks.Count == 0) { babylonTexture.coordinatesIndex = 0; } else { // Retreive UV set indices HashSet <int> uvSetIndices = new HashSet <int>(); foreach (string uvLink in uvLinks) { int indexOpenBracket = uvLink.LastIndexOf("["); int indexCloseBracket = uvLink.LastIndexOf("]"); string uvSetIndexAsString = uvLink.Substring(indexOpenBracket + 1, indexCloseBracket - indexOpenBracket - 1); int uvSetIndex = int.Parse(uvSetIndexAsString); uvSetIndices.Add(uvSetIndex); } if (uvSetIndices.Count > 1) { // Check that all uvSet indices are all 0 or all not 0 int nbZero = 0; foreach (int uvSetIndex in uvSetIndices) { if (uvSetIndex == 0) { nbZero++; } } if (nbZero != 0 && nbZero != uvSetIndices.Count) { RaiseWarning("Texture is linked to UV1 and UV2. Only one UV set per texture is supported.", logRankTexture + 1); } } // The first UV set of a mesh is special because it can never be deleted // Thus if the UV set index is 0 then the binded mesh UV set is always UV1 // Other UV sets of a mesh can be created / deleted at will // Thus the UV set index can have any value (> 0) // In this case, the exported UV set is always UV2 even though it would be UV3 or UV4 in Maya babylonTexture.coordinatesIndex = (new List <int>(uvSetIndices))[0] == 0 ? 0 : 1; } // For more information about UV // see http://help.autodesk.com/view/MAYAUL/2018/ENU/?guid=GUID-94070C7E-C550-42FD-AFC9-FBE82B173B1D babylonTexture.uOffset = textureDependencyNode.findPlug("offsetU").asFloat(); babylonTexture.vOffset = textureDependencyNode.findPlug("offsetV").asFloat(); babylonTexture.uScale = textureDependencyNode.findPlug("repeatU").asFloat(); babylonTexture.vScale = textureDependencyNode.findPlug("repeatV").asFloat(); if (Path.GetExtension(babylonTexture.name).ToLower() == ".dds") { babylonTexture.vScale *= -1; // Need to invert Y-axis for DDS texture } // Maya only has a W rotation babylonTexture.uAng = 0; babylonTexture.vAng = 0; babylonTexture.wAng = textureDependencyNode.findPlug("rotateFrame").asFloat(); // TODO - rotation and scale if (babylonTexture.wAng != 0f && (babylonTexture.uScale != 1f || babylonTexture.vScale != 1f)) { RaiseWarning("Rotation and tiling (scale) on a texture are only supported separatly. You can use the map UV of the mesh for those transformation.", logRankTexture + 1); } // Adress mode U // TODO - What is adress mode when both wrap and mirror? if (textureDependencyNode.findPlug("mirrorU").asBool()) { babylonTexture.wrapU = BabylonTexture.AddressMode.MIRROR_ADDRESSMODE; } else if (textureDependencyNode.findPlug("wrapU").asBool()) { babylonTexture.wrapU = BabylonTexture.AddressMode.WRAP_ADDRESSMODE; } else { // TODO - What is adress mode when not wrap nor mirror? babylonTexture.wrapU = BabylonTexture.AddressMode.CLAMP_ADDRESSMODE; } // Adress mode V // TODO - What is adress mode when both wrap and mirror? if (textureDependencyNode.findPlug("mirrorV").asBool()) { babylonTexture.wrapV = BabylonTexture.AddressMode.MIRROR_ADDRESSMODE; } else if (textureDependencyNode.findPlug("wrapV").asBool()) { babylonTexture.wrapV = BabylonTexture.AddressMode.WRAP_ADDRESSMODE; } else { // TODO - What is adress mode when not wrap nor mirror? babylonTexture.wrapV = BabylonTexture.AddressMode.CLAMP_ADDRESSMODE; } // Animation babylonTexture.animations = GetTextureAnimations(textureDependencyNode).ToArray(); }