--------------------------------------------------------------------------------------------------------------------------------------- -- 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()