/// <summary>
        /// Adds an object space modifier to provided node (by handle).
        /// </summary>
        /// <param name="nodeHandle"> Input the node handle to add the modifier to. </param>
        /// <param name="cid"> Input the class id of the modifier add. </param>
        /// <returns> Returns 1 if successful or -1 if not. </returns>
        static public int AddOsmModifier(uint nodeHandle, IClass_ID cid)
        {
            try
            {
                IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                IInterface14 ip     = global.COREInterface14;

                IINode node = ip.GetINodeByHandle(nodeHandle);

                IObject         obj    = node.ObjectRef;
                IIDerivedObject dobj   = global.CreateDerivedObject(obj);
                object          objMod = ip.CreateInstance(SClass_ID.Osm, cid as IClass_ID);
                IModifier       mod    = (IModifier)objMod;

                dobj.AddModifier(mod, null, 0); // top of stack
                node.ObjectRef = dobj;
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return(-1);
            }

            return(1);
        }
Example #2
0
        public override void CustomExecute(object parameter)
        {
            try
            {
                IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                IInterface14 ip     = global.COREInterface14;

                int nNumSelNodes = ip.SelNodeCount;
                if (nNumSelNodes <= 0)
                {
                    ip.PushPrompt("No nodes are selected. Please select at least one node to convert, before running the command.");
                    return;
                }

                System.Windows.Window dialog = new System.Windows.Window();
                dialog.Title         = "Explode It!";
                dialog.SizeToContent = System.Windows.SizeToContent.WidthAndHeight;
                ExplodeGeomUserControl1 ctlExplode = new ExplodeGeomUserControl1(dialog);
                dialog.Content = ctlExplode;
                dialog.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterOwner;
                dialog.ShowInTaskbar         = false;
                dialog.ResizeMode            = System.Windows.ResizeMode.NoResize;

                System.Windows.Interop.WindowInteropHelper windowHandle =
                    new System.Windows.Interop.WindowInteropHelper(dialog);
                windowHandle.Owner = ManagedServices.AppSDK.GetMaxHWND();
                ManagedServices.AppSDK.ConfigureWindowForMax(dialog);

                dialog.ShowDialog(); //modal version; this prevents changes being made to model while our dialog is running, etc.
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
            }
        }
Example #3
0
        public override int Proc(IntPtr hwnd, int msg, int point, int flags, IIPoint2 m)
        {
            Debug.Print("viewport point: " + m.X + ", " + m.Y);
            switch (msg)
            {
            case 0:     // MOUSE_ABORT see .\maxsdk\include\mouseman.h for all possible values
            {
                IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                IInterface14 ip     = global.COREInterface14;
                ip.PopCommandMode();
                Debug.Print("mouse callback aborted");
            }
            break;

            case 1:     // MOUSE_POINT see .\maxsdk\include\mouseman.h for all possible values
            {
                if (point == 0)
                {
                    IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                    IInterface14 ip     = global.COREInterface14;
                    IViewExp     vp     = ip.ActiveViewExp;
                    IPoint3      pt     = vp.SnapPoint(m, m, null, SnapFlags.InPlane);
                    ip.PopCommandMode();
                    Debug.Print("3d point (in plane): " + pt.X + ", " + pt.Y + ", " + pt.Z);
                }
                // else if ... other points if you have more than one...
            }
            break;

            default:
                break;
            }
            return(1);
        }
Example #4
0
 public static void AssemblyMain()
 {
     Global = GlobalInterface.Instance;
     Core = Global.COREInterface14;
     Class_ID = Global.Class_ID.Create(0x8217f123, 0xef980456);
     Core.AddClass(new Descriptor());
 }
Example #5
0
        public override void Execute(object parameter)
        {
            IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 ip     = global.COREInterface14;

            try
            {
                if (mainDialog == null)
                {
                    mainDialog = new ApplicationView();
                }
                if (mainDialog.Visibility == Visibility.Hidden)
                {
                    isopen = false;
                }
                if (!isopen)
                {
                    isopen             = true;
                    mainDialog.Closed += new EventHandler(SearchSimilar_Closed);
                    System.Windows.Interop.WindowInteropHelper windowHandle = new System.Windows.Interop.WindowInteropHelper(mainDialog);
                    windowHandle.Owner = ManagedServices.AppSDK.GetMaxHWND();
                    ManagedServices.AppSDK.ConfigureWindowForMax(mainDialog);
                    mainDialog.Show();
                }
                else
                {
                    isopen = false;
                    mainDialog.Hide();
                }
            }
            catch (Exception e)
            {
                Debug.Print("Exception occurred: " + e.Message);
            }
        }
Example #6
0
 public static void AssemblyMain()
 {
     Global   = GlobalInterface.Instance;
     Core     = Global.COREInterface14;
     Class_ID = Global.Class_ID.Create(0x8217f123, 0xef980456);
     Core.AddClass(new Descriptor());
 }
        /// <summary>
        /// Adds the Shell modifier to the provided node (by handle).
        /// </summary>
        /// <param name="nodeHandle"> Input the node handle to add the modifier to. </param>
        /// <param name="shellAmount"> Input the amount of shell thickness as float. </param>
        /// <returns> Returns 1 if successful or -1 if not. </returns>
        static public int AddOsmShell(uint nodeHandle, float shellAmount)
        {
            try
            {
                IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                IInterface14 ip     = global.COREInterface14;

                IClass_ID cidOsmShell = global.Class_ID.Create(0x3b9b1a16, 0x6d84e8d0);
                AddOsmModifier(nodeHandle, cidOsmShell);

                IINode    node = ip.GetINodeByHandle(nodeHandle);
                IModifier mod  = GetModifier(node, cidOsmShell);
                if (mod != null)
                {
                    IIParamBlock2 pb = mod.GetParamBlock(0);
                    pb.SetValue(0, 0, shellAmount, 0); // innerAmount parameter is at index zero of the parameter block.
                }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return(-1);
            }

            return(1);
        }
Example #8
0
        public override void ExitMode()
        {
            IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 ip     = global.COREInterface14;

            ip.PopPrompt();
            return;
        }
Example #9
0
        public override void EnterMode()
        {
            IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 ip     = global.COREInterface14;

            ip.PushPrompt("Select a point:");
            return;
        }
Example #10
0
        public ExplodeGeomUserControl1(System.Windows.Window creator)
        {
            m_winParent = creator;
            InitializeComponent();

            IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 ip     = global.COREInterface14;

            IIColorManager cm = global.ColorManager;

            System.Drawing.Color dcolBack = cm.GetColor(Autodesk.Max.GuiColors.Background, Autodesk.Max.IColorManager.State.Normal);

            // due to a bug in the 3ds Max .NET API before 2017, you would have to reverse the R and B values to assign color properly.
            // The Autodesk.Max assembly mapped them incorrectly
            // pre 2017: System.Windows.Media.Color mcolorBack = System.Windows.Media.Color.FromRgb(dcolBack.B, dcolBack.G, dcolBack.R);

            // Get current background color and match our dialog to it
            System.Windows.Media.Color mcolorBack = System.Windows.Media.Color.FromRgb(dcolBack.R, dcolBack.G, dcolBack.B);
            Brush colorBack = new SolidColorBrush(mcolorBack);

            // Note, if you want just a fixed color, you can comment this out and use the XAML defined value.
            LayoutRoot.Background = colorBack;

            // Get current text color and match our dialog to it.
            System.Drawing.Color dcolText = cm.GetColor(Autodesk.Max.GuiColors.Text, Autodesk.Max.IColorManager.State.Normal);
            // pre 2017: System.Windows.Media.Color mcolorText = System.Windows.Media.Color.FromRgb(dcolText.B, dcolText.G, dcolText.R);
            System.Windows.Media.Color mcolorText = System.Windows.Media.Color.FromRgb(dcolText.R, dcolText.G, dcolText.B);
            Brush colorText = new SolidColorBrush(mcolorText);

            // To use pure white, we can just set a system brush.
            //Brush colorText = Brushes.White;

            m_gbExplodeTypes.Foreground = colorText;
            m_rbTriangles.Foreground    = colorText;
            m_rbPolygons.Foreground     = colorText;

            m_gbExplodeOptions.Foreground = colorText;
            m_cbConvertTri.Foreground     = colorText;
            m_cbConvertPoly.Foreground    = colorText;
            m_cbAddShell.Foreground       = colorText;
            m_lblNumOffset.Foreground     = colorText;
            m_cbAddEditMesh.Foreground    = colorText;
            m_cbCollapseStack.Foreground  = colorText;
            m_cbCenterPivot.Foreground    = colorText;
            m_cbDeleteOriginal.Foreground = colorText;
            // This is a button control, and we are not setting its color.
            // So we will not change the text color either.
            //m_btnExplodeIt.Foreground = colorText;

            m_lblLabelProNode.Foreground = colorText;
            m_lblNodeName.Foreground     = colorText;
            m_lblLabelNode.Foreground    = colorText;
            m_lblCurrNode.Foreground     = colorText;
            m_lblLabelOf.Foreground      = colorText;
            m_lblTotNode.Foreground      = colorText;
            m_lblLabelEscape.Foreground  = colorText;
        }
Example #11
0
        public override void Execute(object parameter)
        {
            IGlobal global = Autodesk.Max.GlobalInterface.Instance;

            IInterface14 ip = global.COREInterface14;

            AdnCommandMode mode = new AdnCommandMode();

            ip.PushCommandMode(mode);
        }
Example #12
0
 static void Initialize()
 {
     if (global == null)
     {
         global   = GlobalInterface.Instance;
         core     = global.COREInterface14;
         Class_ID = global.Class_ID.Create(0x8217f123, 0xef980456);
         core.AddClass(new Descriptor());
     }
 }
        /// <summary>
        /// Information sent to this LogTrace will appear on the Design Automation output
        /// </summary>
        public static void LogTrace(string format, params object[] args)
        {
            IGlobal      globalInterface = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 coreInterface   = globalInterface.COREInterface14;
            ILogSys      log             = coreInterface.Log;

            // Note flags are necessary to produce Design Automation output. This is same as C++:
            // SYSLOG_INFO | SYSLOG_IGNORE_VERBOSITY | SYSLOG_BROADCAST
            log.LogEntry(0x00000004 | 0x00040000 | 0x00010000, false, "", string.Format(format, args));
        }
Example #14
0
        public ROD_ExportG()
        {
            maxGlobal    = Autodesk.Max.GlobalInterface.Instance;
            maxInterface = maxGlobal.COREInterface14;
            IPoint3 U = maxGlobal.Point3.Create(1.0, 0.0, 0.0);
            IPoint3 V = maxGlobal.Point3.Create(0.0, 0.0, 1.0);
            IPoint3 N = maxGlobal.Point3.Create(0.0, -1.0, 0.0);
            IPoint3 T = maxGlobal.Point3.Create(0.0, 0.0, 0.0);

            _leftHanded  = maxGlobal.Matrix3.Create(U, V, N, T);
            _GleftHanded = maxGlobal.GMatrix.Create(_leftHanded);
        }
        /// <summary>
        /// Function to specifically update Case Windows with input wedth and height parameters
        /// </summary>
        /// <param name="width">The new Width to set the Window</param>
        /// <param name="height">The new Height to set the Window</param>
        /// <returns>window count</returns>
        static public int UpdateWindowNodes(float width, float height)
        {
            IGlobal      globalInterface = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 coreInterface   = globalInterface.COREInterface14;

            IINode nodeRoot = coreInterface.RootNode;

            m_sceneNodes.Clear();
            GetSceneNodes(nodeRoot);

            // 3ds Max uses a class ID for all object types. This is easiest way to find specific type.
            // ClassID (1902665597L, 1593788199L) == 0x71685F7D, 0x5EFF4727 for casement window
            IClass_ID cidCasementWindow = globalInterface.Class_ID.Create(0x71685F7D, 0x5EFF4727);

            // Use LINQ to filter for windows only - in case scene has more than one,
            // but this should still give us at least one for single window scene!
            var sceneWindows = from node in m_sceneNodes
                               where ((node.ObjectRef != null) && // In some cases the ObjectRef can be null for certain node types.
                                      (node.ObjectRef.ClassID.PartA == cidCasementWindow.PartA) &&
                                      (node.ObjectRef.ClassID.PartB == cidCasementWindow.PartB))
                               select node;

            // Iterate the casement windws and update the hight and width parameters.
            foreach (IINode item in sceneWindows)
            {
                // window is using old-style ParamArray rather than newer ParamBlk2
                IIParamArray pb = item.ObjectRef.ParamBlock;
                pb.SetValue(0, coreInterface.Time, height); // window height is at index zero.
                pb.SetValue(1, coreInterface.Time, width);  // window width is at index one.
            }

            // If there are windows, save the window updates
            int status;

            if (sceneWindows.Count() > 0)
            {
                // The output file name must match what the Design Automation work item is specifying as output file.
                string full_filename = coreInterface.CurFilePath;
                string filename      = coreInterface.CurFileName;
                string new_filename  = full_filename.Replace(filename, "outputFile.max");
                status = coreInterface.SaveToFile(new_filename, true, false);
                if (status == 0) //error
                {
                    return(-1);
                }
            }

            // return how many windows were modified.
            return(sceneWindows.Count());
        }
        /// <summary>
        /// Information sent to this LogTrace will appear on the Design Automation output
        /// </summary>
        private static void LogTrace(string format, params object[] args)
        {
            System.Reflection.Assembly a = System.Reflection.Assembly.GetExecutingAssembly();
            string output_msg            = string.Format("DLL {0} compiled on {1}; {2}",
                                                         System.IO.Path.GetFileName(a.Location),
                                                         File.GetLastWriteTime(a.Location),
                                                         string.Format(format, args));

            IGlobal      globalInterface = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 coreInterface   = globalInterface.COREInterface14;
            ILogSys      log             = coreInterface.Log;

            // Note flags are necessary to produce Design Automation output. This is same as C++:
            // SYSLOG_INFO | SYSLOG_IGNORE_VERBOSITY | SYSLOG_BROADCAST
            log.LogEntry(0x00000004 | 0x00040000 | 0x00010000, false, "", output_msg);
        }
        /// <summary>
        /// Adds the Edit Mesh modifier to the provided node (by handle).
        /// </summary>
        /// <param name="nodeHandle"> Input the node handle to add the modifier to. </param>
        /// <returns> Returns 1 if successful or -1 if not. </returns>
        static public int AddOsmEditMesh(uint nodeHandle)
        {
            try
            {
                IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                IInterface14 ip     = global.COREInterface14;

                IClass_ID cidOsmEditMesh = global.Class_ID.Create(0x00050, 0);
                AddOsmModifier(nodeHandle, cidOsmEditMesh);
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return(-1);
            }

            return(1);
        }
        /// <summary>
        /// Adds the Shell modifier to the provided node (by handle).
        /// </summary>
        /// <param name="nodeHandle"> Input the node handle to add the modifier to. </param>
        /// <param name="shellAmount"> Input the amount of shell thickness as float. </param>
        /// <returns> Returns 1 if successful or -1 if not. </returns>
        static public int AddOsmProoptimizer(IINode node, float VertexPercent, bool KeepNormals)
        {
            try
            {
                IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
                IInterface14 ip     = global.COREInterface14;
                int          t      = ip.Time;

                // classID:#(1056067556, 1496462090)
                IClass_ID cidOsmProoptimizer = global.Class_ID.Create(0x3EF24FE4, 0x5932330A);
                AddOsmModifier(node, cidOsmProoptimizer);

                IModifier mod = GetModifier(node, cidOsmProoptimizer);
                if (mod != null)
                {
                    // In order to get the "Calculate" parameter to trigger the modifier to execute, we have to enable some UI elements.
                    ip.CmdPanelOpen = true;      // ensures the command panel in general is open
                    ip.SelectNode(node, true);   // Select the node to make it active
                    ip.CommandPanelTaskMode = 2; // TASK_MODE_MODIFY. This makes the modifier panel active.
                    // Now we can set the parameters on the modifier, and at end "calculate" the results.
                    IIParamBlock2 pb = mod.GetParamBlock(0);
                    pb.SetValue((int)ProOptimizerPBValues.optimizer_main_ratio, t, VertexPercent, 0);
                    pb.SetValue((int)ProOptimizerPBValues.optimizer_options_keep_uv, t, 1, 0);
                    pb.SetValue((int)ProOptimizerPBValues.optimizer_options_keep_normals, t, 0, 0);
                    // There is no true way to know if this was valid/invalid for the mesh, so we check the outer level routine on triobject for changes. **
                    pb.SetValue((int)ProOptimizerPBValues.optimizer_main_calculate, t, 1, 0);
                    ip.ClearNodeSelection(false);
                }
            }
            catch (Exception ex)
            {
                Debug.Print(ex.Message);
                return(-1);
            }

            return(1);
        }
Example #19
0
        private void m_btnExplodeIt_Click(object sender, RoutedEventArgs e)
        {
            //
            IGlobal      global = Autodesk.Max.GlobalInterface.Instance;
            IInterface14 ip     = global.COREInterface14;

            this.m_pnlProgressPanel.Visibility = System.Windows.Visibility.Visible;

            try
            {
                global.TheHold.Begin();

                ADN_Utility.SetProgressControl(this);

                bool convertPoly = EI_ConvertTypePoly; // true = poly, false = tri
                bool attemptConvert;
                if (convertPoly)
                {
                    attemptConvert = EI_AttemptConvertToPoly;
                }
                else
                {
                    attemptConvert = EI_AttemptConvertToTri;
                }
                bool  addShell      = EI_AddShellModifier;
                float shellAmount   = EI_ShellAmount;
                bool  addEditMesh   = EI_AddEditMeshModifier;
                bool  collapseStack = EI_CollapseStack;
                bool  deleteNode    = EI_DeleteOriginal;

                //ip.DisableSceneRedraw();
                int stat         = 0;
                int nNumSelNodes = ip.SelNodeCount;
                m_lblTotNode.Content = nNumSelNodes.ToString();

                for (int i = 0; i < nNumSelNodes; i++)
                {
                    IINode nodeCur = ip.GetSelNode(i);
                    m_lblNodeName.Content = nodeCur.Name;
                    m_lblCurrNode.Content = i + 1;
                    if (convertPoly)
                    {
                        stat = ADN_Utility.ConvertToPolygonFaces(nodeCur.Handle, attemptConvert, addShell, shellAmount, addEditMesh, collapseStack);
                        if (stat < 0)
                        {
                            break;
                        }
                    }
                    else
                    {
                        stat = ADN_Utility.ConvertToTriangleFaces(nodeCur.Handle, attemptConvert, addShell, shellAmount, addEditMesh, collapseStack);
                        if (stat < 0)
                        {
                            break;
                        }
                    }
                }


                if (stat < 0)
                {
                    global.TheHold.Cancel();
                }
                else
                {
                    // now we need to start at the top to delete the original nodes.
                    if (deleteNode)
                    {
                        IINodeTab tabNodes = global.INodeTabNS.Create();
                        ip.GetSelNodeTab(tabNodes);
                        if (tabNodes != null)
                        {
                            ip.DeleteNodes(tabNodes, true, true, false);
                        }
                    }
                    global.TheHold.Accept("ADN-PolygonExplode");
                }

                ip.RedrawViews(0, RedrawFlags.Normal, null);
            }
            catch
            {
                global.TheHold.Cancel();
            }

            this.m_pnlProgressPanel.Visibility = System.Windows.Visibility.Hidden;
            ADN_Utility.ClearProgressControl(this);

            m_winParent.Close();
            m_bOk = true;
        }
Example #20
0
 public static void AssemblyMain()
 {
     Global = Autodesk.Max.GlobalInterface.Instance;
     Core   = Global.COREInterface14;
     Core.AddClass(new Descriptor());
 }
Example #21
0
 public static void AssemblyMain()
 {
     Global = Autodesk.Max.GlobalInterface.Instance;
     Core = Global.COREInterface14;
     Core.AddClass(new Descriptor()); 
 }
Example #22
0
 public static void AssemblyMain()
 {
     Global = GlobalInterface.Instance;    //初始化全局实例
     Core   = Global.COREInterface14;
     Core.AddClass(new Descriptor());      //初始化插件描述
 }
        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);
        }
        /// <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);
        }