public static bool Export_To(string _filepath) { List <IINode> nodes = GetSelection(); foreach (IINode _node in nodes) { IIDerivedObject _gObject = (IIDerivedObject)_node.ObjectRef; IClass_ID classID = maxGlobal.Class_ID.Create((uint)BuiltInClassIDA.TRIOBJ_CLASS_ID, 0); ITriObject _mObject = (ITriObject)_gObject.ObjRef.ConvertToType(0, classID); IMesh _mMesh = _mObject.Mesh; _mMesh.BuildNormals(); IIDerivedObject theObj = (IIDerivedObject)_node.ObjectRef; for (int m = 0; m < theObj.Modifiers.Count; m++) { IModifier theModifier = theObj.GetModifier(m); if (theModifier.ClassName == "Skin") { IISkin _skin = (IISkin)theModifier.GetInterface((InterfaceID)(0x00010000)); IISkinContextData _skinContext = _skin.GetContextInterface(_node); ComputeVertexData(_mMesh, _skinContext, semantic, _filepath); } } } return(true); }
public static ITriObject GetTriObjectFromNode(this IINode node, int time, out bool deleteIt) { deleteIt = false; IObject obj = node.EvalWorldState(time, true).Obj; IClass_ID triObjId = MaxGlobal.Class_ID.Create((int)BuiltInClassIDA.TRIOBJ_CLASS_ID, 0); if (obj.CanConvertToType(triObjId) != 0) { ITriObject tri = (ITriObject)obj.ConvertToType(time, triObjId); // Note that the TriObject should only be deleted // if the pointer to it is not equal to the object // pointer that called ConvertToType() if (obj != tri) { deleteIt = true; } return(tri); } else { return(null); } }
public Mesh GetMesh(TimeValue t) { if (_Object.CanConvertToType(ClassID.TriObject._IClass_ID) == 0) { return(null); } ITriObject tri = _Object.ConvertToType(t, ClassID.TriObject._IClass_ID) as ITriObject; if (tri == null) { return(null); } Mesh r = new Mesh(tri.Mesh); if (tri.Handle != _Object.Handle) { RefResult rr = tri.MaybeAutoDelete(); if (rr == RefResult.Fail) { throw new Exception("Failed to autodelete the tri-object"); } } return(r); }
/// <summary> /// convert the actual node to editable mesh /// </summary> public void ConvertToMesh() { if (!this.IsValid) { return; } // get the current node world state IObjectState state = this.m_node.EvalWorldState(AssemblyFunctions.Core.Time, false); // get the object out of the state and check if it's possible to convert to editable mesh IObject obj = state.Obj; if (obj.CanConvertToType(AssemblyFunctions.GlobalInterface.TriObjectClassID) == 0) { return; } // convert the obj to triobject // if the node type was editable mesh, tit will return just the current node mesh // elsewhere it will return a new triobj instance which is not bound to anything ITriObject tri = obj.ConvertToType(AssemblyFunctions.Core.Time, AssemblyFunctions.GlobalInterface.TriObjectClassID) as ITriObject; // check if we got something if (tri == null) { return; } // remeber the obj and mesh for later use this.m_triMesh = tri.Mesh; this.m_baseObject = obj; // check if the tri is identical with the current object from the node state // if the node was already editable mesh we don't need to do anything if (this.m_triMesh == null || tri.Equals((INoncopyable)obj)) { return; } // notfiy all dependents // this notfiy was from a max sample. not sure why call first before change and call change afterwards this.m_node.NotifyDependents(Globals.FOREVER, Globals.PART_ALL, RefMessage.Change); // the the object reference of the node to the new triobj // important: if you exchange the object ref ith the same objref // like this.m_node.ObjectRef = this.m_node.ObjectRef you will produce a nice crash sometimes // the property setter inside max.net will delete the old reference and since the old is the new // the reference will be set to null which is illegal this.m_node.ObjectRef = tri; // notfiy all dependents this.m_node.NotifyDependents(Globals.FOREVER, 0, RefMessage.SubanimStructureChanged); this.m_node.NotifyDependents(Globals.FOREVER, Globals.PART_ALL, RefMessage.Change); }
public static (double volume, IDPoint3 center)? GetVolumeAndMassCenter(this ITriObject triObject) { try { double Volume = 0; IDPoint3 Center = MaxGlobal.DPoint3.Create(0, 0, 0); for (int i = 0; i < triObject.Mesh.NumFaces; i++) { IFace face = triObject.Mesh.Faces[i]; IDPoint3 vert2 = MaxGlobal.DPoint3.Create(triObject.Mesh.GetVert((int)face.GetVert(2))); IDPoint3 vert1 = MaxGlobal.DPoint3.Create(triObject.Mesh.GetVert((int)face.GetVert(1))); IDPoint3 vert0 = MaxGlobal.DPoint3.Create(triObject.Mesh.GetVert((int)face.GetVert(0))); vert1.Subtract(vert0); vert2.Subtract(vert0); double dV = MaxGlobal.DotProd( MaxGlobal.CrossProd( vert1.Subtract(vert0), vert2.Subtract(vert0)) , vert0); Volume += dV; Center = Center.Add((vert0.Add(vert1).Add(vert2)).MultiplyBy(dV)); } Volume = Volume / 6; Center = Center.DivideBy(24); Center = Center.DivideBy(Volume); return(Volume, Center); } catch { return(null); } }
protected unsafe GeometryNode CreateGeometryUpdate(IINode node, ITriObject maxGeometry) { GeometryNode update = new GeometryNode(); update.Name = node.Name; update.Parent = node.ParentNode.Name; update.Transform = GetTransform(node); /* * Scene objects have one, or none, material. This member will be that materials name, even if that material is a container like a Composite * or Shell material. * The client will attach the Material ID of the face when/if the materials are split out, which will allow the materials * processing code to identify and import the correct material properties later. (In practice, we dont even need to store this - since knowing * the node name will allow us to find it - but sending it allows us to match the functionality of the FBX importer. */ if (node.Mtl != null) { update.MaterialName = node.Mtl.Name; } IMesh mesh = maxGeometry.Mesh; /* Get the master face array and vertex positions. We split by material id here also for convenience. */ var facesAndPositions = GetTriMeshFacesAndPositions(mesh); update.FaceGroups.AddRange(MakeFaceGroupsFromMaterialIds(facesAndPositions.face_materialIds)); update.faceFlags = facesAndPositions.face_flags; update.Channels.Add(facesAndPositions.positions_channel); /* Get the remaining properties, such as normals and texture coordinates */ update.Channels.Add(GetTriMeshNormals(mesh)); update.Channels.AddRange(GetTriMeshMapChannels(mesh)); return update; }
/// <summary> /// collect and fill the property base on the trimesh /// </summary> public void CollectTriMeshData() { if (!this.IsValid) { return; } // get the current node world state IObjectState state = this.m_node.EvalWorldState(AssemblyFunctions.Core.Time, false); // get the object out of the state and check if it's possible to convert to editable mesh IObject obj = state.Obj; if (obj.CanConvertToType(AssemblyFunctions.GlobalInterface.TriObjectClassID) == 0) { return; } // convert the obj to triobject // if the node type was editable mesh, tit will return just the current node mesh // elsewhere it will return a new triobj instance which is not bound to anything ITriObject tri = obj.ConvertToType(AssemblyFunctions.Core.Time, AssemblyFunctions.GlobalInterface.TriObjectClassID) as ITriObject; if (tri == null) { return; } this.m_iNumVerts = tri.Mesh.NumVerts; this.m_iNumTris = tri.Mesh.NumFaces; // check if triobj is identical with the object from the node // if its not the same we got a new mesh so delete it if (!tri.Equals((INoncopyable)obj)) { tri.DeleteMe(); } }
public static (double volume, Point3D center)? GetVolumeAndMassCenter(this IINode node) { try { bool toDelete = false; ITriObject triObject = node.GetTriObjectFromNodeInCurrentTime(out toDelete); (double volume, IDPoint3 center)? ret = triObject.GetVolumeAndMassCenter(); // Delete the triObject //triObject.DeleteAllRefsToMe(); // Gets the WORLD position IDMatrix3 tm = node.GetObjectTM(MaxInterface.Time, null).ToDMatrix3(); IDPoint3 worldCenter = MaxGlobal.Multiply(tm, ret.Value.center); return(ret.Value.volume, worldCenter.ToPoint3D()); } catch { return(null); } }
public static IINode FlattenHierarchy(this IINode node) { IILayer nodeLayer = LayerUtilities.GetNodeLayer(node); if (nodeLayer != null) { Loader.Core.LayerManager.SetCurrentLayer(nodeLayer.Name); } List <IINode> children = node.NodeTree().ToList(); List <IINode> nonMeshChildren = children.Where(x => !x.IsMesh()).ToList(); List <IINode> meshChildren = children.Where(x => x.IsMesh()).ToList(); IClass_ID cid = Loader.Global.Class_ID.Create((uint)BuiltInClassIDA.SPHERE_CLASS_ID, 0); object obj = Loader.Core.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); IINode result = Loader.Core.CreateObjectNode((IObject)obj); result.Name = node.Name; result.SetNodeTM(Loader.Core.Time, node.GetNodeTM(Loader.Core.Time, Forever)); node.ParentNode.AttachChild(result, true); string convertToEditablePoly = $"ConvertTo (maxOps.getNodeByHandle {result.Handle}) Editable_Poly"; ScriptsUtilities.ExecuteMaxScriptCommand(convertToEditablePoly); IPolyObject polyObject = result.GetPolyObjectFromNode(); IEPoly nodeEPoly = (IEPoly)polyObject.GetInterface(Loader.EditablePoly); List <IMtl> newMultiMat = new List <IMtl>(); foreach (IINode n in meshChildren) { ITriObject triObject = n.GetTriObjectFromNode(); IMesh mesh = triObject.Mesh; if (n.Mtl.IsMultiMtl) { Dictionary <IMtl, List <IFace> > matIDtoFacesMap = new Dictionary <IMtl, List <IFace> >(); ////List<int> matIDsInUse = new List<int>(); foreach (IFace face in mesh.Faces) { int faceMatID = face.MatID; IMtl mat = n.Mtl.GetSubMtl(faceMatID); if (!matIDtoFacesMap.ContainsKey(mat)) { List <IFace> facesOfMat = new List <IFace>(); matIDtoFacesMap.Add(mat, facesOfMat); } matIDtoFacesMap[mat].Add(face); } foreach (KeyValuePair <IMtl, List <IFace> > matToFaceKeyValue in matIDtoFacesMap) { IMtl faceMat = matToFaceKeyValue.Key; //get the material from the list of the multimaterial if (!newMultiMat.Contains(faceMat)) { newMultiMat.Add(faceMat); } ushort newId = (ushort)(newMultiMat.IndexOf(faceMat)); foreach (IFace face in matToFaceKeyValue.Value) { face.MatID = newId; } } } else { //IMtl currentMat = n.Mtl; //int matID = newMultiMat.IndexOf(currentMat); //if (matID == -1) //{ // newMultiMat.Add(currentMat); // ushort newId = (ushort)newMultiMat.Count; // for (int fID = 0; fID < mesh.NumFaces; fID++) // { // mesh.SetFaceMtlIndex(fID, (ushort)newId); // } //} //else //{ // for (int fID = 0; fID < mesh.NumFaces; fID++) // { // mesh.SetFaceMtlIndex(fID, (ushort)matID); // } //} } triObject.Mesh = mesh; n.ObjectRef = triObject; //bool undo = false; //nodeEPoly.EpfnAttach(n, ref undo,result, Loader.Core.Time); } IClass_ID matCid = Loader.Global.Class_ID.Create((uint)BuiltInClassIDA.MULTI_MATERIAL_CLASS_ID, 0); IMtl finalMat = (IMtl)Loader.Core.CreateInstance(SClass_ID.Material, matCid); for (int i = 0; i < newMultiMat.Count; i++) { finalMat.SetSubMtl(i, newMultiMat[i]); } finalMat.Name = "final mat"; result.Mtl = finalMat; foreach (IINode n in nonMeshChildren) { result.AttachChild(n, true); } Loader.Core.DeleteNode(node, false, false); return(result); }
/// <summary> /// This is the routine to convert the input node to polygon faces. /// </summary> /// <param name="nodeHandle"> Input the node by handle. </param> /// <param name="convertToTri"> Input whether to convert to a poly object first. </param> /// <param name="addShell"> Input whether to add the shell modifier when finished converting to face. </param> /// <param name="shell"> Input the shell thickness amount. </param> /// <param name="addEditMesh"> Input whether to add the Edit Mesh modifier when finished converting to face. </param> /// <param name="collapseNode"> Input whether to collapse the node afterwards. </param> /// <param name="centerPivot"> Input whether to center the pivot on each new face. </param> /// <returns> Returns 1 if successful or -1 if not. </returns> static public int ConvertToPolygonFaces(uint nodeHandle, bool convertToPoly = true, // C# now supports default parameters bool addShell = true, float shell = 0.1f, bool addEditMesh = true, bool collapseNode = true, bool centerPivot = true) { try { IGlobal global = Autodesk.Max.GlobalInterface.Instance; IInterface14 ip = global.COREInterface14; IINode node = ip.GetINodeByHandle(nodeHandle); if (node == null) { return(-1); } // Get it's current object state. If a modifier has been applied, for example, // it is going to return the OS of the mesh in it's current form in the timeline. IObjectState os = node.ObjectRef.Eval(ip.Time); // Now grab the object itself. IObject objOriginal = os.Obj; IPolyObject polyObject = objOriginal as IPolyObject; IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.POLYOBJ_CLASS_ID, 0); IPolyObject polyObj = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID) as IPolyObject; if (polyObject == null && convertToPoly) { if (objOriginal.CanConvertToType(global.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID); } else { return(-1); } ITriObject triOriginal = objOriginal as ITriObject; polyObj.Mesh.AddTri(triOriginal.Mesh); polyObj.Mesh.FillInMesh(); polyObj.Mesh.EliminateBadVerts(0); polyObj.Mesh.MakePolyMesh(0, true); } else if (polyObject == null) { polyObj = polyObject; } else { return(-1); } IMatrix3 mat = node.GetNodeTM(0, null); IPoint3 ptOffsetPos = node.ObjOffsetPos; IQuat quatOffsetRot = node.ObjOffsetRot; IScaleValue scaleOffsetScale = node.ObjOffsetScale; // We can grab the faces as a List and iterate them in .NET API. int nNumFaces = polyObj.Mesh.FNum; if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressMaxNum = nNumFaces; } ADN_UserBreakCheck checkUserBreak = new ADN_UserBreakCheck(); for (int i = 0; i < nNumFaces; i++) { if (checkUserBreak.Check() == true) { return(-1); } if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressCurrNum = i; } // Create a new poly object for each new face. object objectNewFace = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); // Create a new node to hold it in the scene. IObject objNewFace = (IObject)objectNewFace; IINode n = global.COREInterface.CreateObjectNode(objNewFace); // Name it and ensure it is unique... string newname = "ADN-Sample-Face"; ip.MakeNameUnique(ref newname); n.Name = newname; // Based on what we created above, we can safely cast it to TriObject IPolyObject polyNewFace = objNewFace as IPolyObject; // Setup the new poly object with 1 face, and the vertex count from the original object's face we are processing polyNewFace.Mesh.SetNumFaces(1); polyNewFace.Mesh.SetMapNum(2); IMNFace f = polyObj.Mesh.F(i); polyNewFace.Mesh.F(0).Assign(f); IMNFace fnew = polyNewFace.Mesh.F(0); IList <int> vtx = f.Vtx; polyNewFace.Mesh.SetNumVerts(vtx.Count); for (int k = 0; k < vtx.Count; k++) { int nvindex = vtx[k]; IMNVert vert = polyObj.Mesh.V(nvindex); Debug.Print("\nVertex = " + k + ", " + nvindex); polyNewFace.Mesh.V(k).Assign(vert); fnew.Vtx[k] = k; } int nedge = nedge = polyNewFace.Mesh.SimpleNewEdge(0, 1); IMNEdge edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(1, 2); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(2, 3); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); nedge = polyNewFace.Mesh.SimpleNewEdge(3, 0); edge = polyNewFace.Mesh.E(nedge); edge.Track = -1; edge.F1 = 0; edge.F2 = -1; polyNewFace.Mesh.SetEdgeVis(nedge, true); polyNewFace.Mesh.FillInMesh(); // make it update. polyNewFace.Mesh.InvalidateGeomCache(); if (addShell) { AddOsmShell(n.Handle, shell); } if (addEditMesh) { AddOsmEditMesh(n.Handle); } if (collapseNode) { ip.CollapseNode(n, true); } // update transform to match object being exploded. n.SetNodeTM(0, mat); n.ObjOffsetPos = ptOffsetPos; n.ObjOffsetRot = quatOffsetRot; n.ObjOffsetScale = scaleOffsetScale; n.ObjOffsetPos = ptOffsetPos; if (centerPivot) { n.CenterPivot(0, false); } } } catch (Exception ex) { Debug.Print(ex.Message); return(-1); } return(1); }
/// <summary> /// This is the routine to convert the input node to triangle faces. /// </summary> /// <param name="nodeHandle"> Input the node by handle. </param> /// <param name="convertToTri"> Input whether to convert to a tri object first. </param> /// <param name="addShell"> Input whether to add the shell modifier when finished converting to face. </param> /// <param name="shell"> Input the shell thickness amount. </param> /// <param name="addEditMesh"> Input whether to add the Edit Mesh modifier when finished converting to face. </param> /// <param name="collapseNode"> Input whether to collapse the node afterwards. </param> /// <param name="centerPivot"> Input whether to center the pivot on each new face. </param> /// <returns> Returns 1 if successful or -1 if not. </returns> static public int ConvertToTriangleFaces(uint nodeHandle, bool convertToTri = true, // C# now supports default parameters bool addShell = true, float shell = 0.1f, bool addEditMesh = true, bool collapseNode = true, bool centerPivot = true) { try { IGlobal global = Autodesk.Max.GlobalInterface.Instance; IInterface14 ip = global.COREInterface14; IINode node = ip.GetINodeByHandle(nodeHandle); // Get it's current object state. If a modifier has been applied, for example, // it is going to return the OS of the mesh in it's current form in the timeline. IObjectState os = node.ObjectRef.Eval(ip.Time); // Now grab the object itself. IObject objOriginal = os.Obj; // Let's make sure it is a TriObject, which is the typical kind of object with a mesh if (!objOriginal.IsSubClassOf(global.TriObjectClassID)) { // If it is NOT, see if we can convert it... if (convertToTri && objOriginal.CanConvertToType(global.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(ip.Time, global.TriObjectClassID); } else { return(-1); } } // Now we should be safe to know it is a TriObject and we can cast it as such. // An exception will be thrown... ITriObject triOriginal = objOriginal as ITriObject; // Let's first setup a class ID for the type of objects are are creating. // New TriObject in this case to hold each face. IClass_ID cid = global.Class_ID.Create((uint)BuiltInClassIDA.TRIOBJ_CLASS_ID, 0); IMatrix3 mat = node.GetNodeTM(0, null); IPoint3 ptOffsetPos = node.ObjOffsetPos; IQuat quatOffsetRot = node.ObjOffsetRot; IScaleValue scaleOffsetScale = node.ObjOffsetScale; // We can grab the faces as a List and iterate them in .NET API. IMesh mesh = triOriginal.Mesh; IList <IFace> faces = triOriginal.Mesh.Faces; int nNumFaces = faces.Count; if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressMaxNum = nNumFaces; } ADN_UserBreakCheck checkUserBreak = new ADN_UserBreakCheck(); int count = 0; foreach (IFace face in faces) { if (checkUserBreak.Check() == true) { return(-1); } if (m_bUsingProgress) { m_ctrlProgress.PB_ProgressCurrNum = ++count; } // Create a new TriObject for each new face. object objectNewFace = ip.CreateInstance(SClass_ID.Geomobject, cid as IClass_ID); // Create a new node to hold it in the scene. IObject objNewFace = (IObject)objectNewFace; IINode n = global.COREInterface.CreateObjectNode(objNewFace); // Name it and ensure it is unique... string newname = "ADN-Sample-Face"; ip.MakeNameUnique(ref newname); n.Name = newname; // Based on what we created above, we can safely cast it to TriObject ITriObject triNewFace = objNewFace as ITriObject; // Setup the new TriObject with 1 face, and the vertex count from the original object's face we are processing triNewFace.Mesh.SetNumFaces(1, false, false); triNewFace.Mesh.SetNumVerts(face.V.Count(), false, false); // Finish setting up the face (always face '0' because there will only be one per object). triNewFace.Mesh.Faces[0].SetVerts(0, 1, 2); triNewFace.Mesh.Faces[0].SetEdgeVisFlags(EdgeVisibility.Vis, EdgeVisibility.Vis, EdgeVisibility.Vis); triNewFace.Mesh.Faces[0].SmGroup = 2; // Now, for each vertex, get the old face's points and store into new. for (int i = 0; i < face.V.Count(); i++) { //Get the vertex from the original object's face we are processing IPoint3 point = triOriginal.Mesh.GetVert((int)face.GetVert(i)); // Set the vertex point in the new face vertex triNewFace.Mesh.SetVert(i, point); } // make it draw. triNewFace.Mesh.InvalidateGeomCache(); if (addShell) { AddOsmShell(n.Handle, shell); } if (addEditMesh) { AddOsmEditMesh(n.Handle); } if (collapseNode) { ip.CollapseNode(n, true); } // update transform to match object being exploded. n.SetNodeTM(0, mat); n.ObjOffsetPos = ptOffsetPos; n.ObjOffsetRot = quatOffsetRot; n.ObjOffsetScale = scaleOffsetScale; n.ObjOffsetPos = ptOffsetPos; if (centerPivot) { n.CenterPivot(0, false); } } } catch (Exception) { return(-1); } return(1); }
static public string UpdateNodes(float vertexPercent, bool keepNormals, bool collapseStack) { IGlobal globalInterface = Autodesk.Max.GlobalInterface.Instance; IInterface14 coreInterface = globalInterface.COREInterface14; // start the scene process globalInterface.TheHold.Begin(); IINode nodeRoot = coreInterface.RootNode; m_sceneNodes.Clear(); GetSceneNodes(nodeRoot); List <IINode> optimizedNodes = new List <IINode> { }; // Iterate each node in the scene file and process all meshes into ProOptimized meshes. foreach (IINode node in m_sceneNodes) { // Check for object assigned to node (could be something other than object) if (node.ObjectRef != null) { IObjectState os = node.ObjectRef.Eval(coreInterface.Time); IObject objOriginal = os.Obj; if (!objOriginal.IsSubClassOf(globalInterface.TriObjectClassID)) { // If it is NOT, see if we can convert it... if (objOriginal.CanConvertToType(globalInterface.TriObjectClassID) == 1) { objOriginal = objOriginal.ConvertToType(coreInterface.Time, globalInterface.TriObjectClassID); } else { RuntimeExecute.LogTrace("\nNode {0} Object Not Converted Error: {1}", node.NodeName, objOriginal.ObjectName); continue; } } ITriObject tri = objOriginal as ITriObject; if (tri == null) { RuntimeExecute.LogTrace("\nNode {0} Object Not Converted Error: {1}", node.NodeName, objOriginal.ObjectName); continue; } int val = tri.Mesh.NumVerts; AddOsmProoptimizer(node, vertexPercent, keepNormals); // get new mesh state os = node.ObjectRef.Eval(coreInterface.Time); tri = os.Obj as ITriObject; // ** after modifier operation we can see if success by checking if the mesh size is different than before if (val != tri.Mesh.NumVerts) { if (collapseStack) { coreInterface.CollapseNode(node, true); } optimizedNodes.Add(node); } } } int status; if (optimizedNodes.Count() > 0) { // Build result file name based on percentage used string full_filename = coreInterface.CurFilePath; string filename = coreInterface.CurFileName; vertexPercent = vertexPercent * 100; string stringVertexPercent = vertexPercent.ToString("F1"); stringVertexPercent = stringVertexPercent.Replace('.', '_'); string output = "outputFile-" + stringVertexPercent + ".max"; string new_filename = full_filename.Replace(filename, output); status = coreInterface.SaveToFile(new_filename, true, false); // setup to export as FBX as well string outputFBX = new_filename.Replace(".max", ".fbx"); string msCmdFbxExport = "exportFile \"" + outputFBX + "\" #noPrompt using:FBXEXP"; bool fbxOk = globalInterface.ExecuteMAXScriptScript(msCmdFbxExport, false, null, false); // If we changed something, put scene back for next iteration globalInterface.TheHold.Cancel(); if ((status == 0) || (fbxOk == false)) // error saving max or fbx file { return(null); } return(new_filename); } return(null); }
public void UpdateMeshAnimation(ITriObject maxObject, IList<MyMeshKeyframe> keyframes) { if (keyframes.Count <= 0) { return; } Autodesk.Max.IMasterPointControl masterPointController = null; for (int i = 0; i < maxObject.NumSubs; i++) { /* Find the master point controller */ IAnimatable anim = maxObject.SubAnim(i); if (anim is Autodesk.Max.IMasterPointControl) { masterPointController = anim as Autodesk.Max.IMasterPointControl; break; } } if (masterPointController == null) { Log.Add("Could not find MasterPointController", LogLevel.Error); return; } int numberOfVerts = maxObject.Mesh.NumVerts; masterPointController.SetNumSubControllers(numberOfVerts, false); for (int v = 0; v < numberOfVerts; v++) { IControl controller = masterPointController.GetSubController(v); if (controller == null) { /* no key controller -> add a new one */ IClassDesc defaultPoint3ControllerClass = gi.GetDefaultController(Autodesk.Max.SClass_ID.CtrlPoint3); IControl newKeyController = gi.COREInterface.CreateInstance(SClass_ID.CtrlPoint3, defaultPoint3ControllerClass.ClassID) as IControl; masterPointController.SetSubController(v, newKeyController); controller = masterPointController.GetSubController(v); /* When creating a new controller, assign it as a subanimatable as well as a subcontroller */ masterPointController.AssignController(controller, v); } Autodesk.Max.IIKeyControl vertexKeyController = controller.GetInterface(InterfaceID.Keycontrol) as Autodesk.Max.IIKeyControl; vertexKeyController.NumKeys = keyframes.Count; for (int k = 0; k < keyframes.Count; k++) { IIBezPoint3Key key = gi.IBezPoint3Key.Create(); key.Time = (int)keyframes[k].Time; key.InLength.Set(0.5f, 0.5f, 0.5f); key.OutLength.Set(0.5f, 0.5f, 0.5f); key.Intan.Set(0, 0, 0); key.Outtan.Set(0, 0, 0); key.Val.Set( keyframes[k].VertexPositions[(v * 3) + 0], keyframes[k].VertexPositions[(v * 3) + 1], keyframes[k].VertexPositions[(v * 3) + 2]); vertexKeyController.SetKey(k, key); } } }
public static BaseNode CreateNode(IINode iNode, bool isPolyObject, bool isParentNode) { //We need to create our 'RealBaseNode' BaseNode tempBaseNode; if (isParentNode) { tempBaseNode = new ParentNode(iNode, isPolyObject); } else { tempBaseNode = new ChildNode(iNode, isPolyObject); } IObject baseObjectRef = iNode.ObjectRef.FindBaseObject(); if (!isPolyObject) { //Cast base object to triObject because we know it's a triObject ITriObject triObj = baseObjectRef as ITriObject; if (triObj == null) { return(null); //if for some reason the cast failed, return } IMesh triMesh = triObj.Mesh; var numFaces = triMesh.NumFaces; DebugMethods.Log(isParentNode? String.Format("ParentNode num face {0}", numFaces) : String.Format("ChildNode num face {0}", numFaces)); //Build FaceID Dictionary. for (int index = 0; index < numFaces; index++) { IFace face = triMesh.Faces[index]; ushort matID = face.MatID; if (tempBaseNode.DoesKeyExist(matID)) { tempBaseNode.SetMaterialIDBit(matID, index); } else { tempBaseNode.CreateNewMaterialBitArray(matID, index, numFaces); } } } else { IPolyObject polyObj = baseObjectRef as IPolyObject; if (polyObj == null) { return(null); //if for some reason the cast failed, return } IMNMesh polyMesh = polyObj.Mesh; var numFaces = polyMesh.FNum; DebugMethods.Log(isParentNode ? String.Format("ParentNode num face {0}", numFaces) : String.Format("ChildNode num face {0}", numFaces)); //Build FaceID Dictionary. for (int index = 0; index < numFaces; index++) { IMNFace face = polyMesh.F(index); ushort matID = face.Material; if (tempBaseNode.DoesKeyExist(matID)) { //Same material ID, just set the bit //tempBaseNode.GetMaterialBitArray(matID).Set(index, true); tempBaseNode.SetMaterialIDBit(matID, index); } else { tempBaseNode.CreateNewMaterialBitArray(matID, index, numFaces); } } } //We should have a 'RealBaseNode' with a filled up dictionary full of IDs & faces! //Maybe there is some weird object with 0 faces? if (tempBaseNode.GetMaterialIDCount() == 0) { return(null); } return(tempBaseNode); }