/// <summary> /// Return the custom attributes of a material /// </summary> /// <param name="materialNode"></param> /// <param name="babylonScene"></param> /// <param name="excludeAttributes">Attribute names to not export</param> public Dictionary <string, object> ExportExtraAttributes(IIGameNode gameNode, BabylonScene babylonScene, List <string> excludeAttributes = null) { // Retreive the max object ScriptsUtilities.ExecuteMaxScriptCommand("obj = execute(\"$'" + gameNode.MaxNode.Handle + "'\");"); return(_ExportExtraAttributes(gameNode.IGameObject.IPropertyContainer, babylonScene, excludeAttributes)); }
public async void butExport_Click(object sender, EventArgs e) { treeView.Nodes.Clear(); exportParameters = GetExportParameters(); PreExportProcess preExportProcess = new PreExportProcess(exportParameters, logger); bool abort = false; try { abort = AutosaveWarning(); if (abort) { return; } if (exportParameters.usePreExportProcess) { Loader.Core.FileHold(); preExportProcess.ApplyPreExport(); } await DoExport(singleExportItem); } catch (Exception ex) { logger.RaiseError(ex.Message); } finally { if (exportParameters.usePreExportProcess && !exportParameters.applyPreprocessToScene) { ScriptsUtilities.ExecuteMaxScriptCommand("fetchMaxFile quiet:true"); } } }
/// <summary> /// Return the custom attributes of a material /// </summary> /// <param name="materialNode"></param> /// <param name="babylonScene"></param> /// <param name="excludeAttributes">Attribute names to not export</param> public Dictionary <string, object> ExportExtraAttributes(IIGameMaterial gameMaterial, BabylonScene babylonScene, List <string> excludeAttributes = null) { // Retreive the max object ScriptsUtilities.ExecuteMaxScriptCommand("obj = sceneMaterials[\"" + gameMaterial.MaterialName + "\"];"); return(_ExportExtraAttributes(gameMaterial.IPropertyContainer, babylonScene, excludeAttributes)); }
private async void butMultiExport_Click(object sender, EventArgs e) { treeView.Nodes.Clear(); string outputFileExt = comboOutputFormat.SelectedItem.ToString(); if (outputFileExt.Contains("binary babylon")) { outputFileExt = "babylon"; } ExportItemList exportItemList = new ExportItemList(outputFileExt); exportItemList.LoadFromData(); int numLoadedItems = exportItemList.Count; if (ModifierKeys == Keys.Shift) { MultiExportForm form = new MultiExportForm(exportItemList); form.ShowDialog(this); } else if (numLoadedItems > 0) { exportParameters = GetExportParameters(); PreExportProcess preExportProcess = new PreExportProcess(exportParameters, logger); if (logger != null) { preExportProcess.logger = logger; } bool abort = false; try { abort = AutosaveWarning(); if (abort) { return; } if (exportParameters.usePreExportProcess) { Loader.Core.FileHold(); preExportProcess.ApplyPreExport(); } await DoExport(exportItemList); } catch (Exception ex) { logger.RaiseError(ex.Message); } finally { if (exportParameters.usePreExportProcess && !exportParameters.applyPreprocessToScene) { ScriptsUtilities.ExecuteMaxScriptCommand("fetchMaxFile quiet:true"); } } } }
public static void FlattenHierarchy(this IINode node) { ////todo: replace this with C# string convertToEditablePoly = $"ConvertTo (maxOps.getNodeByHandle {node.Handle}) Editable_Poly"; ScriptsUtilities.ExecuteMaxScriptCommand(convertToEditablePoly); IPolyObject polyObject = node.GetPolyObjectFromNode(); IEPoly flattenEPoly = (IEPoly)polyObject.GetInterface(Loader.EditablePoly); IINodeTab toflatten = Loader.Global.NodeTab.Create(); foreach (IINode n in node.NodeTree()) { toflatten.AppendNode(n, false, 1); } flattenEPoly.EpfnMultiAttach(toflatten, node, Loader.Core.Time); }
private void AddCallbacks() { // Retreive the material just created string cmd = "maxMaterial = callbacks.notificationParam();"; // Easy syntax for a switch/case expression cmd += "\r\n" + "if classof maxMaterial == StandardMaterial then"; cmd += "\r\n" + "("; cmd += "\r\n" + BabylonExporter.GetStandardBabylonAttributesDataCA(); cmd += "\r\n" + "custAttributes.add maxMaterial babylonAttributesDataCA;"; cmd += "\r\n" + ")"; cmd += "\r\n" + "else if classof maxMaterial == PhysicalMaterial then"; cmd += "\r\n" + "("; cmd += "\r\n" + BabylonExporter.GetPhysicalBabylonAttributesDataCA(); cmd += "\r\n" + "custAttributes.add maxMaterial babylonAttributesDataCA;"; cmd += "\r\n" + ")"; cmd += "\r\n" + "else if classof maxMaterial == ai_standard_surface then"; cmd += "\r\n" + "("; cmd += "\r\n" + BabylonExporter.GetAiStandardSurfaceBabylonAttributesDataCA(); cmd += "\r\n" + "custAttributes.add maxMaterial babylonAttributesDataCA;"; cmd += "\r\n" + ")"; // Escape cmd cmd = cmd.Replace("\"", "\\\""); // Create cmd as string ScriptsUtilities.ExecuteMaxScriptCommand("cmd = \"" + cmd + "\""); // Remove any definition of this callback ScriptsUtilities.ExecuteMaxScriptCommand("callbacks.removeScripts id:#BabylonAttributesMaterial;"); // Add a callback triggered when a new material is created // Note: // The callback is NOT persistent (default value is false). // This means that it is not linked to a specific file. // Rather, the callback is active for the current run of 3ds Max. // See Autodesk documentation for more info: http://help.autodesk.com/view/3DSMAX/2015/ENU/?guid=__files_GUID_C1F6495F_5831_4FC8_A00C_667C5F2EAE36_htm ScriptsUtilities.ExecuteMaxScriptCommand("callbacks.addScript #mtlRefAdded cmd id:#BabylonAttributesMaterial;"); }
private async Task <bool> DoExport(ExportItem exportItem, bool multiExport = false, bool clearLogs = true) { new BabylonAnimationActionItem().Close(); SaveOptions(); //store layer visibility status and force visibility on Dictionary <IILayer, bool> layerState = new Dictionary <IILayer, bool>(); if (exportItem.Layers != null) { foreach (IILayer layer in exportItem.Layers) { List <IILayer> treeLayers = layer.LayerTree().ToList(); treeLayers.Add(layer); foreach (IILayer l in treeLayers) { #if MAX2015 layerState.Add(l, l.IsHidden); #else layerState.Add(l, l.IsHidden(false)); #endif l.Hide(false, false); } } } exporter = new BabylonExporter(); if (clearLogs) { treeView.Nodes.Clear(); } exporter.OnExportProgressChanged += progress => { progressBar.Value = progress; Application.DoEvents(); }; exporter.OnWarning += (warning, rank) => CreateWarningMessage(warning, rank); exporter.OnError += (error, rank) => CreateErrorMessage(error, rank); exporter.OnMessage += (message, color, rank, emphasis) => CreateMessage(message, color, rank, emphasis); exporter.OnVerbose += (message, color, rank, emphasis) => { try { currentNode = CreateTreeNode(rank, message, color); if (emphasis) { currentNode.EnsureVisible(); } } catch { //do nothing } Application.DoEvents(); }; butExport.Enabled = false; butExportAndRun.Enabled = false; butMultiExport.Enabled = false; butCancel.Enabled = true; bool success = true; try { string modelAbsolutePath = multiExport ? exportItem.ExportFilePathAbsolute : txtModelPath.Text; string textureExportPath = multiExport ? exportItem.ExportTexturesesFolderAbsolute : txtTexturesPath.Text; var scaleFactorParsed = 1.0f; var textureQualityParsed = 100L; try { scaleFactorParsed = float.Parse(txtScaleFactor.Text); } catch (Exception e) { throw new InvalidDataException(String.Format("Invalid Scale Factor value: {0}", txtScaleFactor.Text)); } try { textureQualityParsed = long.Parse(txtQuality.Text); } catch (Exception e) { throw new InvalidDataException(String.Format("Invalid Texture Quality value: {0}", txtScaleFactor.Text)); } MaxExportParameters exportParameters = new MaxExportParameters { outputPath = modelAbsolutePath, textureFolder = textureExportPath, outputFormat = comboOutputFormat.SelectedItem.ToString(), scaleFactor = scaleFactorParsed, writeTextures = chkWriteTextures.Enabled && chkWriteTextures.Checked, overwriteTextures = chkOverwriteTextures.Enabled && chkOverwriteTextures.Checked, exportHiddenObjects = chkHidden.Checked, exportOnlySelected = chkOnlySelected.Checked, generateManifest = chkManifest.Checked, autoSaveSceneFile = chkAutoSave.Checked, exportTangents = chkExportTangents.Checked, exportMorphTangents = chkExportMorphTangents.Checked, exportMorphNormals = chkExportMorphNormals.Checked, txtQuality = textureQualityParsed, mergeAOwithMR = chkMergeAOwithMR.Enabled && chkMergeAOwithMR.Checked, bakeAnimationType = (BakeAnimationType)cmbBakeAnimationOptions.SelectedIndex, dracoCompression = chkDracoCompression.Enabled && chkDracoCompression.Checked, enableKHRLightsPunctual = chkKHRLightsPunctual.Checked, enableKHRTextureTransform = chkKHRTextureTransform.Checked, enableKHRMaterialsUnlit = chkKHRMaterialsUnlit.Checked, exportMaterials = chkExportMaterials.Enabled && chkExportMaterials.Checked, exportAnimations = chkExportAnimations.Checked, exportAnimationsOnly = chkExportAnimationsOnly.Checked, optimizeAnimations = !chkDoNotOptimizeAnimations.Checked, animgroupExportNonAnimated = chkAnimgroupExportNonAnimated.Checked, exportNode = exportItem?.Node, exportLayers = exportItem?.Layers, pbrNoLight = chkNoAutoLight.Checked, pbrFull = chkFullPBR.Checked, pbrEnvironment = txtEnvironmentName.Text, usePreExportProcess = chkUsePreExportProces.Checked, flattenScene = chkFlatten.Enabled && chkFlatten.Checked, mergeContainersAndXRef = chkMrgContainersAndXref.Checked, useMultiExporter = multiExport }; exporter.callerForm = this; exporter.Export(exportParameters); } catch (OperationCanceledException) { progressBar.Value = 0; success = false; ScriptsUtilities.ExecuteMaxScriptCommand(@"global BabylonExporterStatus = ""Available"""); } catch (Exception ex) { IUTF8Str operationStatus = GlobalInterface.Instance.UTF8Str.Create("BabylonExportAborted"); Loader.Global.BroadcastNotification(SystemNotificationCode.PreExport, operationStatus); currentNode = CreateTreeNode(0, "Export cancelled: " + ex.Message, Color.Red); currentNode = CreateTreeNode(1, ex.ToString(), Color.Red); currentNode.EnsureVisible(); progressBar.Value = 0; success = false; ScriptsUtilities.ExecuteMaxScriptCommand(@"global BabylonExporterStatus = Available"); } butCancel.Enabled = false; butExport.Enabled = true; butMultiExport.Enabled = true; butExportAndRun.Enabled = WebServer.IsSupported; BringToFront(); //re-store layer visibility status if (exportItem.Layers != null) { foreach (IILayer layer in exportItem.Layers) { List <IILayer> treeLayers = layer.LayerTree().ToList(); treeLayers.Add(layer); foreach (IILayer l in treeLayers) { bool exist; layerState.TryGetValue(l, out exist); if (exist) { l.Hide(layerState[l], false); } } } } return(success); }
public static IINode FlattenHierarchyMS(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(); string groupName = node.Name + Random.Next(1, 100000); node.Name = groupName; //use the default one in the future. for some reason uv ar broken with C# version ScriptsUtilities.ExecuteMaxScriptCommand($@" groupNode = maxOps.getNodeByHandle {node.Handle.ToString()} meshChildren = #() for c in groupNode.children do ( if superclassof c == GeometryClass then ( append meshChildren c ) ) obj = mesh vertices:#([0,0,0],[10,0,0],[0,10,0]) faces:#([1,2,3]) name:groupNode.name transform:groupNode.transform converttopoly obj multimat_new=#() for obj_temp in meshChildren do ( obj_temp.parent=undefined converttomesh obj_temp resetXform obj_temp converttomesh obj_temp local save_mat=obj_temp.material if classof save_mat == Multimaterial then ( ID_true_used=#{{}} T_Faces_matID=#() T_Faces_matID.count=save_mat.count for f=1 to obj_temp.numfaces do ( local face_matID=(getFaceMatID obj_temp f) if not(ID_true_used[face_matID]) then ( T_Faces_matID[face_matID]=#{{f}} ID_true_used[face_matID]=true ) else ( append (T_Faces_matID[face_matID]) f ) ) for index_mat in ID_true_used do ( mat_current=save_mat[index_mat] matID=finditem multimat_new mat_current if matID==0 then ( append multimat_new mat_current new_Id=multimat_new.count for f in (T_Faces_matID[index_mat]) do setFaceMatID obj_temp f new_Id ) else for f in (T_Faces_matID[index_mat]) do setFaceMatID obj_temp f matID ) ) else ( mat_current = save_mat matID = finditem multimat_new mat_current if matID == 0 then ( append multimat_new mat_current new_Id = multimat_new.count for f = 1 to obj_temp.numfaces do setFaceMatID obj_temp f new_Id ) else for f = 1 to obj_temp.numfaces do setFaceMatID obj_temp f matID ) obj_temp.material = undefined with undo off(polyop.attach obj obj_temp) ) final_mat = multimaterial numsubs: (multimat_new.count) final_mat.name = obj.name + ""_mat"" for i = 1 to(multimat_new.count) do ( if multimat_new[i] == undefined then(final_mat[i] = FlightSim() ; final_mat[i].name = ""Newmat"" ) else final_mat[i] = multimat_new[i] ) polyop.deleteFaces obj 1 obj.material = final_mat obj.parent = groupNode.parent"); Loader.Core.DeleteNode(node, false, false); IINode newNode = Loader.Core.GetINodeByName(groupName); foreach (IINode n in nonMeshChildren) { newNode.AttachChild(n, true); } return(newNode); }
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); }
private async Task <bool> DoExport(ExportItem exportItem, bool multiExport = false, bool clearLogs = true) { new BabylonAnimationActionItem().Close(); SaveOptions(); if (multiExport) { ShowExportItemLayers(exportItem.Layers); } exporter = new BabylonExporter(); if (clearLogs) { treeView.Nodes.Clear(); } exporter.OnExportProgressChanged += progress => { progressBar.Value = progress; Application.DoEvents(); }; exporter.OnWarning += (warning, rank) => CreateWarningMessage(warning, rank); exporter.OnError += (error, rank) => CreateErrorMessage(error, rank); exporter.OnMessage += (message, color, rank, emphasis) => CreateMessage(message, color, rank, emphasis); exporter.OnVerbose += (message, color, rank, emphasis) => CreateMessage(message, color, rank, emphasis); exporter.OnPrint += (message, color, rank, emphasis) => CreateMessage(message, color, rank, emphasis); butExport.Enabled = false; butExportAndRun.Enabled = false; butMultiExport.Enabled = false; butCancel.Enabled = true; bool success = true; try { string modelAbsolutePath = multiExport ? exportItem.ExportFilePathAbsolute : txtModelPath.Text; string textureExportPath = multiExport ? exportItem.ExportTexturesesFolderAbsolute : txtTexturesPath.Text; var scaleFactorParsed = 1.0f; var textureQualityParsed = 100L; try { scaleFactorParsed = float.Parse(txtScaleFactor.Text); } catch (Exception e) { throw new InvalidDataException(String.Format("Invalid Scale Factor value: {0}", txtScaleFactor.Text)); } try { textureQualityParsed = long.Parse(txtQuality.Text); } catch (Exception e) { throw new InvalidDataException(String.Format("Invalid Texture Quality value: {0}", txtScaleFactor.Text)); } MaxExportParameters exportParameters = new MaxExportParameters { outputPath = modelAbsolutePath, textureFolder = textureExportPath, outputFormat = comboOutputFormat.SelectedItem.ToString(), scaleFactor = scaleFactorParsed, writeTextures = chkWriteTextures.Enabled && chkWriteTextures.Checked, overwriteTextures = chkOverwriteTextures.Enabled && chkOverwriteTextures.Checked, exportHiddenObjects = chkHidden.Checked, exportOnlySelected = chkOnlySelected.Checked, generateManifest = chkManifest.Checked, autoSaveSceneFile = chkAutoSave.Checked, exportTangents = chkExportTangents.Checked, exportMorphTangents = chkExportMorphTangents.Checked, exportMorphNormals = chkExportMorphNormals.Checked, txtQuality = textureQualityParsed, mergeAOwithMR = chkMergeAOwithMR.Enabled && chkMergeAOwithMR.Checked, bakeAnimationType = (BakeAnimationType)cmbBakeAnimationOptions.SelectedIndex, logLevel = (LogLevel)logLevelcmb.SelectedIndex, dracoCompression = chkDracoCompression.Enabled && chkDracoCompression.Checked, enableKHRLightsPunctual = chkKHRLightsPunctual.Checked, enableKHRTextureTransform = chkKHRTextureTransform.Checked, enableKHRMaterialsUnlit = chkKHRMaterialsUnlit.Checked, exportMaterials = chkExportMaterials.Enabled && chkExportMaterials.Checked, animationExportType = (AnimationExportType)cmbExportAnimationType.SelectedIndex, enableASBAnimationRetargeting = chkASBAnimationRetargeting.Checked, optimizeAnimations = !chkDoNotOptimizeAnimations.Checked, animgroupExportNonAnimated = chkAnimgroupExportNonAnimated.Checked, exportNode = exportItem?.Node, exportLayers = exportItem?.Layers, pbrNoLight = chkNoAutoLight.Checked, pbrFull = chkFullPBR.Checked, pbrEnvironment = txtEnvironmentName.Text, usePreExportProcess = chkUsePreExportProces.Checked, flattenScene = chkFlatten.Enabled && chkFlatten.Checked, mergeContainersAndXRef = chkMrgContainersAndXref.Checked, useMultiExporter = multiExport, tangentSpaceConvention = (TangentSpaceConvention)cmbNormalMapConvention.SelectedIndex, removeLodPrefix = chk_RemoveLodPrefix.Checked, removeNamespaces = chkRemoveNamespace.Checked, srcTextureExtension = txtSrcTextureExt.Text, dstTextureExtension = txtDstTextureExt.Text }; exporter.callerForm = this; exporter.Export(exportParameters); } catch (OperationCanceledException) { progressBar.Value = 0; success = false; ScriptsUtilities.ExecuteMaxScriptCommand(@"global BabylonExporterStatus = ""Available"""); } catch (Exception ex) { IUTF8Str operationStatus = GlobalInterface.Instance.UTF8Str.Create("BabylonExportAborted"); Loader.Global.BroadcastNotification(SystemNotificationCode.PreExport, operationStatus); currentNode = CreateTreeNode(0, "Export cancelled: " + ex.Message, Color.Red); currentNode = CreateTreeNode(1, ex.ToString(), Color.Red); currentNode.EnsureVisible(); progressBar.Value = 0; success = false; ScriptsUtilities.ExecuteMaxScriptCommand(@"global BabylonExporterStatus = Available"); } butCancel.Enabled = false; butExport.Enabled = true; butMultiExport.Enabled = true; butExportAndRun.Enabled = WebServer.IsSupported; BringToFront(); return(success); }
private static void ResolveMultipleInheritedContainer(IIContainerObject container) { //resolve container name int b = 0; if (container.ContainerNode.GetUserPropBool("flightsim_resolved", ref b) && b != 0) { return; } List <IINode> containerHierarchy = new List <IINode>() { }; containerHierarchy.AddRange(container.ContainerNode.ContainerNodeTree(false)); string helperPropBuffer = string.Empty; container.BabylonContainerHelper().GetUserPropBuffer(ref helperPropBuffer); int containerID = 1; container.ContainerNode.GetUserPropInt("babylonjs_ContainerID", ref containerID); container.ContainerNode.Name = containerID == -1 ? $"{container.ContainerNode.Name}" : $"{container.ContainerNode.Name}_{containerID}"; // resolve nodes , adding an id at the end { //manage multiple containers inherited from the same source foreach (IINode n in containerHierarchy) { if (n.IsBabylonContainerHelper()) { continue; } //change the guid of the node //replace the guid in the babylon helper string oldGuid = n.GetStringProperty("babylonjs_GUID", Guid.NewGuid().ToString()); n.DeleteProperty("babylonjs_GUID"); Guid newGuid = n.GetGuid(); helperPropBuffer = helperPropBuffer.Replace(oldGuid, newGuid.ToString()); n.Name = containerID == -1 ? $"{n.Name}" : $"{n.Name}_{containerID}"; if (n.Mtl != null && FlightSimMaterialUtilities.HasFlightSimMaterials(n.Mtl) && FlightSimMaterialUtilities.HasRuntimeAccess(n.Mtl)) { if (n.Mtl.IsMultiMtl) { throw new Exception($@"Material {n.Mtl.Name} has a property ""Unique In Container"" enabled, cannot be child of a multi material"); } else { string cmd = $"mNode = maxOps.getNodeByHandle {n.Handle} \r\n" + $"newMat = copy mNode.material \r\n" + $"newMat.name = \"{n.Mtl.Name}_{containerID}\" \r\n" + $"mNode.material = newMat"; ScriptsUtilities.ExecuteMaxScriptCommand(cmd); } } } } //replace animationList guid to have distinct list of AnimationGroup for each container string animationListStr = string.Empty; IINode containerHelper = container.BabylonContainerHelper(); containerHelper.GetUserPropString(s_AnimationListPropertyName, ref animationListStr); if (!string.IsNullOrEmpty(animationListStr)) { string[] animationGroupGuid = animationListStr.Split(AnimationGroup.s_PropertySeparator); foreach (string guidStr in animationGroupGuid) { Guid newAnimGroupGuid = Guid.NewGuid(); helperPropBuffer = helperPropBuffer.Replace(guidStr, newAnimGroupGuid.ToString()); } containerHelper.SetUserPropBuffer(helperPropBuffer); //add ID of container to animationGroup name to identify animation in viewer containerHelper.GetUserPropString(s_AnimationListPropertyName, ref animationListStr); string[] newAnimationGroupGuid = animationListStr.Split(AnimationGroup.s_PropertySeparator); foreach (string guidStr in newAnimationGroupGuid) { string propertiesString = string.Empty; if (!containerHelper.GetUserPropString(guidStr, ref propertiesString)) { return; } try // new serialization method { SerializableAnimationGroup serialAnimGroup = new SerializableAnimationGroup(propertiesString); serialAnimGroup.name = containerID == -1 ? serialAnimGroup.name : serialAnimGroup.name + $"_{containerID}"; string serializedInfo = JsonConvert.SerializeObject(serialAnimGroup); container.BabylonContainerHelper().SetUserPropString(guidStr, serializedInfo); } catch (Exception) { string[] properties = propertiesString.Split(AnimationGroup.s_PropertySeparator); if (properties.Length < 4) { throw new Exception($"Invalid number of properties, can't de-serialize property of {containerHelper.Name} of {container.ContainerNode.Name}."); } string name = properties[0]; if (!string.IsNullOrEmpty(name)) { propertiesString = propertiesString.Replace(name, name + "_" + containerID); container.BabylonContainerHelper().SetUserPropString(guidStr, propertiesString); } } } } container.ContainerNode.SetUserPropBool("flightsim_resolved", true); }