Files
2025-09-29 00:52:08 +02:00

2440 lines
90 KiB
Plaintext
Executable File

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