/// <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); }
internal static unsafe void ExplodeNode(BaseNode baseNode, IPoint3 moveValue, ushort matID) { //A node is a part of an IIINode, it's a matID part of the real IINode var node = baseNode.INode; var world = node.GetObjTMAfterWSM(0, null); var local = world; if (!node.IsRootNode) { IMatrix3 m3Parent = node.ParentNode.GetObjTMAfterWSM(0, null); local = world.Multiply(m_Global.Inverse(m3Parent)); } //We basically need to keep all the verts - but this could be millions, or we could keep an ID //IINode inode = baseNode.Node; //string sKey = matID.ToString(); //string sMoveValue = string.Format("[{0};{1};{2}]", moveValue.X, moveValue.Y, moveValue.Z); //inode.SetUserPropString(ref sKey, ref sMoveValue); //--------------------------------------------------------------------------------------------- if (baseNode.Mesh.TriMesh != null) //mesh { var mesh = baseNode.Mesh.TriMesh; BitArray facesPerID = baseNode.GetMaterialBitArray(matID); HashSet <int> uniqueVertexIndexList = new HashSet <int>(); //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IFace face = mesh.Faces[i]; IntPtr vertsIndices = face.AllVerts; uint *dwordVertIndices = (uint *)vertsIndices.ToPointer(); for (int j = 0; j < 3; j++) { var vertexIndex = (int)dwordVertIndices[j]; uniqueVertexIndexList.Add(vertexIndex); } } } foreach (var vertexIndex in uniqueVertexIndexList) { var localPos = mesh.GetVert(vertexIndex); var finalPos = CalculateWorldPosVerts(localPos, moveValue, local); mesh.SetVert(vertexIndex, finalPos); } } else //poly { var mesh = baseNode.Mesh.PolyMesh; //Get the bitArray for each ID BitArray facesPerID = baseNode.GetMaterialBitArray(matID); HashSet <int> uniqueVertexIndexList = new HashSet <int>(); //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IMNFace face = mesh.F(i); IList <int> vertIndices = face.Vtx; foreach (var vertexIndex in vertIndices) { uniqueVertexIndexList.Add(vertexIndex); } } } foreach (var vertexIndex in uniqueVertexIndexList) { var localPos = mesh.P(vertexIndex); var finalPos = CalculateWorldPosVerts(localPos, moveValue, local); mesh.V(vertexIndex).P = finalPos; } } }
public static unsafe void BuildBoundingBox(ParentNode parentNode, ushort matID) { var node = parentNode.INode; var world = node.GetObjTMAfterWSM(0, null); var local = world; if (!node.IsRootNode) { IMatrix3 m3Parent = node.ParentNode.GetObjTMAfterWSM(0, null); local = world.Multiply(m_Global.Inverse(m3Parent)); } if (parentNode.Mesh.TriMesh != null) //mesh { var mesh = parentNode.Mesh.TriMesh; BitArray facesPerID = parentNode.GetMaterialBitArray(matID); float?xMaxValue = null, xMinValue = null; float?yMaxValue = null, yMinValue = null; float?zMaxValue = null, zMinValue = null; IPoint3 maxValues, minValues; //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IFace face = mesh.Faces[i]; IntPtr vertsIndices = face.AllVerts; uint *dwordVertIndices = (uint *)vertsIndices.ToPointer(); for (int j = 0; j < 3; j++) { var vertexIndex = (int)dwordVertIndices[j]; //Maybe don't hold giant lists of vertex indices - just do it while we loop over them all //TODO very important check if this is necessary //parentNode.MaterialIDVertexList.Add(vertexIndex); var pos = mesh.GetVert(vertexIndex); pos = local.PointTransform(pos); FindMaxMinValues(ref xMaxValue, ref xMinValue, ref yMaxValue, ref yMinValue, ref zMaxValue, ref zMinValue, pos); } } } maxValues = m_Global.Point3.Create(xMaxValue.Value, yMaxValue.Value, zMaxValue.Value); minValues = m_Global.Point3.Create(xMinValue.Value, yMinValue.Value, zMinValue.Value); var bb = new BoundingBox(maxValues, minValues); parentNode.SetBoundingBox(matID, bb); } else { var mesh = parentNode.Mesh.PolyMesh; //Get the bitArray for each ID BitArray facesPerID = parentNode.GetMaterialBitArray(matID); float?xMaxValue = null, xMinValue = null; float?yMaxValue = null, yMinValue = null; float?zMaxValue = null, zMinValue = null; IPoint3 maxValues, minValues; //loop through the bitarray to see which faces are set. For the set faces, save the verts for (int i = 0; i < facesPerID.Count; i++) { if (facesPerID[i]) { //This should be a singular face that has the defined ID IMNFace face = mesh.F(i); IList <int> vertIndices = face.Vtx; foreach (var vertexIndex in vertIndices) { //parentNode.MaterialIDVertexList.Add(vertexIndex); var pos = mesh.P(vertexIndex); pos = local.PointTransform(pos); FindMaxMinValues(ref xMaxValue, ref xMinValue, ref yMaxValue, ref yMinValue, ref zMaxValue, ref zMinValue, pos); } } } maxValues = m_Global.Point3.Create(xMaxValue.Value, yMaxValue.Value, zMaxValue.Value); minValues = m_Global.Point3.Create(xMinValue.Value, yMinValue.Value, zMinValue.Value); var bb = new BoundingBox(maxValues, minValues); parentNode.SetBoundingBox(matID, bb); } }
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); }
public static ushort?UpdateFaceDictionary(BaseNode node) { var triMesh = node.Mesh.TriMesh; var polyMesh = node.Mesh.PolyMesh; var faceCountPerID = new Dictionary <ushort, int>(); //Get numFaces of each ID foreach (var ids in node.GetUsedMaterialIDsArray()) { faceCountPerID.Add(ids, GetCardinality(node.GetMaterialBitArray(ids))); } //clear the materialFaceDictionary node.ClearMaterialBitArray(); if (node.Mesh.TriMesh != null) { var numFaces = triMesh.NumFaces; var parentNode = node as ParentNode; //Build FaceID Dictionary. for (int index = 0; index < numFaces; index++) { IFace face = triMesh.Faces[index]; ushort matID = face.MatID; //This means it's a child node if (parentNode == null) { UpdateNode(node, matID, index, numFaces); } //So it's a parentNode, but we don't want to update delete material IDs so check if that's not true else if (!parentNode.IsMatIDDeleted(matID)) { UpdateNode(node, matID, index, numFaces); } } } else { var numFaces = polyMesh.FNum; var parentNode = node as ParentNode; //Build FaceID Dictionary. for (int index = 0; index < numFaces; index++) { IMNFace face = polyMesh.F(index); ushort matID = face.Material; if (parentNode == null) { UpdateNode(node, matID, index, numFaces); } else if (!parentNode.IsMatIDDeleted(matID)) { UpdateNode(node, matID, index, numFaces); } } } foreach (var ids in node.GetUsedMaterialIDsArray()) { int numFaces; if (!faceCountPerID.TryGetValue(ids, out numFaces)) { continue; } if (numFaces != GetCardinality(node.GetMaterialBitArray(ids))) { return(ids); } } return(null); }