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

587 lines
24 KiB
Plaintext
Executable File

---------------------------------------------------------------------------------------------------------------------------------------
-- RSL_CollisionOps2
-- By Paul Truss
-- Senior Technical Artist
-- Rockstar Games London
-- 12/01/2011
--
-- Modified for Rockstar North by Neil Gregory
-- 08/2011
--
-- Collision Tools
-- Create collision objects with pre-defined attributes
-- Edit collision object attributes, both standard and rexbound. Display surface types on rexbound collision meshes.
-- Select/Display collision objects based on class, attribute and surface type
---------------------------------------------------------------------------------------------------------------------------------------
-- dotNet class shortcuts script
filein (RsConfigGetWildWestDir()+"/script/max/rockstar_london/utils/RSL_dotNetClasses.ms")
-- dotNet UI functions script
filein (RsConfigGetWildWestDir()+"/script/max/rockstar_london/utils/RSL_dotNetUIOps.ms")
filein (RsConfigGetToolsDir()+"/dcc/current/max2011/scripts/pipeline/util/collision_funcs.ms")
-- Collision Ops 2 specific scripts
filein (RsConfigGetWildWestDir()+"/script/3dsMax/General_tools/RSL_CollisionOps2Attribute.ms")
filein (RsConfigGetWildWestDir()+"/script/3dsMax/General_tools/RSL_CollisionOps2Create.ms")
filein (RsConfigGetWildWestDir()+"/script/3dsMax/General_tools/RSL_CollisionOps2Edit.ms")
filein (RsConfigGetWildWestDir()+"/script/3dsMax/General_tools/RSL_CollisionOps2SelectDisplay.ms")
global RSL_CollisionOps
/*
-- Struct used to store collision types for collision meshes so that we know what it's currently set to
-- and we can change them so something else in create mode.
struct RSL_CollisionType
(
--camera = true, -- Camera attribute
--physics = true, -- Physics attribute
--hiDetail = true, -- Hi Detail attribute
--loco = false, -- Loco attribute
-- if we change any of the collision type attributes we set this to true so that when the object is switched back to a collision mesh
-- we know to update the collision type attributes for the object
modified = false,
-- collision type attribute indices
--idxCamera = getAttrIndex "Gta Collision" "Camera",
--idxPhysics = getAttrIndex "Gta Collision" "Physics",
--idxHiDetail = getAttrIndex "Gta Collision" "Hi Detail",
--idxLoco = getAttrIndex "Gta Collision" "Loco",
---------------------------------------------------------------------------------------------------------------------------------------
-- setTypesByObject() gets the attributes from the parsed object
---------------------------------------------------------------------------------------------------------------------------------------
fn setTypesByObject object =
(
camera = try (getAttr object idxCamera) catch(false)
physics = try (getAttr object idxPhysics) catch(false)
hiDetail = try (getAttr object idxHiDetail) catch(false)
loco = try (getAttr object idxLoco) catch(false)
)
)
*/
-- main struct
struct RSL_CollisionOps2
(
---------------------------------------------------------------------------------------------------------------------------------------
-- Struct variables
---------------------------------------------------------------------------------------------------------------------------------------
INIfile = (RsConfigGetWildWestDir() + "etc/config/general/collisionOps2.ini"),
helpLink = "https://devstar.rockstargames.com/wiki/index.php/Collision_Operations",
-- struct instances
createUI = RSL_CollisionOps2Create(),
editUI = RSL_CollisionOps2Edit(),
selectDisplayUI = RSL_CollisionOps2SelectDisplay(),
attribute = RSL_CollisionOps2Attribute(),
currentTool, -- variable containing the interface for the current mode
theObjects = #(), -- variable containing the currently selected objects if they are valid
currentMesh, -- variable containing the currently selected collision mesh
--currentColType = RSL_CollisionType(), -- struct instance
currentType = #primitive, -- variable containing the currently selected collision type e.g. #rexbound or #primitive
---------------------------------------------------------------------------------------------------------------------------------------
-- UI variables
---------------------------------------------------------------------------------------------------------------------------------------
CollisionForm,
mainTableLayout,
helpButton,
createCheckButton,editCheckButton,displayCheckButton,
---------------------------------------------------------------------------------------------------------------------------------------
--
-- FUNCTIONS
--
---------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------
-- checkCreateSelection() checks the current selection to see if it is a vaild selection when in create mode. Returns true or false
---------------------------------------------------------------------------------------------------------------------------------------
fn checkCreateSelection =
(
-- make sure we have at least one object selected
local result = selection.count > 0
-- if we do have at least one object selected...
if result then
(
-- set theObjects struct variable to the current selection
theObjects = selection as array
-- loop through theObjects making sure we have only geometry selected and that they are not Xref objects
for object in theObjects do
(
if (getAttrClass object) != "Gta Object" or (classOf object) == XrefObject then
(
result = false
)
)
)
result
),
---------------------------------------------------------------------------------------------------------------------------------------
-- createSelectionChanged() CallBack function used in create mode that checks the selection is valid. Sets the createUI.statusLabel
-- and the enabled state of the createUI.createButton
---------------------------------------------------------------------------------------------------------------------------------------
fn createSelectionChanged =
(
if (checkCreateSelection()) then
(
-- if one valid object is selected, set the createUI.statusLabel to the name of the object
-- otherwise set it to "Multiple Objects"
if theObjects.count == 1 then
(
createUI.statusLabel.text = " Selected: " + theObjects[1].name
)
else
(
createUI.statusLabel.text = " Selected: Multiple Objects"
)
createUI.createButton.enabled = true
)
else
(
-- if no objects are selected, set createUI.statusLabel to "None" or if the selection is invalid, set it to "Invalid Selection"
if selection.count == 0 then
(
createUI.statusLabel.text = " Selected: None"
)
else
(
createUI.statusLabel.text = " Selected: Invalid Selection"
)
createUI.createButton.enabled = false
theObjects = undefined
)
),
---------------------------------------------------------------------------------------------------------------------------------------
-- checkCreateSelection() checks the current selection to see if it is a vaild selection when in edit mode. Returns true or false
---------------------------------------------------------------------------------------------------------------------------------------
fn checkEditSelection =
(
-- make sure we have at least one object selected
local result = selection.count > 0
-- if the currentMesh struct variable is not undefined then we need to switch the currentMesh back to a collision mesh
if currentMesh != undefined then
(
editUI.switchToCollision currentMesh
currentMesh = undefined
)
-- if we do have at least one object selected...
if result then
(
theObjects = selection as array
-- in edit mode we allow you to have multiple primitive objects selected or just one collision mesh.
-- if we have multiple objects selected, we check so make sure they are all "Gta Collision" and that none of then are collision meshes
if theObjects.count > 1 then
(
for object in theObjects do
(
if (getAttrClass object) != "Gta Collision" then
(
result = false
exit
)
else
(
if (classOf object) == Col_Mesh then
(
result = false
exit
)
)
)
)
else
(
-- if we have just one object selected, we check that it is a "Gta Collision" object
if (getAttrClass theObjects[1]) != "Gta Collision" then
(
result = false
)
else
(
-- if it is a collision mesh object, we set the currentMesh struct variable to the object
-- and gather it's collision type attributes into the currentColType struct instance
if (classOf theObjects[1]) == Col_Mesh then
(
currentMesh = theObjects[1]
--currentColType.setTypesByObject theObjects[1]
)
)
)
)
result
),
---------------------------------------------------------------------------------------------------------------------------------------
-- createSelectionChanged() CallBack function used in edit mode that checks the selection is valid. Sets up the attribute
-- controls depending on the selection e.g. one collision mesh is selected so we create rexbound attribute controls
---------------------------------------------------------------------------------------------------------------------------------------
fn editSelectionChanged =
(
if (checkEditSelection()) then
(
-- if the selection is valid, enable the edit controls
currentTool.enabled = true
-- if one valid object is selected and it's a collision mesh...
if theObjects.count == 1 and (classOf theObjects[1]) == Col_Mesh then
(
-- swicth the collision mesh over to an editable mesh, gather the attributes and setup the display material
editUI.switchToMesh currentMesh
-- if the currentType is not #rexbound, we need to re-create the attribute controls for rexbound specific attributes
if editUI.attributesGroup != undefined and currentType != #rexbound then
(
currentType = #rexbound
editUI.attributesGroup.controls.clear()
editUI.attributesGroup.controls.add (editUI.createAttributeControls #rexbound)
)
)
else
(
-- if one or more valid objects are selected and they're collision primitives and the currentType is not #primitive
-- we need to re-create the attribute controls for standard collision object attributes
if editUI.attributesGroup != undefined and currentType != #primitive then
(
currentType = #primitive
editUI.attributesGroup.controls.clear()
editUI.attributesGroup.controls.add (editUI.createAttributeControls #primitive)
)
)
-- the select controls only work on #rexbound collision meshes so we dissable them if the currentType is not #rexbound
editUI.selectGroup.enabled = currentType == #rexbound
)
else
(
theObjects = undefined
-- dissable the edit controls if the selection is invalid.
currentTool.enabled = false
)
),
---------------------------------------------------------------------------------------------------------------------------------------
--
-- UI FUNCTIONS
--
---------------------------------------------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------------------------------------------
-- dispatchOnFormClosing() Dispatches onFormClosing() Called when the user closes collision ops
---------------------------------------------------------------------------------------------------------------------------------------
fn dispatchOnFormClosing s e =
(
RSL_CollisionOps.onFormClosing s e
),
---------------------------------------------------------------------------------------------------------------------------------------
-- onFormClosing() Called when collision ops is closed
---------------------------------------------------------------------------------------------------------------------------------------
fn onFormClosing s e =
(
-- if the currentMesh struct variable is not undefined then we need to switch the currentMesh back to a collision mesh
if currentMesh != undefined then
(
editUI.switchToCollision currentMesh
currentMesh = undefined
)
-- NEEDS MORE WORK
-- if currentTool != undefined and currentTool.name == "mainTableLayout" then
-- (
-- selectDisplayUI.exitSelectDisplay()
-- )
-- close any of the separate dialogs if they are open
try (attribute.surfaceTypeForm.close()) catch()
try (attribute.proceduralTypeForm.close()) catch()
try (attribute.pieceTypeForm.close()) catch()
-- remove the callbacks if they are running
callbacks.removeScripts id:#RSL_CollisionOps_createCallBacks
callbacks.removeScripts id:#RSL_CollisionOps_editCallBacks
),
---------------------------------------------------------------------------------------------------------------------------------------
-- disableAccelerators() Called when the user enters any control that needs keyboard input e.g. a textBox
---------------------------------------------------------------------------------------------------------------------------------------
fn disableAccelerators s e =
(
enableAccelerators = false
),
---------------------------------------------------------------------------------------------------------------------------------------
-- dispatchOnFormClosing() Dispatches helpButtonPressed() Called when the user presses the help button
---------------------------------------------------------------------------------------------------------------------------------------
fn dispatchHelpButtonPressed s e =
(
RSL_CollisionOps.helpButtonPressed s e
),
---------------------------------------------------------------------------------------------------------------------------------------
-- helpButtonPressed() Opens the help page in a browser
---------------------------------------------------------------------------------------------------------------------------------------
fn helpButtonPressed s e =
(
ShellLaunch helpLink ""
),
---------------------------------------------------------------------------------------------------------------------------------------
-- dispatchModeCheck() Dispatches modeCheck() Called when the user presses any one of the three mode buttons
---------------------------------------------------------------------------------------------------------------------------------------
fn dispatchModeCheck s e =
(
RSL_CollisionOps.modeCheck s e
),
---------------------------------------------------------------------------------------------------------------------------------------
-- helpButtonPressed() Switches the mode depending on which mode button is pressed
---------------------------------------------------------------------------------------------------------------------------------------
fn modeCheck s e =
(
-- if the mode button is checked...
if s.checked then
(
CollisionForm.MaximumSize = dotNetObject "System.Drawing.Size" 2048 2048
case s.name of
(
"createCheckButton":-- if create mode is checked
(
-- if the currentMesh struct variable is not undefined then we need to switch the currentMesh back to a collision mesh
if currentMesh != undefined then
(
editUI.switchToCollision currentMesh
currentMesh = undefined
)
-- remove the edit callbacks
callbacks.removeScripts id:#RSL_CollisionOps_editCallBacks
-- un-check the other mode check buttons
editCheckButton.checked = false
displayCheckButton.checked = false
-- dispose of the current tool and create the new UI
if currentTool != undefined then currentTool.Dispose()
currentTool = createUI.getUI()
CollisionForm.Height = 750
-- run the create callback function the first time and then enabled the callback
createSelectionChanged()
callbacks.addScript #selectionSetChanged "RSL_CollisionOps.createSelectionChanged()" id:#RSL_CollisionOps_createCallBacks
)
"editCheckButton":
(
-- remove the create callbacks
callbacks.removeScripts id:#RSL_CollisionOps_createCallBacks
-- dispose of the current tool and create the new UI
if currentTool != undefined then currentTool.Dispose()
currentTool = editUI.getUI()
-- run the edit callback function the first time
editSelectionChanged()
-- un-check the other mode check buttons
createCheckButton.checked = false
displayCheckButton.checked = false
-- set the form height if it's not big enough
if CollisionForm.Height < 768 then
(
CollisionForm.Height = 768
)
-- enabled the edit callback
callbacks.addScript #selectionSetChanged "RSL_CollisionOps.editSelectionChanged()" id:#RSL_CollisionOps_editCallBacks
)
"displayCheckButton":
(
-- if the currentMesh struct variable is not undefined then we need to switch the currentMesh back to a collision mesh
if currentMesh != undefined then
(
editUI.switchToCollision currentMesh
currentMesh = undefined
)
-- remove both the create and edit callbacks
callbacks.removeScripts id:#RSL_CollisionOps_createCallBacks
callbacks.removeScripts id:#RSL_CollisionOps_editCallBacks
-- un-check the other mode check buttons
createCheckButton.checked = false
editCheckButton.checked = false
-- dispose of the current tool and create the new UI
if currentTool != undefined then currentTool.Dispose()
currentTool = selectDisplayUI.getUI()
-- set the form height
CollisionForm.Height = 808
)
)
-- add the currentTool to the mainTableLayout control and set its column span
mainTableLayout.Controls.Add currentTool 0 2
mainTableLayout.SetColumnSpan currentTool 3
)
else
(
-- the mode button is un-checked so we remove the callbacks, dispose of the currentTool and reset the form height
callbacks.removeScripts id:#RSL_CollisionOps_createCallBacks
callbacks.removeScripts id:#RSL_CollisionOps_editCallBacks
if currentTool != undefined then
(
currentTool.dispose()
)
CollisionForm.MaximumSize = dotNetObject "System.Drawing.Size" 2048 82
CollisionForm.Height = 82
)
),
---------------------------------------------------------------------------------------------------------------------------------------
-- dispatchOnFormResize() Dispatches formResize() Called when the user resizes the form
---------------------------------------------------------------------------------------------------------------------------------------
fn dispatchOnFormResize s e =
(
RSL_CollisionOps.formResize s e
),
---------------------------------------------------------------------------------------------------------------------------------------
-- dispatchOnFormResize() Does nothing, only used for debug
---------------------------------------------------------------------------------------------------------------------------------------
fn formResize s e =
(
-- format "w:% h:%\n" s.size.width s.size.height
),
---------------------------------------------------------------------------------------------------------------------------------------
-- createForm() Creates the main form
---------------------------------------------------------------------------------------------------------------------------------------
fn createForm =
(
clearListener()
-- if all the files we need are available, we can create the main form
if (doesFileExist attribute.rsNorthAttFile) and (doesFileExist attribute.materialsDatFile) and (doesFileExist attribute.proceduralDatFile) then
(
attribute.readAttFile()
attribute.readMaterialsDatFile()
attribute.readPoceduralDatFile()
attribute.filterTemp()
attribute.generateTextures()
--In this case, we're going to use a standard form rather than the custom maxForm written for max. this is because I want to change the
-- form back colour which you can't do if you use the custom maxForm. For this to work, we need to set the parent of the form to be Max.
-- USING THIS TYPE OF FORM MEANS WE NEED TO DISSABLE ACCELERATORS WHEN WE FOCUS ON A CONTROL THAT TAKES
-- KEY PRESSES SUCH AS SPINNERS AND TEXT BOXES
--Get the max handle pointer.
local maxHandlePointer=(Windows.GetMAXHWND())
--Convert the HWND handle of Max to a dotNet system pointer
local sysPointer = DotNetObject "System.IntPtr" maxHandlePointer
--Create a dotNet wrapper containing the maxHWND
local maxHwnd = DotNetObject "MaxCustomControls.Win32HandleWrapper" sysPointer
CollisionForm = RS_dotNetUI.form "CollisionForm" text:" R* Collision Operations V2.1" size:[320, 82] max:[2048, 82] min:[250, 82] borderStyle:RS_dotNetPreset.FB_SizableToolWindow
CollisionForm.StartPosition = (dotNetClass "System.Windows.Forms.FormStartPosition").Manual
CollisionForm.Location = dotNetObject "System.Drawing.Point" 95 125
dotnet.addEventHandler CollisionForm "FormClosing" dispatchOnFormClosing
dotnet.addEventHandler CollisionForm "Enter" disableAccelerators
dotnet.addEventHandler CollisionForm "Resize" dispatchOnFormResize
helpButton = RS_dotNetUI.Button "helpButton" text:(toUpper "Help") dockStyle:RS_dotNetPreset.DS_Fill
dotnet.addEventHandler helpButton "click" dispatchHelpButtonPressed
createCheckButton = RS_dotNetUI.checkBox "createCheckButton" text:(toUpper "Create") appearance:RS_dotNetPreset.AC_Button dockStyle:RS_dotNetPreset.DS_Fill
dotnet.addEventHandler createCheckButton "click" dispatchModeCheck
editCheckButton = RS_dotNetUI.checkBox "editCheckButton" text:(toUpper "Edit") appearance:RS_dotNetPreset.AC_Button dockStyle:RS_dotNetPreset.DS_Fill
dotnet.addEventHandler editCheckButton "click" dispatchModeCheck
displayCheckButton = RS_dotNetUI.checkBox "displayCheckButton" text:(toUpper "Select/Display") appearance:RS_dotNetPreset.AC_Button dockStyle:RS_dotNetPreset.DS_Fill
dotnet.addEventHandler displayCheckButton "click" dispatchModeCheck
mainTableLayout = RS_dotNetUI.tableLayout "mainTableLayout" text:"mainTableLayout" collumns:#((dataPair type:"Percent" value:50),(dataPair type:"Percent" value:50),(dataPair type:"Percent" value:50)) \
rows:#((dataPair type:"Absolute" value:24),(dataPair type:"Absolute" value:24),(dataPair type:"Percent" value:50)) dockStyle:RS_dotNetPreset.DS_Fill
mainTableLayout.controls.add helpButton 0 0
mainTableLayout.controls.add createCheckButton 0 1
mainTableLayout.controls.add editCheckButton 1 1
mainTableLayout.controls.add displayCheckButton 2 1
mainTableLayout.SetColumnSpan helpButton 3
CollisionForm.controls.add mainTableLayout
CollisionForm.Show (maxHwnd)
)
else
(
-- check to see which file is missing and display a messageBox
if not (doesFileExist attribute.rsNorthAttFile) then
(
messageBox ("rsnorth.att file missing. Unable to continue.\n" + attribute.rsNorthAttFile) title:"Error..."
)
else
(
if not (doesFileExist attribute.materialsDatFile) then
(
messageBox ("materials.dat file missing. Unable to continue.\n" + attribute.materialsDatFile) title:"Error..."
)
else
(
messageBox ("procedural.dat file missing. Unable to continue.\n" + attribute.proceduralDatFile) title:"Error..."
)
)
)
)
)
-- if the global struct instance is not undefined the form is probably open so we close it
if RSL_CollisionOps != undefined then
(
try
(
RSL_CollisionOps.CollisionForm.close()
)
catch()
)
RSL_CollisionOps = RSL_CollisionOps2()
RSL_CollisionOps.createForm()