try( ClearAllProgressWindows() ) catch() try( ProceduralPainter.ToolWindow.Close() ) catch() FileIn ( RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms" ) --FileIn the types for the procedural painter and editor FileIn "ProceduralBase.ms" FileIn "ProceduralPainter_MapImage.ms" FileIn "ProcPainter_Live.ms" struct ExportedLocTri ( Center, Area, VertList = #(), GroundTintList = #(), GroundTint, ScaleXYZLevel, ScaleZLevel, DensityLevel, ProceduralTagIndex ) struct ProceduralPainter ( Include @"X:\gta5\tools_ng\wildwest\script\3dsMax\General_tools\Collision\ProceduralPainter_LiveEditing.ms" --Contains the core logic of the painter tool Painter, --The tool window ToolWindow, --The working object we are operating on and the working layer WorkingObject, WorkingObjectFaceCount, WorkingLayer, --The source objects that we are working on SourceObjectList = #(), --Our blank procedural level BlankProceduralLevel, --Channel we store the our procedural data. DataChannel = 31, --Map images we source data from GroundTintMapImage = MapImage ImageFilePath:( RsMakeBackSlashes( gRsConfig.Project.DefaultBranch.Assets + "/maps/procPaint/ProceduralTintMap.png" ) ), ProceduralMapImage = MapImage ImageFilePath:( RsMakeBackSlashes( RsConfigGetAssetsDir() + "/maps/procPaint/ProceduralSetMap.png" ) ), --A list of visual meshes that dont result in a proc_mesh in this session CreatesEmptyProcMesh = #(), /*------------------------------------------------------------------------------ Starts an edit session setting up the active object that we are working on. */------------------------------------------------------------------------------ fn StartEditing = ( --Check our selection is valid. if not( this.CheckNodesAreValid Selection ) then ( return False ) with undo off ( SuspendEditing() --Set our source object selection this.SourceObjectList = Selection as Array --Collect any data that already exists on the source objects this.CollectData() --Setup our working object this.CreateWorkingObject() --Push an update to all our materials this.UpdateAllRenderSettings() this.UpdateAllDisplaySettings() ResumeEditing() ) --Select our working object and engage a lock on it Select this.WorkingObject --Transform lock the object SetTransformLockFlags this.WorkingObject #All --Get count of faces this.WorkingObjectFaceCount = PolyOp_GetNumFaces this.WorkingObject --Engage a selection/editing lock on our working object this.EngageLock() return True ), /*------------------------------------------------------------------------------ Concludes this editing session */------------------------------------------------------------------------------ fn StopEditing = ( --Check we are currently editing first. If we aren't then then we don't need to stop anything. --This occurs if the tool is opened and then closed again straight after. if( this.Painter.CurrentlyEditing ) then ( --Check if we have unsaved data before stopping. if( this.Painter.NodeCollection.UnsavedDataChanges ) then ( if not( QueryBox "There is some unsaved data on the current object. If we continue all unsaved changes will be lost. Would you like to continue?" ) then ( return False ) ) --Mark that we are no longer currently editing this.Painter.CurrentlyEditing = false --Atempt to clean up the working object this.DeleteWorkingObject() ) --Unhide all decal/cutout/alpha faces on the source objects for SourceObject in this.SourceObjectList do ( if( IsValidNode SourceObject ) then ( PolyOp.UnHideAllFaces SourceObject ) ) --Dispose map images this.GroundTintMapImage.Dispose() this.ProceduralMapImage.Dispose() --Clear the collection this.Painter.NodeCollection.Clear() --Clear the applied count this.Painter.ResetAppliedCounts() --Stop sync the camera this.Painter.CameraSyncState = off --Disengage any locks we may have this.DisengageLock() ), /*------------------------------------------------------------------------------ Callback functions and variables. */------------------------------------------------------------------------------ LockCallback, fn LockCallbackFn = ( --Set to modifier panel SetCommandPanelTaskMode #Modify --Check the node is still valid before forcing it if not ( IsValidNode this.WorkingObject ) then ( --If it isn't then it's likely been deleted or the user has opened a new scene. --Mark that we are no longer editing and call StopEditing. MessageBox "Editing will be stopped and changes will be lost. This is because the tool has detected that the working object is no longer in a valid and editable state. It is likely becuase it has been deleted and/or a fresh scene has been opened." this.Painter.CurrentlyEditing = false this.StopEditing() this.DeleteWorkingObject() return() ) --Check that modifiers haven't been added if( this.WorkingObject.Modifiers.Count > 0 ) then ( DeleteModifier this.WorkingObject this.WorkingObject.Modifiers[1] ) --Check if the user has converted the object into something strange if( ClassOf this.WorkingObject != Editable_Poly ) then ( Max Undo ) --Check that geometry edits haven't been made CurrentWorkingObjectFaceCount = PolyOp_GetNumFaces this.WorkingObject if( CurrentWorkingObjectFaceCount != this.WorkingObjectFaceCount ) then ( Max Undo ) --Check if our working object is the current and only selection. If it isn't then force it. if not( Selection.Count == 1 and Selection[1] == this.WorkingObject ) then ( Select this.WorkingObject ) --Put us in select mode if anything invalid is set if not ( ToolMode.CommandMode == #SELECT or ToolMode.CommandMode == #VIEWPORT ) then ( ToolMode.CommandMode = #SELECT ) --Make sure we are in the correct sub selection mode if( GetSelectionLevel this.WorkingObject != #Face ) then ( SetSelectionLevel this.WorkingObject #Face ) ), /*------------------------------------------------------------------------------ Locks selection to the working object using callbacks. */------------------------------------------------------------------------------ fn EngageLock = ( RegisterRedrawViewsCallback this.LockCallbackFn), /*------------------------------------------------------------------------------ Unlocks the selection. Used when we have finished editing. Or something unexpected has happened which is blocking the tool from working correctly. */------------------------------------------------------------------------------ fn DisengageLock = ( UnregisterRedrawViewsCallback this.LockCallbackFn ), /*------------------------------------------------------------------------------ Checks the passed nodes to ensures they are each valid for edit. */------------------------------------------------------------------------------ fn CheckNodesAreValid TargetNodeArray = ( if ( Selection.Count > 50 ) then ( MessageBox "You have more than 50 objects selected. Please reduce this number. Try working in batches." return False ) if ( Selection.Count < 1 ) then ( MessageBox "Please select some objects and try again." return False ) for SubNode in TargetNodeArray do ( if not ( IsValidNode SubNode and SubNode.modifiers.Count == 0 and ( ClassOf SubNode == Editable_Poly or ClassOf SubNode == PolyMeshObject ) ) then ( MessageBox "One or more of the selected objects are not valid. Check that all objects are of type 'Editable_Poly' and collapsed. Also ensure none of the objects are LOD objects. " return False ) ) return True ), /*------------------------------------------------------------------------------ Creates our working object for the target object selection */------------------------------------------------------------------------------ fn CreateWorkingObject = ( RSPushPrompt "Creating working object..." --Create holding poly this.WorkingObject = Editable_Mesh Name:"__ProceduralPainter__WorkingObject__" ConvertToPoly( this.WorkingObject ) --Clone our input objects ClonedNodes = #() MaxOps.CloneNodes this.SourceObjectList newNodes:&ClonedNodes --Clear materials so we can attached together safely ClonedNodes.Material = Undefined --For each clone for ClonedNode in ClonedNodes do ( --Attach into holding poly PolyOp.Attach this.WorkingObject ClonedNode ) --Assign our master multimat this.WorkingObject.Material = this.WorkingObjectMultiMat --Set the pivot to the centre of the object and reset this.WorkingObject.Pivot = this.WorkingObject.Center ResetXForm this.WorkingObject --Use push PushMod = Push Push_Value:0.05 AddModifier this.WorkingObject PushMod --Add UVW map modifier and collapse UVWMapMod = UVWMap Position:[0,0,0] Length:1 Width:10000 UTile:0.01 AddModifier this.WorkingObject UVWMapMod ConvertToPoly this.WorkingObject --Push a full update to all faces - sets up the material IDs correctly this.PushFaceUpdate #All --Set object properties this.WorkingObject.BackFaceCull = true this.WorkingObject.ShowFrozenInGray = false --Clear face selection and setup editing SetCommandPanelTaskMode #Modify PolyOp.SetFaceSelection this.WorkingObject #{} --Create a new layer for our working object - stops the object getting created on a layer that is hidden or frozen this.WorkingLayer = LayerManager.NewLayer() this.WorkingLayer.SetName "__ProceduralPainter__WorkingLayer__" this.WorkingLayer.AddNode this.WorkingObject RSPopPrompt() ), /*------------------------------------------------------------------------------ Attempts to clean up the working object and layer */------------------------------------------------------------------------------ fn DeleteWorkingObject = ( print this.WorkingObject --Delete the working object if( IsValidNode this.WorkingObject ) then ( Delete this.WorkingObject ) --Try and remove the working layer try( LayerManager.DeleteLayerByName this.WorkingLayer.Name ) catch () ), /*------------------------------------------------------------------------------ Collects information about what ProceduralLevels are applied to the various faces of the target nodes. */------------------------------------------------------------------------------ fn CollectData = ( --Create our top progress window SourceObjectProgress = RSProgressWindow Title:"Collecting previous data..." StartStep:0 EndStep:SourceObjectList.Count SourceObjectProgress.Start() --Go through each object in the source object list for SourceObject in this.SourceObjectList do ( --Push progress update SourceObjectProgress.PostProgressStep() --Set the object as a procedural source this.SetProceduralSourceProperty SourceObject True --Check we have map support on the handle channel this.SetupPresetChannel SourceObject this.DataChannel --Convert the object to and from editable mesh which fixes a very strange issue where face indexes go out of wack ConvertToMesh SourceObject ConvertToPoly SourceObject --Get face list for the whole object FaceList = RSPoly_GetFaceList_NamedParameter SourceObject #All --Create a new node via our node collection CurrentNode = this.Painter.NodeCollection.CreateNode SourceObject.Handle SourceObject.Name FaceList.Count --Unhide all faces PolyOp.UnHideAllFaces SourceObject --Start a sub progress FaceIndexProgress = SourceObjectProgress.StartSubProgress 0 FaceList.Count SubTitle:SourceObject.Name FaceIndexProgress.Start() --Go through each face HiddenFaceList = #{} for FaceIndex in FaceList do ( --Push progress update FaceIndexProgress.PostProgressStep() --Get source level map value LevelMapValue = RSPoly_GetFace_MapValue SourceObject FaceIndex this.DataChannel --Get the material on the face FaceMaterial = RSGeom_GetFace_Material SourceObject FaceIndex if( ClassOf FaceMaterial == Rage_Shader ) then ( --If the shader is decal/cutout/alpha then mark it too be hidden ShaderName = RstGetShaderName FaceMaterial if( FindString ShaderName "decal" != Undefined or \ FindString ShaderName "alpha" != Undefined or \ FindString ShaderName "cutout" != Undefined ) then ( Append HiddenFaceList FaceIndex LevelMapValue = [0,0,0] RSPoly_SetFace_MapValue SourceObject FaceIndex this.DataChannel LevelMapValue ) ) --Get the procedural level from the components of LevelMapValue. ProceduralLevel = this.Painter.SetData.GetProceduralLevel LevelMapValue[1] LevelMapValue[2] LevelMapValue[3] --Create face data on the node with this information CurrentNode.CreateFaceData FaceIndex ProceduralLevel ) --Hide decal/cutout/alpha faces PolyOp.SetHiddenFaces SourceObject HiddenFaceList --End progress FaceIndexProgress.End() ) --Update the applied set this.Painter.UpdateAppliedSet() --Update applied counts this.Painter.UpdateAppliedCounts() --Set the applied set as the currently selected set this.Painter.ResetSelectedSet() --End progress SourceObjectProgress.End() ), /*------------------------------------------------------------------------------ Checks and sets up preset channel. */------------------------------------------------------------------------------ fn SetupPresetChannel InputNode InputMapChannel = ( --Check for map support if not ( PolyOp.GetMapSupport InputNode InputMapChannel ) then ( --If it doesn't have any then set it up. Passing in black as the base map value - which is blank RSPoly_ResetMapChannel InputNode InputMapChannel [0,0,0] ) ), --Our master MultiMat WorkingObjectMultiMat = MultiMaterial Name:"ProceduralPainter" NumSubs:1, /*------------------------------------------------------------------------------ Gets the relevant material ID for the passed procedural level */------------------------------------------------------------------------------ fn GetProceduralLevelMaterialID InputProceduralLevel = ( --Get the target material TargetProceduralLevelMaterial = this.ResolveProceduralLevelMaterial InputProceduralLevel --Return the ID return( MultiMat_GetMaterialID WorkingObjectMultiMat TargetProceduralLevelMaterial) ), /*------------------------------------------------------------------------------ Takes a procedural level and returns the associated material. Sets up a new material if need be. */------------------------------------------------------------------------------ fn ResolveProceduralLevelMaterial InputProceduralLevel = ( ProceduralLevelString = InputProceduralLevel.ToString() --Check if we already have an associated procedural level material ReturnProceduralLevelMaterial = MultiMat_GetMaterialByName WorkingObjectMultiMat ProceduralLevelString --Otherwise create a new procedural level material if( ReturnProceduralLevelMaterial == Undefined ) then ( --Create a new standard material with our checkered opacity map ReturnProceduralLevelMaterial = Standard Name:ProceduralLevelString ShowInViewport:True ReturnProceduralLevelMaterial.SelfIllumAmount = 30.0 ReturnProceduralLevelMaterial.OpacityMap = Checker Name:( ProceduralLevelString + " - OPACITY") --Add the material to the next available slot MultiMat_AddMaterial WorkingObjectMultiMat ReturnProceduralLevelMaterial ) --Return the material return ReturnProceduralLevelMaterial ), DisplayMode = DotNetClass "ProceduralSystem.DisplayMode", /*------------------------------------------------------------------------------ Updates the respective material of the passed procedural level so that it is in sync with our current display mode */------------------------------------------------------------------------------ fn UpdateDisplaySettings InputProceduralLevel = ( --Get the target material TargetProceduralLevelMaterial = this.ResolveProceduralLevelMaterial InputProceduralLevel --Combined result if( this.Painter.Display.CurrentMode == DisplayMode.CombinedResult ) then ( TargetProceduralLevelMaterial.Diffuse = Color InputProceduralLevel.Color.R InputProceduralLevel.Color.G InputProceduralLevel.Color.B return() ) --IsolateDensity if( this.Painter.Display.CurrentMode == DisplayMode.IsolateDensity ) then ( ColorValue = InputProceduralLevel.Density * 255 TargetProceduralLevelMaterial.Diffuse = Color ColorValue ColorValue ColorValue return() ) --IsolateGroundTint if( this.Painter.Display.CurrentMode == DisplayMode.IsolateGroundTint ) then ( TargetProceduralLevelMaterial.Diffuse = Color InputProceduralLevel.GroundTint.R InputProceduralLevel.GroundTint.G InputProceduralLevel.GroundTint.B return() ) --IsolateScaleZ if( this.Painter.Display.CurrentMode == DisplayMode.IsolateScaleZ ) then ( ColorValue = InputProceduralLevel.ScaleZ * 255 TargetProceduralLevelMaterial.Diffuse = Color ColorValue ColorValue ColorValue return() ) --IsolateScaleXYZ if( this.Painter.Display.CurrentMode == DisplayMode.IsolateScaleXYZ ) then ( ColorValue = InputProceduralLevel.ScaleXYZ * 255 TargetProceduralLevelMaterial.Diffuse = Color ColorValue ColorValue ColorValue return() ) ), fn UpdateAllDisplaySettings = ( for Procedural in this.Painter.AppliedSet.ProceduralArray do ( for ProceduralLevel in Procedural.LevelArray do ( this.UpdateDisplaySettings ProceduralLevel ) ) ), RenderMode = DotNetClass "ProceduralSystem.RenderMode", /*------------------------------------------------------------------------------ Updates the respective material of the passed procedural level so that it is in sync with our current render mode */------------------------------------------------------------------------------ fn UpdateRenderSettings InputProceduralLevel = ( --Get the target material TargetProceduralLevelMaterial = this.ResolveProceduralLevelMaterial InputProceduralLevel --Transparent if( this.Painter.Render.CurrentMode == RenderMode.Transparent ) then ( ProceduralLevelOpacityValue = InputProceduralLevel.ParentProcedural.DisplayOpacity * 1.25 TargetProceduralLevelMaterial.OpacityMap.Color1 = Color ProceduralLevelOpacityValue ProceduralLevelOpacityValue ProceduralLevelOpacityValue ProceduralLevelOpacityValue = InputProceduralLevel.ParentProcedural.DisplayOpacity * 2.55 TargetProceduralLevelMaterial.OpacityMap.Color2 = Color ProceduralLevelOpacityValue ProceduralLevelOpacityValue ProceduralLevelOpacityValue return() ) --Opaque if( this.Painter.Render.CurrentMode == RenderMode.Opaque ) then ( TargetProceduralLevelMaterial.OpacityMap.Color1 = Color 255 255 255 TargetProceduralLevelMaterial.OpacityMap.Color2 = Color 255 255 255 return() ) ), fn UpdateAllRenderSettings = ( for Procedural in this.Painter.AppliedSet.ProceduralArray do ( for ProceduralLevel in Procedural.LevelArray do ( this.UpdateRenderSettings ProceduralLevel ) ) ), /*------------------------------------------------------------------------------ Updates the material IDs of the passed faces so they are upto date with the associated procedural level */------------------------------------------------------------------------------ fn PushFaceUpdate InputFaceList = ( --Check if we have been passed a named parameter #All #Selection #None if( ClassOf InputFaceList == Name ) then ( --Convert to proper bitarray InputFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject InputFaceList ) --Convert to array InputFaceList = InputFaceList as Array --Get the procedural levels used across the given faces ProceduralLevelList = this.Painter.NodeCollection.GetProceduralLevelList InputFaceList --For each of the procedural level applied for TargetProceduralLevel in ProceduralLevelList do ( --Resolve the material ID that relates to our procedural level TargetProceduralLevelMaterialID = this.GetProceduralLevelMaterialID TargetProceduralLevel --Get the face list the procedural is applied to - restrict by input face list TargetFaceList = this.Painter.NodeCollection.GetFaceListByProceduralLevel TargetProceduralLevel InputFaceList --Convert to bit array and set the material ID TargetFaceList = TargetFaceList as BitArray PolyOp_SetFaceMatID this.WorkingObject TargetFaceList TargetProceduralLevelMaterialID ) ), /*------------------------------------------------------------------------------ Applies the passed procedural level to all the faces in the passed face list */------------------------------------------------------------------------------ fn ApplyProceduralLevel InputFaceList InputProceduralLevel = ( --Check if we have been passed a named parameter #All #Selection #None if( ClassOf InputFaceList == Name ) then ( --If we are working with a selection then check that the selection is not to big if( InputFaceList == #Selection ) then ( InputFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject InputFaceList if( ( InputFaceList as Array ).Count > 1000 ) then ( if not( QueryBox "This operation is going to affect more than 1000 faces. The operation will be permanent. Would you like to continue?" ) then ( return() ) ) ) else ( InputFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject InputFaceList ) ) --Mutilate from bitarray to an array so it can be passed to DotNet InputFaceList = InputFaceList as Array --Apply the procedural this.Painter.NodeCollection.SetProceduralLevel InputFaceList InputProceduralLevel --Mark that we now have unsaved changes this.Painter.NodeCollection.UnsavedDataChanges = true if( InputProceduralLevel != this.BlankProceduralLevel ) then ( --Update the display and render settings for the material this.UpdateRenderSettings InputProceduralLevel this.UpdateDisplaySettings InputProceduralLevel ) --Push this update through to the face mat ID this.PushFaceUpdate InputFaceList ), /*------------------------------------------------------------------------------ Sets a time and data stamp on the passed object */------------------------------------------------------------------------------ ProceduralTimeStampKey = "ProceduralTimeStamp", ProceduralDataStampKey = "ProceduralDataStamp", fn SetProceduralStamp InputNode = ( UTCNow = ( DotNetClass "System.DateTime" ).UTCNow SetUserProp InputNode this.ProceduralTimeStampKey ( UTCNow.ToFileTime() ) SetUserProp InputNode this.ProceduralDataStampKey ( this.Painter.SetData.Version.ToString() ) ), /*------------------------------------------------------------------------------ Returns true if the passed object has a data version stamp that matches our current SetData */------------------------------------------------------------------------------ fn HasProceduralStamp InputNode = ( ProceduralTimeStampValue = GetUserProp InputNode this.ProceduralTimeStampKey ProceduralDataStampValue = GetUserProp InputNode this.ProceduralDataStampKey if( ProceduralTimeStampValue != Undefined and ProceduralDataStampValue != Undefined ) then ( return True ) else ( return False ) ), /*------------------------------------------------------------------------------ Returns true if the passed object has a data version stamp that matches our current SetData */------------------------------------------------------------------------------ fn CheckDataStamp InputNode = ( CurrentDataStamp = GetUserProp InputNode this.ProceduralDataStampKey if( CurrentDataStamp == this.Painter.SetData.Version.ToString() ) then ( return True ) else ( return False ) ), /*------------------------------------------------------------------------------ Commits/writes the stored data from the NodeCollection out to the relevant nodes */------------------------------------------------------------------------------ fn CommitData = ( --Create our top progress window NodeDataProgress = RSProgressWindow Title:"Saving procedural data..." StartStep:0 EndStep:this.Painter.NodeCollection.NodeDataArray.Count NodeDataProgress.Start() --Go through each of our node data objects for NodeData in this.Painter.NodeCollection.NodeDataArray do ( --Push progress update NodeDataProgress.PostProgressStep() --Get the target node by handle TargetNode = MaxOps.GetNodeByHandle NodeData.Handle --Start a sub progress NodeFaceDataProgress = NodeDataProgress.StartSubProgress 0 NodeData.NodeFaceDataArray.Count SubTitle:TargetNode.Name NodeFaceDataProgress.Start() --Reset channel to black ready to apply procedural level map values RSPoly_ResetMapChannel TargetNode this.DataChannel [0,0,0] --Go through each NodeFaceData for NodeFaceData in NodeData.NodeFaceDataArray do ( --Push progress update NodeFaceDataProgress.PostProgressStep() --Get our applied procedural level ProceduralLevel = NodeFaceData.ProceduralLevel if( ProceduralLevel != this.BlankProceduralLevel ) then ( --Setup our map values from the procedural level LevelMapValue = [ProceduralLevel.Color.ScR, ProceduralLevel.Color.ScG, ProceduralLevel.Color.ScB] --Set the target faces to the map value RSPoly_SetFace_MapValue TargetNode NodeFaceData.FaceIndex this.DataChannel LevelMapValue ) --Reset value changed NodeFaceData.DataAltered = False ) --Set the the procedural stamp for this change this.SetProceduralStamp TargetNode --End progress NodeFaceDataProgress.End() ) --End progress NodeDataProgress.End() --Mark that we now have saved all changes this.Painter.NodeCollection.UnsavedDataChanges = False --Automatically transfer to collision meshes if needed if( this.Painter.AutoTransferAfterCommit ) then ( this.TransferListUpdate() --this.TransferDataToProcMesh() ) ), /*------------------------------------------------------------------------------ Selects the faces of the passed procedural level */------------------------------------------------------------------------------ fn SelectProceduralLevel InputProceduralLevel = ( --Get an array of all the faces SourceFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject #All SourceFaceList = SourceFaceList as Array --Get faces used by the input procedural level TargetFaceList = this.Painter.NodeCollection.GetFaceListByProceduralLevel InputProceduralLevel SourceFaceList --Mutilate to bit array TargetFaceList = TargetFaceList as BitArray --Select polys PolyOp.SetFaceSelection this.WorkingObject TargetFaceList --Push a scene redraw so the faces appear selected RedrawViews() ), /*------------------------------------------------------------------------------ Attempts to auto step/blend the levels. Works on selected faces */------------------------------------------------------------------------------ fn AutoBlendProceduralLevels = ( --Get an array of selected the faces SourceFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject #Selection SourceFaceList = SourceFaceList as Array --Get the procedurals used on the passed faces ProceduralList = this.Painter.NodeCollection.GetProceduralList SourceFaceList --For each procedural for Procedural in ProceduralList do ( --Start with all faces (from all levels) ProceduralFaceList = this.Painter.NodeCollection.GetFaceListByProcedural Procedural SourceFaceList ProceduralFaceList = ProceduralFaceList as BitArray --Set the base level as our current level CurrentLevel = Procedural.BaseLevel --While we haven't reached the top level while CurrentLevel != Undefined do ( --Get the procedural level faces by reducing from the original face list ProceduralLevelFaceList = RSPoly_GetFaceList_DistanceReduced this.WorkingObject ProceduralFaceList CurrentLevel.BlendDistance --Set faces to current level this.ApplyProceduralLevel ProceduralLevelFaceList CurrentLevel --Move onto next level CurrentLevel = CurrentLevel.NextLevel ) ) ), /*------------------------------------------------------------------------------ Fragments the block of faces based on the spacing values stored in the procedural */------------------------------------------------------------------------------ fn ProcessFaceSpacing = ( --Get an array of selected the faces SourceFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject #Selection SourceFaceList = SourceFaceList as Array --Get the procedurals used on the passed faces ProceduralList = this.Painter.NodeCollection.GetProceduralList SourceFaceList --For each procedural for Procedural in ProceduralList do ( --Start with all faces (from all levels) ProceduralFaceList = this.Painter.NodeCollection.GetFaceListByProcedural Procedural SourceFaceList ProceduralFaceList = ProceduralFaceList as BitArray --Then get a reduced selection based on distance ProcessedProceduralFaceList = RSPoly_GetFaceList_DistanceDistributed this.WorkingObject ProceduralFaceList Procedural.Spacing --Get the inversion FinalProceduralFaceList = ProceduralFaceList - ProcessedProceduralFaceList --First clear the faces this.ApplyProceduralLevel FinalProceduralFaceList this.BlankProceduralLevel ) ), /*------------------------------------------------------------------------------ Selects the faces of the passed procedural */------------------------------------------------------------------------------ fn SelectProcedural InputProcedural = ( --Get an array of all the faces SourceFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject #All SourceFaceList = SourceFaceList as Array --Get faces used by the input procedural TargetFaceList = this.Painter.NodeCollection.GetFaceListByProcedural InputProcedural SourceFaceList --Mutilate to bit array TargetFaceList = TargetFaceList as BitArray --Select polys PolyOp.SetFaceSelection this.WorkingObject TargetFaceList --Push a scene redraw so the faces appear selected RedrawViews() ), /*------------------------------------------------------------------------------ Removes the passed procedural from the any of the objects faces */------------------------------------------------------------------------------ fn RemoveProcedural InputProcedural = ( --Get an array of all the faces SourceFaceList = RSPoly_GetFaceList_NamedParameter this.WorkingObject #All SourceFaceList = SourceFaceList as Array --Get faces used by the input procedural level TargetFaceList = this.Painter.NodeCollection.GetFaceListByProcedural InputProcedural SourceFaceList --Apply our blank procedural to those faces this.ApplyProceduralLevel TargetFaceList this.BlankProceduralLevel ), /*------------------------------------------------------------------------------ Returns an array of objects that are within range for transfering of data */------------------------------------------------------------------------------ fn GetProximateProceduralSourceList InputCollisionMesh = ( return( for ProceduralSource in this.GetProceduralSourceList() where RSNodesAreProximal InputCollisionMesh ProceduralSource collect ProceduralSource ) ), /*------------------------------------------------------------------------------ Sets a time and data stamp on the passed object */------------------------------------------------------------------------------ fn NeedsUpdating InputVisualMesh = ( if ((finditem CreatesEmptyProcMesh InputVisualMesh) != 0) then ( return false ) --First we check the data stamp of the visualmesh. If it doesn't match then we'll have to do an update. if not( this.CheckDataStamp InputVisualMesh ) then ( --format "% Check dataStamp\n" InputVisualMesh return true ) --does this visual mesh have a corresponding procedural collision mesh local InputCollisionMesh = getNodeByName (InputVisualMesh.name + "_PROC_MESH") if (InputCollisionMesh != undefined) then ( --If the data stamp matched then we check the time stamp on each of the source objects --and see if it is later than the stamp on our collision mesh. Or in other words; has any of the --procedural source objects been changed since we last transfered data. CollisionTimeStamp = GetUserProp InputCollisionMesh this.ProceduralTimeStampKey --Check to make sure we have a time stamp on our collision mesh. If not then we must update. if( CollisionTimeStamp == Undefined ) then ( --format "% Collision time stamp\n" InputCollisionMesh return true ) --Check the collision time stamp against the InputVisualMesh --Get the time stamp local ProceduralSourceTimeStamp = GetUserProp InputVisualMesh this.ProceduralTimeStampKey if( ProceduralSourceTimeStamp == Undefined ) then ( --If it doesn't have one set then add one now this.SetProceduralStamp InputVisualMesh ProceduralSourceTimeStamp = GetUserProp InputVisualMesh this.ProceduralTimeStampKey ) --Check it if( ProceduralSourceTimeStamp > CollisionTimeStamp ) then ( --format "% out of date collision: % \n" ProceduralSource InputCollisionMesh return true ) ) else if (InputCollisionMesh == undefined) or (not isValidNode InputCollisionMesh) then ( --format "% no collision mesh\n" InputVisualMesh return true ) --default return false ), /*------------------------------------------------------------------------------ Gets the material for passed collision mesh */------------------------------------------------------------------------------ fn GetCollisionContainerMaterial InputCollisionMesh = ( --Get the container of the collision CollisionContainer = Containers.IsInContainer InputCollisionMesh if( CollisionContainer != Undefined ) then ( CollisionContainerMaterialName = CollisionContainer.Name + "_REX_COLLISION" MaterialMatches = for SceneMat in SceneMaterials where SceneMat.Name == CollisionContainerMaterialName collect SceneMat if( MaterialMatches.Count > 0 ) then ( return MaterialMatches[1] ) else ( return ( MultiMaterial NumSubs:1 Name:CollisionContainerMaterialName ) ) ) else ( return Undefined ) ), /*------------------------------------------------------------------------------ Gets the material applied to the passed face */------------------------------------------------------------------------------ fn GetCollisionFaceMaterial InputCollisionMesh InputFaceIndex = ( CollisionFaceMaterial = RSGeom_GetFace_Material InputCollisionMesh InputFaceIndex if( ClassOf CollisionFaceMaterial == RexBoundMtl ) then ( return CollisionFaceMaterial ) else ( return Undefined ) ), /*------------------------------------------------------------------------------ Updates the collision transfer list */------------------------------------------------------------------------------ fn TransferListUpdate = ( local VisualTargetList = this.GetProceduralSourceList() gRsUlog.validate() --CollisionTargetList = this.GetCollisionTargetList() this.Painter.TransferListClear() for VisualTarget in VisualTargetList do ( VisualTargetNeedsUpdating = this.NeedsUpdating VisualTarget this.Painter.TransferListAddCollision VisualTarget.Handle VisualTarget.Name VisualTargetNeedsUpdating ) ), /*------------------------------------------------------------------------------ Adds collision meshes from the passed selection. */------------------------------------------------------------------------------ fn TransferListAddCollision InputObjArray = ( for Obj in InputObjArray do ( this.SetCollisionTargetProperty Obj True ) this.TransferListUpdate() ), /*------------------------------------------------------------------------------ Removes collision meshes from the passed selection */------------------------------------------------------------------------------ fn TransferListRemoveCollision InputObjArray = ( for Obj in InputObjArray do ( this.Painter.TransferListRemoveCollision Obj.Handle this.SetCollisionTargetProperty Obj False ) this.TransferListUpdate() ), /*------------------------------------------------------------------------------ Removes collision meshes from the passed selection */------------------------------------------------------------------------------ fn TransferListSelectAllCollision = ( this.TransferListUpdate() --CollisionTargetList = this.GetCollisionTargetList() CollisionTargetList = this.GetProceduralSourceList() ClearSelection() Select CollisionTargetList ), /*------------------------------------------------------------------------------ Clear list selection */------------------------------------------------------------------------------ fn TransferListSelectNoneCollision = ( this.TransferListUpdate() for i = 0 to (this.Painter.TransferList.Count - 1) do ( this.Painter.TransferList.Item[i].SelectedForTransfer = false ) ClearSelection() ), /*------------------------------------------------------------------------------ Gets a list of all collisions that are marked as targets for transfer */------------------------------------------------------------------------------ fn GetCollisionTargetList = ( return ( for HelperObj in Helpers where this.IsCollisionTarget HelperObj collect HelperObj ) ), /*------------------------------------------------------------------------------ Returns true if the passed object is a collision target */------------------------------------------------------------------------------ CollisionTargetKey = "ProceduralTransfer", fn IsCollisionTarget InputObject = ( --Check the object is of the correct type (rules out things like RSRef objects) if not( ClassOf InputObject == Col_Mesh ) then ( return False ) --Check that the col_mesh is in a Containers if ( Containers.IsInContainer InputObject == Undefined ) then ( return False ) --Check that the col_mesh is material type --if not ( gRsCollTypes.hasWeaponsFlag InputObject ) then ( return False ) if ( getUserProp InputObject "material" == undefined ) then ( return False ) if not ( getUserProp InputObject "material" ) then ( return False ) else ( return true ) --Check the target collision property exists CollisionTargetPropertyValue = GetUserProp InputObject this.CollisionTargetKey if( CollisionTargetPropertyValue != Undefined and ClassOf CollisionTargetPropertyValue == BooleanClass ) then ( return CollisionTargetPropertyValue ) else ( return False ) ), /*------------------------------------------------------------------------------ Sets the collision target property on the passed object to the passed value */------------------------------------------------------------------------------ fn SetCollisionTargetProperty InputObject InputValue = ( SetUserProp InputObject this.CollisionTargetKey InputValue ), /*------------------------------------------------------------------------------ Updates our collision target spatial hash set. Primarily used to pair changes from the game to collision meshes. */------------------------------------------------------------------------------ CollisionTargetSpatialHashSet, fn UpdateCollisionTargetSpatialHashSet = ( --Get list of our collision targets CollisionTargetList = this.GetCollisionTargetList() --Check if we need to update the hash set if( this.CollisionTargetSpatialHashSet != Undefined ) then ( if( this.CollisionTargetSpatialHashSet.ContentHash != CollisionTargetList as String ) then ( this.CollisionTargetSpatialHashSet.Dispose() ) else ( return Ok ) ) --Create our SpatialHashSet this.CollisionTargetSpatialHashSet = SpatialHashSet() this.CollisionTargetSpatialHashSet.SetCellSearchBreadth 1 --Create our top progress window SpatialHashSetProgress = RSProgressWindow Title:"Gathering target collision data..." StartStep:0 EndStep:CollisionTargetList.Count SpatialHashSetProgress.Start() for CollisionTarget in CollisionTargetList do ( --Push progress update SpatialHashSetProgress.PostProgressStep() --Convert the collision mesh to editable mesh Col2Mesh CollisionTarget FaceCount = GetNumFaces CollisionTarget --Create sub progress FaceProgress = SpatialHashSetProgress.StartSubProgress 0 FaceCount SubTitle:CollisionTarget.Name FaceProgress.Start() --For each face of the ProceduralSource for FaceIndex = 1 to FaceCount do ( --Push progress update FaceProgress.PostProgressStep() --Get the face centre FaceCentre = MeshOp_GetFaceCenter CollisionTarget FaceIndex --Create our entry and add it FaceEntry = DataPair FaceIndex:FaceIndex Node:CollisionTarget this.CollisionTargetSpatialHashSet.AddEntry FaceCentre FaceEntry ) --Back to collision Mesh2Col CollisionTarget --End progress FaceProgress.End() ) --End progress SpatialHashSetProgress.End() --Set content hash so we know not to rebuild this.CollisionTargetSpatialHashSet.ContentHash = CollisionTargetList as String ), /*------------------------------------------------------------------------------ Gets a list of all procedural sources in the scene */------------------------------------------------------------------------------ fn GetProceduralSourceList = ( return( for GeomObj in Geometry where this.IsProceduralSource GeomObj collect GeomObj ) ), /*------------------------------------------------------------------------------ Returns true if the passed object is a procedural source */------------------------------------------------------------------------------ ProceduralSourceKey = "ProceduralSource", fn IsProceduralSource InputObject = ( --Check the object is of the correct type (rules out things like RSRef objects) if not( ClassOf InputObject == Editable_Mesh or ClassOf InputObject == Editable_Poly or ClassOf InputObject == PolyMeshObject ) then ( return false ) --Check the object isn't a LOD because that wouldn't make sense if( RsIsAnyLOD InputObject ) then ( return false ) --Check the procedural source property exists ProceduralSourcePropertyValue = GetUserProp InputObject this.ProceduralSourceKey if( ProceduralSourcePropertyValue == Undefined or ClassOf ProceduralSourcePropertyValue != BooleanClass or ProceduralSourcePropertyValue == False ) then ( return False ) --Check what shader is applied. If its a single fur_grass type then exclude it local InputObjectMaterial = InputObject.material local faceMats = RsGetMatIdsUsedByObj InputObject if (classOf InputObjectMaterial == Multimaterial) then ( local faceMats = RsGetMatIdsUsedByObj InputObject if (faceMats.numberSet == 1) then --singular material assignment ( local matId = (faceMats as Array)[1] -- CHECK TO SEE IF SHADER IS VALID if (InputObjectMaterial[matId] != undefined) then ( local shaderName = RstGetShaderName InputObjectMaterial[matId] if (matchpattern shaderName pattern:"*fur*") then ( return false ) ) else ( messageBox ("MapID [" + matId as string + "] on " + InputObject.name + " isn't a valid shader, please check your mapIDs!") title:"Proc Painter Error!" return false ) ) ) else ( if (classOf InputObjectMaterial) != Rage_Shader then ( return false ) else ( local shaderName = RstGetShaderName InputObjectMaterial if (matchpattern shaderName pattern:"*fur*") then ( return false ) ) ) --Check we have some data in channel 31 (if we don't then we'll give it some). --This really shouldn't happen it's merely a defensive measure. if( ClassOf InputObject == Editable_Mesh ) then ( if not( MeshOp.GetMapSupport InputObject this.DataChannel ) then ( RSMesh_ResetMapChannel InputObject this.DataChannel [0,0,0] ) ) if( ClassOf InputObject == Editable_Poly or ClassOf InputObject == PolyMeshObject ) then ( if not( PolyOp.GetMapSupport InputObject this.DataChannel ) then ( RSPoly_ResetMapChannel InputObject this.DataChannel [0,0,0] ) ) return True ), /*------------------------------------------------------------------------------ Sets the procedural source property on the passed object to the passed value */------------------------------------------------------------------------------ fn SetProceduralSourceProperty InputObject InputValue = ( SetUserProp InputObject this.ProceduralSourceKey InputValue ), /*------------------------------------------------------------------------------ Updates our procedural source spatial hash set. Primarily used to pair collision faces with procedural source faces. */------------------------------------------------------------------------------ ProceduralSourceSpatialHashSet, fn UpdateProceduralSourceSpatialHashSet = ( --Get list of procedural sources ProceduralSourceList = this.GetProceduralSourceList() --Check if we need to update the hash set if( this.ProceduralSourceSpatialHashSet != Undefined ) then ( if( this.ProceduralSourceSpatialHashSet.ContentHash != ProceduralSourceList as String ) then ( this.ProceduralSourceSpatialHashSet.Dispose() ) else ( return Ok ) ) --Create our SpatialHashSet this.ProceduralSourceSpatialHashSet = SpatialHashSet() this.ProceduralSourceSpatialHashSet.SetCellSearchBreadth 1 --Create our top progress window SpatialHashSetProgress = RSProgressWindow Title:"Gathering scene procedural data..." StartStep:0 EndStep:ProceduralSourceList.Count SpatialHashSetProgress.Start() for ProceduralSource in ProceduralSourceList do ( --Push progress update SpatialHashSetProgress.PostProgressStep() FaceCount = GetNumFaces ProceduralSource --Create sub progress FaceProgress = SpatialHashSetProgress.StartSubProgress 0 FaceCount SubTitle:ProceduralSource.Name FaceProgress.Start() --For each face of the ProceduralSource for FaceIndex = 1 to FaceCount do ( --Push progress update FaceProgress.PostProgressStep() --Get the material on the face FaceMaterial = RSGeom_GetFace_Material ProceduralSource FaceIndex if( ClassOf FaceMaterial == Rage_Shader ) then ( --If the shader is decal/cutout/alpha then continue to the next face ShaderName = RstGetShaderName FaceMaterial if( FindString ShaderName "decal" != Undefined or \ FindString ShaderName "alpha" != Undefined or \ FindString ShaderName "cutout" != Undefined ) then ( continue ) ) --Get the face centre FaceCentre if( ClassOf ProceduralSource == Editable_Mesh ) then ( FaceCentre = MeshOp_GetFaceCenter ProceduralSource FaceIndex ) else ( FaceCentre = PolyOp_GetFaceCenter ProceduralSource FaceIndex ) --Create our entry and add it FaceEntry = DataPair FaceIndex:FaceIndex Node:ProceduralSource this.ProceduralSourceSpatialHashSet.AddEntry FaceCentre FaceEntry ) --End progress FaceProgress.End() ) --End progress SpatialHashSetProgress.End() --Set content hash so we know not to rebuild this.ProceduralSourceSpatialHashSet.ContentHash = ProceduralSourceList as String ), --Channel for storing the ground tinting CollisionGroundTintChannel = 11, CollisionGroundTintOverrideChannel = 13, --Channel for storing the scale and density CollisionScaleDensityChannel = 12, CollisionScaleDensityOverrideChannel = 14, /*------------------------------------------------------------------------------ Transfers data from source procedural objects to collision meshes */------------------------------------------------------------------------------ TransferFaceAreaThreshold = 3.0, GroundTintBlankMapValue = [1,1,1], ProcecduralTintAttributeIdx = ( GetAttrIndex "Gta Collision" "Use Procedural Tint" ), /*** Chack and create a layer to hold any generated Procmeshes ***/ fn AddModelToProcMeshLayer model = ( local layerName = "Procedural Material Meshes" local procMeshLayer = LayerManager.getLayerFromName layerName if (procMeshLayer == undefined) then ( procMeshLayer = LayerManager.newLayerFromName layerName ) procMeshLayer.addNode model ), /*------------------------------------------------------------------------------ Transfers data from source procedural objects to procedural meshes */------------------------------------------------------------------------------ fn TransferDataToProcMesh = ( local keepGoing = undefined --Get visual targets that are marked for transfer VisualTargetList = for VisualTarget in this.Painter.TransferArray where VisualTarget.SelectedForTransfer and \ ((MaxOps.GetNodeByHandle VisualTarget.NodeHandle) != Undefined) collect MaxOps.GetNodeByHandle VisualTarget.NodeHandle --Load the ground tint image --if( CollisionTargetList.Count == 0 ) then( return() ) -- if (Selection.count == 0) then -- ( clearSelection() for VisualMesh in VisualTargetList do ( selectmore VisualMesh ) -- ) --Check our selection is valid. if not( this.CheckNodesAreValid Selection ) then ( return False ) --Process the valid selected items for i=1 to selection.count do ( local item = selection[i] -- local lastItem = if (i == selection.count) then true else false -- format "lastItem? %\n" lastItem --check if the procmesh name already exists local dupeName = for o in objects where ((tolower o.name) == (tolower(item.name + "_PROC_MESH"))) collect o if (dupeName.count > 0) then ( --local keepGoing = queryBox "There is already a ProcMesh for this mesh do you want to replace it?" title:"Duplicate ProcMesh" if (keepGoing != #yesAll) then ( keepGoing = RsQueryBoxAll "There is already a ProcMesh for this mesh do you want to replace it?" title:"Duplicate ProcMesh" print keepGoing if (keepGoing == #yes) or (keepGoing == #yesAll) then ( print "#yes" local theObj = getNodeByName (item.name + "_PROC_MESH") delete theObj ) else ( print "NO" continue ) ) else ( local theObj = getNodeByName (item.name + "_PROC_MESH") delete theObj ) ) --Dupe the selected mesh local Cloned = #() maxOps.cloneNodes item newNodes:&Cloned local ProcMesh = Cloned[1] ProcMesh.name = item.name + "_PROC_MESH" --convert to editable mesh ConvertToMesh ProcMesh ConvertToPoly ProcMesh --give it a new multmaterial --ProcMesh.material = Multimaterial() --Check if we should include ground tinting in the transfer if( this.Painter.TransferGroundTint ) then ( --Load the ground tint image if not( this.GroundTintMapImage.Load() ) then ( MessageBox @"Failed to load the image. Check your workspace is mapped and upto date with; //depot/gta5/assets/maps/procPaint/ProceduralTintMap.png" return() ) ) --Update our procedural source spatial hash set this.UpdateProceduralSourceSpatialHashSet() --Create our top progress window --ProcMeshProgress = RSProgressWindow Title:"Transfering data to collision meshes..." StartStep:0 EndStep:1 --ProcMeshProgress.Start() local CollisionMesh = VisualTargetList[1] --Clear map channels --ConvertToPoly CollisionMesh RSPoly_ResetMapChannel ProcMesh this.CollisionGroundTintChannel [1,1,1] RSPoly_ResetMapChannel ProcMesh this.CollisionScaleDensityChannel [1,1,1] --Get the master collision material - the collision material which covers the whole container --CollisionContainerMaterial = this.GetCollisionContainerMaterial CollisionMesh CollisionContainerMaterial = Multimaterial numsubs:1 --Get face count for collision FaceCount = GetNumFaces ProcMesh --Stores all the information about how face matIDs will get remapped to new MultiMat FaceMatIDRemapList = #() --Create sub progress FaceProgress = RSProgressWindow Title:"Transfering data to ProcMesh..." StartStep:0 EndStep:FaceCount FaceProgress.Start() --For each face of the procmesh mesh for FaceIndex = 1 to FaceCount do ( try ( --Push progress update FaceProgress.PostProgressStep() --Get the collision face centre FaceCenter = PolyOp_GetFaceCenter ProcMesh FaceIndex --Resolve locality entry FaceProceduralSourceSpatialEntry = this.ProceduralSourceSpatialHashSet.GetClosestEntry FaceCenter Threshold:0.4 --Target procedural level - start with nothing ProceduralLevel = this.BlankProceduralLevel --Check we actually got something and get the procedural level if( FaceProceduralSourceSpatialEntry != Undefined ) then ( --Get source level map value LevelMapValue if( ClassOf FaceProceduralSourceSpatialEntry.Value.Node == Editable_Mesh ) then ( LevelMapValue = RSMesh_GetFace_MapValue FaceProceduralSourceSpatialEntry.Value.Node FaceProceduralSourceSpatialEntry.Value.FaceIndex this.DataChannel ) else ( LevelMapValue = RSPoly_GetFace_MapValue FaceProceduralSourceSpatialEntry.Value.Node FaceProceduralSourceSpatialEntry.Value.FaceIndex this.DataChannel ) --Get the procedural level from the components of HandleMapValue. ProceduralLevel = this.Painter.SetData.GetProceduralLevel LevelMapValue[1] LevelMapValue[2] LevelMapValue[3] ) --If not then create a blank 'Default' RexBoundMtl NewRexMtl = RexBoundMtl() RexSetCollisionName NewRexMtl "DEFAULT" --Set the procedural name to the correct type if( ProceduralLevel == this.BlankProceduralLevel ) then ( RexSetProceduralName NewRexMtl "" ) else ( RexSetProceduralName NewRexMtl ProceduralLevel.TargetTag RexSetProcCullImmune NewRexMtl ProceduralLevel.ParentProcedural.CullImmune ) --Resolve the material ID from the master collision material NewMatID for MatID in CollisionContainerMaterial.MaterialIDList while (NewMatID == undefined) do ( if( RexAreMatsEqual CollisionContainerMaterial[MatID] NewRexMtl ) then ( NewMatID = MatID --exit() ) ) --If we have reached this far and we still don't have a MatID then we'll have to add a new one if( NewMatID == undefined ) then ( NewMatID = MultiMat_AddNewMultiMaterialSlot CollisionContainerMaterial NewRexMtl.Name = GetMaterialString NewRexMtl CollisionContainerMaterial.Names[ NewMatID ] = NewRexMtl.Name CollisionContainerMaterial[ NewMatID ] = NewRexMtl ) --Store ID changes to be acted on after loop if( FaceMatIDRemapList[ NewMatID ] == Undefined ) then ( FaceMatIDRemapList[ NewMatID ] = #{ FaceIndex } ) else ( Append FaceMatIDRemapList[ NewMatID ] FaceIndex ) --Scale density channel ScaleDensityMapValue = [ ProceduralLevel.ScaleZ, ProceduralLevel.ScaleXYZ, ProceduralLevel.Density ] RSPoly_SetFace_MapValue ProcMesh FaceIndex this.CollisionScaleDensityChannel ScaleDensityMapValue --Ground tint channel if( this.Painter.TransferGroundTint ) then ( --Check to see if we should use the tint override channel if( this.Painter.UseGroundTintOverride ) then ( --Check we have the override channel if ( PolyOp.GetMapSupport ProcMesh this.CollisionGroundTintOverrideChannel ) then ( --If the color is anything other than white (off) then set it GroundTintOverrideMapValue = RSPoly_GetFace_MapValue ProcMesh FaceIndex this.CollisionGroundTintOverrideChannel if( GroundTintOverrideMapValue != [1,1,1] ) then ( RSPoly_SetFace_MapValue ProcMesh FaceIndex this.CollisionGroundTintChannel GroundTintOverrideMapValue continue ) ) ) --Otherwise we'll use the color from our ground tint map image FaceCentre = PolyOp_GetFaceCenter ProcMesh FaceIndex GroundTintColor = this.GroundTintMapImage.GetPixel FaceCentre if (GroundTintColor == undefined) then ( GroundTintColor = color 128 128 128 ) GroundTintMapValue = [ GroundTintColor.R, GroundTintColor.G, GroundTintColor.B ] / 255.0 RSPoly_SetFace_MapValue ProcMesh FaceIndex this.CollisionGroundTintChannel GroundTintMapValue ) ) catch ( print "hit an exception" print(getCurrentException()) print item --End progress FaceProgress.End() ) ) --Run through each MatID setting target faces in batches FaceMatIDRemapCount = FaceMatIDRemapList.Count for MatID = 1 to FaceMatIDRemapCount do ( TargetFaces = FaceMatIDRemapList[MatID] --break() if( TargetFaces != Undefined ) then ( PolyOp_SetFaceMatID ProcMesh TargetFaces MatID ) ) --End progress FaceProgress.End() --Apply master material (if it's not applied already) ProcMesh.Material = CollisionContainerMaterial --meditMaterials[3] = CollisionContainerMaterial --Collapse dead structs (applies the color changes we have made during this process) PolyOp_CollapseDeadStructs ProcMesh --find and strip out faces that dont have procedural type assigned to them local FacesWithProc = #() RsGetProceduralFaces ProcMesh ProcMesh.material FacesWithProc --Check that all the faces with proc is not the same as all the faces of the procmesh. if they are not then --cull the faces without proc from the whole face set if not ((ProcMesh.faces as BitArray).numberSet == (FacesWithProc as BitArray).numberSet) then ( local CullFaces = (ProcMesh.faces as BitArray) - (FacesWithProc as BitArray) if (CullFaces.numberSet == 0) then ( print "no proc faces set" delete ProcMesh continue ) polyop.deleteFaces ProcMesh CullFaces ) --Collapse dead structs PolyOp_CollapseDeadStructs ProcMesh --check we have some faces left if (ProcMesh.faces.count > 0) then ( print "we got proc faces" --Convert mesh back to collision Mesh2Col ProcMesh --Set a new stamp this.SetProceduralStamp item this.SetProceduralStamp ProcMesh --Set the flag to 'Use Procedural Tint' SetAttr ProcMesh this.ProcecduralTintAttributeIdx True --Set the material collision flag gRsCollTypes.setObjCollTypeFlags ProcMesh "Material" /* if( this.Painter.TransferGroundTint ) then ( --Check the collisions to ensure they are under the 255 combination limit this.PalletiseGroundTint CollisionTargetList ) */ --add the procmesh to its own layer AddModelToProcMeshLayer ProcMesh SetCollisionTargetProperty CollisionMesh False ProcMesh.wirecolor = orange ) else ( print "no faces in the procmesh" delete ProcMesh --remove the object from the transfer list --this.Painter.TransferArray append CreatesEmptyProcMesh item --remove it as a target -- if lastItem then -- ( -- this.TransferListRemoveCollision CollisionTargetList -- ) ) ) --Update our transfer list this.TransferListUpdate() ), /*------------------------------------------------------------------------------ Imports data from the procedural map image */------------------------------------------------------------------------------ fn ImportData = ( --Check selection is valid if not( this.CheckNodesAreValid Selection ) then ( return False ) --Check user does want to do the operation if not( QueryBox "This operation will import procedural data from the master map image. This will overwrite procedural data that exists all ready on the selected objects. Continue?") then ( return() ) --First we load the map image if not( this.ProceduralMapImage.Load() ) then ( MessageBox @"Failed to load the image. Check your workspace is mapped and upto date with; //depot/gta5/assets/maps/procPaint/ProceduralTypeMap.png" return() ) --Then check our selection TargetArray = Selection as Array --Create our top progress window ImportMapImageDataProgress = RSProgressWindow Title:"Importing data from map image..." StartStep:0 EndStep:TargetArray.Count ImportMapImageDataProgress.Start() --Then we go through each object in the target array for Target in TargetArray do ( --Push progress update ImportMapImageDataProgress.PostProgressStep() --Check we have map support on the handle channel and wipe down to black RSPoly_ResetMapChannel Target this.DataChannel [0,0,0] --Get face count for the target FaceCount = GetNumFaces Target --Create sub progress FaceProgress = ImportMapImageDataProgress.StartSubProgress 0 FaceCount SubTitle:Target.Name FaceProgress.Start() --For each face in the target we will get the major pixel and --attempt to derive the procedural from this. for FaceIndex = 1 to FaceCount do ( --Push progress update FaceProgress.PostProgressStep() --Get face centre FaceCentre = PolyOp_GetFaceCenter Target FaceIndex --Get the major color FaceColor = this.ProceduralMapImage.GetPixel FaceCentre --RSImage_GetMajorColor this.MapImage FacePointList --Create a Point3 which has the values scaled so they fit in the 0.0 - 1.0 FaceMapValue = [ FaceColor.R, FaceColor.G, FaceColor.B ] / 255.0 --Set the target faces to the map value RSPoly_SetFace_MapValue Target FaceIndex this.DataChannel FaceMapValue ) --Collapse dead structs to push the changes through and garbage collect PolyOp.CollapseDeadStructs Target --Set time and data stamp this.SetProceduralStamp Target --End progress FaceProgress.End() ) --End progress ImportMapImageDataProgress.End() ), /*------------------------------------------------------------------------------ Exports data to the procedural map image */------------------------------------------------------------------------------ fn ExportData = ( --First we load the map image if not( this.ProceduralMapImage.Load() ) then ( MessageBox @"Failed to load the image. Check your workspace is mapped and upto date with; //depot/gta5/assets/maps/procPaint/ProceduralTypeMap.png" return() ) --Then check our selection TargetArray = this.GetProceduralSourceList() --Create our top progress window ExportMapImageDataProgress = RSProgressWindow Title:"Exporting procedural data to map image..." StartStep:0 EndStep:TargetArray.Count ExportMapImageDataProgress.Start() --Then we go through each object in the target array for Target in TargetArray do ( --Push progress update ExportMapImageDataProgress.PostProgressStep() --Get face count for the target FaceCount = GetNumFaces Target --Create sub progress FaceProgress = ExportMapImageDataProgress.StartSubProgress 0 FaceCount SubTitle:Target.Name FaceProgress.Start() --For each face in the target we will get the procedural level for FaceIndex = 1 to FaceCount do ( --ToDo Fix the exporting of data. Currently it doesn't work properly. --Max seems to add noise to the image effectively breaking the whole system. --Push progress update FaceProgress.PostProgressStep() --Get all the face verts and map value FaceVertexPositionList LevelMapValue if( ClassOf Target == Editable_Mesh ) then ( FaceVertexList = GetFace Target FaceIndex FaceVertexPositionList = for FaceVertex in FaceVertexList collect ( MeshOp.GetVert Target FaceVertex ) LevelMapValue = RSMesh_GetFace_MapValue Target FaceIndex this.DataChannel ) if( ClassOf Target == Editable_Poly or ClassOf Target == PolyMeshObject ) then ( FaceVertexList = PolyOp.GetFaceVerts Target FaceIndex FaceVertexPositionList = for FaceVertex in FaceVertexList collect ( PolyOp.GetVert Target FaceVertex ) LevelMapValue = RSPoly_GetFace_MapValue Target FaceIndex this.DataChannel ) --Get the procedural level from the components of LevelMapValue. ProceduralLevel = this.Painter.SetData.GetProceduralLevel LevelMapValue[1] LevelMapValue[2] LevelMapValue[3] --Setup our map values from the procedural level LevelColor = Black if( ProceduralLevel != this.BlankProceduralLevel ) then ( LevelColor = Color ProceduralLevel.Color.R ProceduralLevel.Color.G ProceduralLevel.Color.B ) --Set the color this.ProceduralMapImage.SetAreaPixels FaceVertexPositionList LevelColor ) --End progress FaceProgress.End() ) --End progress ExportMapImageDataProgress.End() --Save the map image this.ProceduralMapImage.Save() --Dispose of the map image resources this.ProceduralMapImage.Dispose() ), /*-------------------------------------------------- Clips the values of the input MapValue so that it sits correctly in the 0-1 range */-------------------------------------------------- fn NormaliseMapValue InputMapValue = ( for MapValueIndex = 1 to 3 do ( CurrentValue = InputMapValue[MapValueIndex] if( CurrentValue == 0 or CurrentValue == 1 ) then continue if( CurrentValue < 0 ) then ( OffsetValue = Ceil ( Abs( CurrentValue ) ) InputMapValue[MapValueIndex] += OffsetValue ) else ( OffsetValue = Floor CurrentValue InputMapValue[MapValueIndex] -= OffsetValue ) ) return InputMapValue ), --The maximum limit on collision combinations CollisionCombinationLimit = 100, /*-------------------------------------------------- Creates a collision set for the input mesh. Note that the mesh must be converted to a editable mesh before */-------------------------------------------------- fn GetCollisionCombinationSet InputMesh ParentProgress:Undefined = ( --Get face count for collision FaceCount = GetNumFaces InputMesh --Check if we have information in the various channels MeshHasScaleDensity = MeshOp_GetMapSupport InputMesh this.CollisionScaleDensityChannel MeshHasGroundTint = MeshOp_GetMapSupport InputMesh this.CollisionGroundTintChannel --Start our collision combination set (tracks all our collision combinations) CollisionCombinationSet = DotNetObject "ProceduralSystem.CollisionCombinationSet" --Start a sub progress FaceIndexProgress if( ParentProgress == Undefined )then ( FaceIndexProgress = RSProgressWindow Title:"Palletising collision meshes (255 limit)..." StartStep:0 EndStep:FaceCount ) else ( FaceIndexProgress = ParentProgress.StartSubProgress 0 FaceCount SubTitle:InputMesh.Name ) FaceIndexProgress.Start() --For each face of the collision mesh for FaceIndex = 1 to FaceCount do ( --Push progress update FaceIndexProgress.PostProgressStep() --Get face material FaceMatID = MeshOp_GetFaceMatID InputMesh FaceIndex FaceRexMat = InputMesh.Material[FaceMatID] --Get string representation of the RexBoundMtl FaceRexMatString = "" if( ClassOf FaceRexMat == RexBoundMtl ) then ( FaceRexMatString = GetMaterialString FaceRexMat ) --Figure out the various different levels applied for Scale and Density ScaleXYZLevel = 0 ScaleZLevel = 0 DensityLevel = 0 if( MeshHasScaleDensity ) then ( ScaleDensityMapValue = RSMesh_GetFace_MapValue InputMesh FaceIndex 12 ScaleDensityMapValue = NormaliseMapValue ScaleDensityMapValue ScaleZLevel = RoundToIncrement( ScaleDensityMapValue[1] * 3 ) ScaleXYZLevel = RoundToIncrement( ScaleDensityMapValue[2] * 15 ) DensityLevel = RoundToIncrement( ScaleDensityMapValue[3] * 3 ) ) --Get current ground tint color GroundTintR = 0 GroundTintG = 0 GroundTintB = 0 if( MeshHasGroundTint ) then ( GroundTintMapValue = RSMesh_GetFace_MapValue InputMesh FaceIndex 11 GroundTintMapValue = this.NormaliseMapValue GroundTintMapValue GroundTintR = RoundToIncrement ( 255 * GroundTintMapValue[1] ) GroundTintG = RoundToIncrement ( 255 * GroundTintMapValue[2] ) GroundTintB = RoundToIncrement ( 255 * GroundTintMapValue[3] ) GroundTintR = DotNetObject "System.Byte" GroundTintR GroundTintG = DotNetObject "System.Byte" GroundTintG GroundTintB = DotNetObject "System.Byte" GroundTintB ) --Create ground tint color FaceGroundTint = DotNetObject "ProceduralSystem.Color" GroundTintR GroundTintG GroundTintB --Create a combined string from all the information collected --FaceCollisionCombinationString = ToUpper( FaceRexMatString + " SCALEZ:" + (ScaleZLevel as String) + " SCALEXYZ:" + (ScaleXYZLevel as String) + " DENSITY:" + (DensityLevel as String) + " TINT_R:" + ( GroundTintR as String) + " TINT_G:" + ( GroundTintG as String) + " TINT_B:" + ( GroundTintB as String) ) FaceCollisionCombinationString = ToUpper( FaceRexMatString + " SCALEZ:" + (ScaleZLevel as String) + " SCALEXYZ:" + (ScaleXYZLevel as String) + " DENSITY:" + (DensityLevel as String) ) --Create hash from the string FaceCollisionCombinationHash = GetHashValue FaceCollisionCombinationString 1 --Add the face to the collision combination set CollisionCombinationSet.AddFace FaceCollisionCombinationHash FaceIndex FaceGroundTint ) --End progress FaceIndexProgress.End() --Return our CollisionCombinationSet return CollisionCombinationSet ), /*-------------------------------------------------- Palletise the ground tint channel for each of the passed collision */-------------------------------------------------- fn PalletiseGroundTint InputCollisionMeshList = ( --Create our top progress window CollisionMeshProgress = RSProgressWindow Title:"Palletising collision meshes (255 limit)..." StartStep:0 EndStep:InputCollisionMeshList.Count CollisionMeshProgress.Start() for CollisionMesh in InputCollisionMeshList do ( --Push progress update CollisionMeshProgress.PostProgressStep() --Convert the collision mesh to mesh Col2Mesh CollisionMesh --Create our collision combination set based on the materials on the collision CollisionCombinationSet = this.GetCollisionCombinationSet CollisionMesh ParentProgress:CollisionMeshProgress if( CollisionCombinationSet.BaseCombinationCount() < this.CollisionCombinationLimit ) then ( --Reduce the ground tint channel until we are under the combination limit CurrentThreshold = 2 while( CollisionCombinationSet.FullCombinationCount() > this.CollisionCombinationLimit ) do ( CollisionCombinationSet.CondenseGroundTints( CurrentThreshold ) CurrentThreshold += 2 ) --Give all the faces unique map space MeshOp_ApplyUVWMap CollisionMesh #Face Channel:this.CollisionGroundTintChannel for FaceInfo in CollisionCombinationSet.FaceInfoArray do ( GroundTintMapValue = [FaceInfo.FaceGroundTint.ScR, FaceInfo.FaceGroundTint.ScG, FaceInfo.FaceGroundTint.ScB] RSMesh_SetFace_MapValue CollisionMesh FaceInfo.FaceIndex this.CollisionGroundTintChannel GroundTintMapValue ) ) else ( WarningMessage = Format "Can't fix this collision mesh [%] by palletising the ground tint channel as there are already too many base collision material combinations. This mesh will need to be split or the number of procedurals/surface types reduced in order to get it to export correctly." CollisionMesh.Name gRSULog.LogWarning WarningMessage Context:"ProceduralPainter" ) --Convert mesh back to collision Mesh2Col CollisionMesh ) --End progress CollisionMeshProgress.End() ), --The paths to the relevant metadata and UI markup for the tool UIMarkupFilePath = RsMakeBackSlashes( RsConfigGetWildWestDir() + "/script/3dsMax/UI/ProceduralPainter.xaml" ), SetDataFilePath = RsMakeBackSlashes( gRsConfig.Project.DefaultBranch.Assets + "/maps/procPaint/ProceduralSets.xml" ), MetadataFilePath = RsMakeBackSlashes( gRsConfig.Project.DefaultBranch.Common + "/data/materials/procedural.meta" ), ThumbnailDirectoryPath = RsMakeBackSlashes( gRsConfig.Project.DefaultBranch.Assets + "/maps/procPaint/Thumbnails/..." ), /*------------------------------------------------------------------------------ Intializes the tool */------------------------------------------------------------------------------ on Create do ( --Sync to the latest on the relevant files if( gRsPerforce.connect() ) then ( RSPushPrompt "Syncing assets..." SyncSuccess = gRsPerforce.Sync #( this.SetDataFilePath, this.MetadataFilePath, this.ThumbnailDirectoryPath, this.GroundTintMapImage.ImageFilePath, this.ProceduralMapImage.ImageFilePath ) if not ( SyncSuccess ) then ( MessageBox "Unable to sync correctly to some of the required assets. You may have them set to be writable locally and/or there maybe problems with your P4 setup. The tool may not function correctly without the latest on those assets. Continue at your own risk..." ) RSPopPrompt() ) --Create an instance of the 'Painter' class passing in the relevant file paths this.Painter = DotNetObject "ProceduralSystem.Painter" SetDataFilePath MetadataFilePath --Setup our tool window and open it this.ToolWindow = RSToolWindow Name:"Procedural Painter" UISource:UIMarkupFilePath --Alias our blank procedural level - nothing more than a shortcut in script this.BlankProceduralLevel = this.Painter.SetData.BlankProceduralLevel this.UpdateRenderSettings this.BlankProceduralLevel this.UpdateDisplaySettings this.BlankProceduralLevel --Bind our tool window content to the painter instance --This will link them together. this.Painter.BindElement this.ToolWindow.Content --Updates our transfer list this.TransferListUpdate() --Make sure we have 1GB of heap memory (needed for transfering) if( HeapSize < 1048576000 ) then ( HeapSize = 1048576000 ) --Open the window this.ToolWindow.Open() ) ) ProceduralPainter = ProceduralPainter() /*------------------------------------------------------------------------------ EVENTS /*------------------------------------------------------------------------------ Action on display mode changed. */------------------------------------------------------------------------------ fn ProceduralPainter_OnDisplayModeChanged = ( print "Updating overall display settings" ProceduralPainter.UpdateAllDisplaySettings() ) DotNet.AddEventHandler ProceduralPainter.Painter "DisplayModeChanged" ProceduralPainter_OnDisplayModeChanged /*------------------------------------------------------------------------------ Action on display mode changed. */------------------------------------------------------------------------------ fn ProceduralPainter_OnRenderModeChanged = ( print "Updating overall render settings" ProceduralPainter.UpdateAllRenderSettings() ) DotNet.AddEventHandler ProceduralPainter.Painter "RenderModeChanged" ProceduralPainter_OnRenderModeChanged /*------------------------------------------------------------------------------ Action when user requests to apply procedural level to selection */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedApplyLevelToSelection s e = ( print "Applying to selection" ProceduralPainter.ApplyProceduralLevel #Selection e.TargetProceduralLevel ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedApplyLevelToSelection" ProceduralPainter_OnRequestedApplyLevelToSelection /*------------------------------------------------------------------------------ Action when user requests to select polys that use a given procedural level */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedSelectFacesUsedByLevel s e = ( print "Selecting procedural level" ProceduralPainter.SelectProceduralLevel e.TargetProceduralLevel ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedSelectFacesUsedByLevel" ProceduralPainter_OnRequestedSelectFacesUsedByLevel /*------------------------------------------------------------------------------ Action when user requests to start editing */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedStartEdit s e = ( print "Starting edit" ProceduralPainter.Painter.CurrentlyEditing = ProceduralPainter.StartEditing() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedStartEdit" ProceduralPainter_OnRequestedStartEdit /*------------------------------------------------------------------------------ Action when user requests to stop editing */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedStopEdit s e = ( print "Stoping edit" ProceduralPainter.StopEditing() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedStopEdit" ProceduralPainter_OnRequestedStopEdit /*------------------------------------------------------------------------------ Action when user requests to commit unsaved data */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedCommitData s e = ( print "Commiting data" DisableSceneRedraw() with undo off ( ProceduralPainter.CommitData() ) EnableSceneRedraw() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedCommitData" ProceduralPainter_OnRequestedCommitData /*------------------------------------------------------------------------------ Action when user requests to select polys that use a given procedural */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedSelectFacesUsedByProcedural s e = ( print "Selecting procedural" ProceduralPainter.SelectProcedural e.TargetProcedural ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedSelectFacesUsedByProcedural" ProceduralPainter_OnRequestedSelectFacesUsedByProcedural /*------------------------------------------------------------------------------ Action when user requests to auto blend the levels across the faces selected */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedAutoBlendFaceProceduralLevels s e = ( print "Selecting procedural" ProceduralPainter.AutoBlendProceduralLevels() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedAutoBlendFaceProceduralLevels" ProceduralPainter_OnRequestedAutoBlendFaceProceduralLevels /*------------------------------------------------------------------------------ Action when user requests to process spacing on the selected faces */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedProcessFaceSpacing s e = ( print "Processing spacing on the selected faces." ProceduralPainter.ProcessFaceSpacing() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedProcessFaceSpacing" ProceduralPainter_OnRequestedProcessFaceSpacing /*------------------------------------------------------------------------------ Action when user requests to clear polys that use a given procedural */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedClearProcedural s e = ( print "Clearing procedural" if( QueryBox "This is going to clear all faces using this procedural (across all levels). Are you sure you want to do this?" ) then ( ProceduralPainter.RemoveProcedural e.TargetProcedural ) ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedClearProcedural" ProceduralPainter_OnRequestedClearProcedural /*------------------------------------------------------------------------------ Action when user requests to import data from previous procedural painter tool */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedImportData s e = ( print "Importing data from master map image" with undo off ( ProceduralPainter.ImportData() ) ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedImportData" ProceduralPainter_OnRequestedImportData /*------------------------------------------------------------------------------ Action when user requests to import data from previous procedural painter tool */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedExportData s e = ( print "Exporting data to master map image" ProceduralPainter.ExportData() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedExportData" ProceduralPainter_OnRequestedExportData /*------------------------------------------------------------------------------ Action when user requests to transfer data across to the selected collision objects */------------------------------------------------------------------------------ /* fn ProceduralPainter_OnRequestedTransferData s e = ( print "Transfering data" DisableSceneRedraw() with undo off ( ProceduralPainter.TransferData() ) EnableSceneRedraw() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedTransferData" ProceduralPainter_OnRequestedTransferData */ /*------------------------------------------------------------------------------ Action when user requests to transfer data across to new procmesh object */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedTransferDataProcMesh s e = ( print "Transfering data" try ( DisableSceneRedraw() with undo off ( ProceduralPainter.TransferDataToProcMesh() ) EnableSceneRedraw() ) catch ( EnableSceneRedraw() ) ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedTransferDataProcMesh" ProceduralPainter_OnRequestedTransferDataProcMesh /*------------------------------------------------------------------------------ Action when user requests to live update the selected faces */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedLiveFaceUpdate s e = ( print "Live updating faces" ProceduralPainter.LiveUpdateFaces() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedLiveFaceUpdate" ProceduralPainter_OnRequestedLiveFaceUpdate /*------------------------------------------------------------------------------ Action when user requests to add the selected collision mesh to the transfer list */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedAddCollision s e = ( print "Adding collision meshes" ProceduralPainter.TransferListAddCollision (Selection as Array) ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedAddCollision" ProceduralPainter_OnRequestedAddCollision /*------------------------------------------------------------------------------ Action when user requests to remove the selected collision mesh from the transfer list */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedRemoveCollision s e = ( print "Removing collision meshes" ProceduralPainter.TransferListRemoveCollision (Selection as Array) ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedRemoveCollision" ProceduralPainter_OnRequestedRemoveCollision /*------------------------------------------------------------------------------ Action when user requests to select a mesh from the transfer list */------------------------------------------------------------------------------ fn ProceduralPainter_FocusSelectListItemEvent s e = ( print "Selecting list mesh" clearSelection() local newSelection = #() for NodeData in ProceduralPainter.Painter.TransferArray do ( if NodeData.IsSelected then ( --print "isSelected" append newSelection (MaxOps.GetNodeByHandle NodeData.NodeHandle) ) ) if newSelection.count > 0 then ( select newSelection max zoomext sel all ) ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedFocusSelectedVisualTargets" ProceduralPainter_FocusSelectListItemEvent /*------------------------------------------------------------------------------ Action when user requests to select all collision mesh from the transfer list */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedSelectVisualTargets s e = ( print "Selecting visual meshes" ProceduralPainter.TransferListSelectAllCollision() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedSelectVisualTargets" ProceduralPainter_OnRequestedSelectVisualTargets /*------------------------------------------------------------------------------ Action when user requests to select none collision mesh from the transfer list */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedSelectNoneVisualTargets s e = ( print "Unselecting visual meshes" ProceduralPainter.TransferListSelectNoneCollision() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedSelectNoneVisualTargets" ProceduralPainter_OnRequestedSelectNoneVisualTargets /*------------------------------------------------------------------------------ Action when user requests to update the transfer list */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedUpdateTransferList s e = ( print "Updating transfer list" ProceduralPainter.TransferListUpdate() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedUpdateTransferList" ProceduralPainter_OnRequestedUpdateTransferList /*------------------------------------------------------------------------------ Action when tool requests to start visually highlighting a given procedural level */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedStartHighlightProceduralLevel s e = ( print "Starting highlighting of procedural." ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedStartHighlightProceduralLevel" ProceduralPainter_OnRequestedStartHighlightProceduralLevel /*------------------------------------------------------------------------------ Action when tool requests to stop visually highlighting a given procedural level */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedStopHighlightProceduralLevel s e = ( print "Stoping highlighting of procedural." ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedStopHighlightProceduralLevel" ProceduralPainter_OnRequestedStopHighlightProceduralLevel /*------------------------------------------------------------------------------ Action when tool posts a change to the camera sync state */------------------------------------------------------------------------------ fn ProceduralPainter_OnCameraSyncStateAltered s e = ( print "Camera sync state altered." if( ProceduralPainter.Painter.CameraSyncState == On ) then ( ProceduralPainter.LiveSyncCameraOn() ) else ( ProceduralPainter.LiveSyncCameraOff() ) ) DotNet.AddEventHandler ProceduralPainter.Painter "CameraSyncStateAltered" ProceduralPainter_OnCameraSyncStateAltered /*------------------------------------------------------------------------------ Action when tool request to import live data from game */------------------------------------------------------------------------------ fn ProceduralPainter_OnRequestedImportLiveData s e = ( print "Requested import of live data." ProceduralPainter.LiveImport() ) DotNet.AddEventHandler ProceduralPainter.Painter "RequestedImportLiveData" ProceduralPainter_OnRequestedImportLiveData /*------------------------------------------------------------------------------ Action when the user requests the tool to be closed down */------------------------------------------------------------------------------ fn ProceduralPainter_OnFormClosing s e = ( print "Closing down" ProceduralPainter.StopEditing() if( ProceduralPainter.Painter.CurrentlyEditing ) then ( --If we are still editing then cancel the close down. e.Cancel = true ) ) DotNet.AddEventHandler ProceduralPainter.ToolWindow.Form "FormClosing" ProceduralPainter_OnFormClosing