/// <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());
        }
        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);
        }