Files
gtav-src/tools_ng/dcc/current/max2012/scripts/pipeline/util/placement.ms
T
2025-09-29 00:52:08 +02:00

685 lines
22 KiB
Plaintext
Executable File

--
-- File:: pipeline/util/placement.ms
-- Description:: Placement Toolkit
--
-- Author:: Marissa Warner-Wu <marissa.warner-wu@rockstarnorth.com>
-- Date:: 15 March 2010
--
-----------------------------------------------------------------------------
-- HISTORY
--
-- by David Muir <david.muir@rockstarnorth.com>
-- Authored some scripts used here
--
-----------------------------------------------------------------------------
-----------------------------------------------------------------------------
-- Uses
-----------------------------------------------------------------------------
filein "pipeline/helpers/maps/EPlanter.ms"
filein "pipeline/util/MeshUtil.ms"
filein "rockstar/helpers/pathnodecheck.ms"
filein "rockstar/util/datimporter.ms"
filein "pipeline/helpers/climbing/handhold_importer.ms"
-----------------------------------------------------------------------------
-- Rollouts
-----------------------------------------------------------------------------
--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
--------------------------------- PLACEMENT
--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
rollout PlaceHelpersRoll "Placement Helpers"
(
local fSectorSize = 50.0
local fWidthInSectors = 120.0
local fDepthInSectors = 120.0
-- FILTERS
fn objFilter obj = isProperty obj "mesh"
--////////////////////////////////////////////////////////////
-- interface
--////////////////////////////////////////////////////////////
hyperlink lnkHelp "Help?" address:"https://devstar.rockstargames.com/wiki/index.php/Placement_Toolkit#Placement_Helpers" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255)
local btnWidth = 110
local btnHeight = 24
local grpWidth = btnWidth + 12
group "Decal Surface Snap"
(
spinner spnDecalPush "Push:" tooltip:"Distance(m) for mesh to be pushed away from the surface beneath once snapped." range:[0.0, 5.0, 0.0] type:#float align:#left across:2 offset:[15,0]
button btnDecalSurfaceSnap "Snap Selected" tooltip:"Snap the decal verts to the mesh selected underneath" width:120 offset:[0, -3]
)
group "Conform To Ground"
(
spinner spnPush "Push:" tooltip:"Distance(m) for mesh to be pushed away from the surface beneath once conformed." range:[0.0, 5.0, 0.0] type:#float align:#left across:2 offset:[15,0]
button btnConformToGround "Conform Selected" tooltip:"Conform the selected meshes to whatever mesh are below them." width:120 offset:[0, -3]
)
group "Move to Ground"
(
--groupBox grpAlignZ "Move to Ground" pos:[(PlaceHelpersRoll.width / 2) - 4 - grpWidth,40] width:grpWidth height:73
pickbutton pbn_ground "Pick Ground" width:120 offset:[0, 0] across:2 filter:objFilter autoDisplay:true
button btnAlignZ "Move Selection" tooltip:"Move selected objects up/down until they sit on top of this picked object" width:130
checkbox chkGndAlign "Align to Ground" tooltip:"Align selection to ground-object" width:130 offset:[10,2]
spinner spnPercentOfGroundMin "Min %:" tooltip:"" range:[0, 100, 0] type:#integer align:#right offset:[0,2] width:130 across:2 enabled:false
spinner spnPercentOfGroundMax "Max %:" tooltip:"" range:[0, 100, 100] type:#integer align:#right offset:[0,2] width:130 enabled:false
)
group "Instancer"
(
--groupBox grpInstancer "Instancer" pos:[(PlaceHelpersRoll.width / 2) + 4,40] width:grpWidth height:73
pickbutton btnChooseInst "Choose Master Obj" autoDisplay:true tooltip:"Choose master-object for instancing" across:2 width:120
button btnChangeModels "Replace Selected" tooltip:"Replace selected objects with instances of above object" width:120
)
group "Random rotate in Z"
(
--groupBox grpRandRotZ "Random rotate in Z" pos:(grpAlignZ.pos + [0, grpAlignZ.height + 2]) width:grpWidth height:68
button btnRandRotZ "Rotate selected" tooltip:"Rotated selected objects randomly around Z axis" across:2 width:120
radioButtons radAxisChoice "" labels:#("Local", "World") align:#left tooltip:"Rotate in Local or World Z-axis" width:120 offset:[20,2]
)
group "Random Scale"
(
--groupBox grpRandScale "Random Scale" pos:(grpInstancer.pos + [0, grpInstancer.height + 2]) width:grpWidth height:68
button btnRandScale "Scale selected" \
tooltip:"Scales (non-dynamic) selected objects randomly within the jitter value\nDynamic objects will be de-selected" \
across:2 width:120
spinner spnScaleJitter "Jitter %:" range:[0,100,10] type:#integer align:#left width:120 offset:[10,0]
)
group "World Sector Calculator"
(
--groupBox grpWorld "World Sector Calculator" offset:[4,8] width:252 height:92
--label lblWorld "World Centre:" width:77 height:16
spinner spnWorldX "World Centre: X: " across:2 offset:[40, 0]
spinner spnWorldY "Y: " range:[0,100,0]
--label lblSector "Sector:" width:77 height:16
spinner spnSecX "Sector: X: " range:[0,100,0] across:2 offset:[40, 0]
spinner spnSecY "Y: " range:[0,100,0]
)
--////////////////////////////////////////////////////////////
-- methods
--////////////////////////////////////////////////////////////
--------------------------------------------------------------
-- Move to Ground
--------------------------------------------------------------
fn pickFilter obj =
(
(isProperty obj "mesh")
)
--------------------------------------------------------------
-- World-Sector Calculator
--------------------------------------------------------------
fn Refresh worldUpdate = (
if ( worldUpdate ) then
(
-- World coordinates updated, reflect change in sector coords
spnSecX.value = floor( ( ( spnWorldX.value as float ) / fSectorSize ) + ( fWidthInSectors / 2.0 ) )
spnSecY.value = floor( ( ( spnWorldY.value as float ) / fSectorSize ) + ( fDepthInSectors / 2.0 ) )
)
else
(
-- Sector coordinates updated, reflect change in world coords
spnWorldX.value = ( ( ( spnSecX.value as float ) - fWidthInSectors / 2.0 ) * fSectorSize ) + fSectorSize / 2.0
spnWorldY.value = ( ( ( spnSecY.value as float ) - fDepthInSectors / 2.0 ) * fSectorSize ) + fSectorSize / 2.0
)
)
--////////////////////////////////////////////////////////////
-- events
--////////////////////////////////////////////////////////////
on chkGndAlign changed arg do
(
spnPercentOfGroundMin.enabled = spnPercentOfGroundMax.enabled = arg
)
-------------------------------------------------------------
-- Decal Surface Snap
-------------------------------------------------------------
on btnDecalSurfaceSnap pressed do
(
--check selection
if selection.count == 0 then
(
messageBox "Nothing Selected\nPick surface then decal before running" title:"Bad Selection"
return false
)
if selection.count == 1 then
(
messageBox "Select the surface and decal before running" title:"Bad Selection"
return false
)
local surfaceMesh = convertToMesh selection[1]
local decalMesh = convertToMesh selection[2] --needs to be editable mesh
--setup RayMeshGridIntersect
local rayMesh = RayMeshGridIntersect()
rayMesh.Initialize 10
rayMesh.addNode surfaceMesh
raymesh.BuildGrid()
--raycast from target decal normal into the the surfaceMesh
for vtx=1 to (getNumVerts decalMesh) do
(
--the Position
local position = in coordsys #world (meshop.getVert decalMesh vtx node:decalMesh)
--get the normal
local normal = normalize(getnormal decalMesh vtx)
--normal = [0,0,1]
--cast a ray
local hits = raymesh.intersectRay position -normal true
--if we get a hit then....
if hits > 0 then
(
--get the face hit
local theIndex = rayMesh.getClosestHit() --get the index of the closest hit by the ray
local theFace = rayMesh.getHitFace theIndex
--get the barycentric coords of the face hit
--local baryPt = rayMesh.getHitBary theIndex
--get the verts for the face hit
local faceVerts = (meshop.getVertsUsingFace surfaceMesh theFace) as Array
--get the closestvert, the smallest value in baryPt should give the index of the closest vert
local minTest = 9999999
--local baryArray = #(baryPt.x, baryPt.y, baryPt.z) --shifted order otherwise it goes further than it should
local vIdx = 0
local closestVertPos = [0, 0, 0]
for pt=1 to 3 do
(
local thisVertPos = meshop.getvert surfaceMesh faceVerts[pt]
local dist = distance thisVertPos position
if dist < minTest then
(
closestVertPos = thisVertPos
minTest = dist
)
)
--move the decal vert to closesVert Pos + push
local finalPos = closestVertPos + (spnDecalPush.value * normal)
in coordsys #world meshop.setVert decalMesh #{vtx} finalPos node:decalMesh
)
)
)
-------------------------------------------------------------
-- Conform To Ground
-------------------------------------------------------------
on btnConformToGround pressed do
(
--check anythign selected
if $selection.count == 0 then
(
messageBox "Nothing Selected!" title:"Error"
return false
)
--get the user selection
local meshes = for obj in $selection where classOf obj == Editable_Poly or classOf obj == Editable_mesh collect obj
if meshes.count == 0 then
(
messageBox "No Meshes Selected!" title:"Error"
return false
)
coordsys #world
--iterate through those we got
for item in meshes do
(
undo "drop to ground" on
(
local geoClass = classOf item
local numVerts = getNumVerts item.mesh
local theVerts = for v=1 to (getNumVerts item.mesh) collect v
if subObjectLevel != 0 then theVerts = for v=1 to item.selectedVerts.count collect item.selectedVerts[v].index
local progMsg = "Calculating positions for: " + item.name
progressStart progMsg
progressUpdate 1
--for each vert get its position
for v in theVerts do
(
if getProgressCancel() then
(
progressEnd()
max undo
return false
)
local vertPos = meshop.getVert item.mesh v node:item
local hitPosZ = vertPos
-- fire a ray down from that position
local hits = intersectRayScene (ray vertPos [0, 0, -1])
--take the hit objects that are not our dropee
local hitObj = for o in hits where o[1] != item collect o[2] --collect rays
--set the new position if we have one
if hitObj.count != 0 then
(
if hitObj.count > 1 then --find the closest
(
local nearestDist = 9999999.0
local nearestObj = hitObj[1]
for hit in hitObj where abs(vertPos.z - hit.pos.z) < nearestDist do
(
nearestDist = abs(vertPos.z - hit.pos.z)
nearestObj = hit
)
hitPosZ = nearestObj.pos.z
)
else
(
hitPosZ = hitObj[1].pos.z
)
--add any push value the user set in the UI
vertPos.z = hitPosZ + spnPush.value
--set the vert position to be the new position
case geoClass of
(
Editable_Poly:
(
polyop.setVert item v vertPos
)
Editable_Mesh:
(
setVert item v vertPos
)
)--end case
)-- end hit
progressUpdate (100.0 * (v / numVerts as Float))
--progressUpdate v
)--end vert
progressEnd()
)
CenterPivot item
)--end item
CompleteRedraw()
)
--------------------------------------------------------------
-- Move to Ground
--------------------------------------------------------------
on btnAlignZ pressed do
(
undo "move to ground" on
(
local selObjs = for obj in selection where (isProperty obj "mesh") collect obj
if (selObjs.count == 0) do
(
messagebox "Please select at least one object with a mesh."
return false
)
if (pbn_ground.object == undefined) do
(
messagebox "Please set a ground object first."
return false
)
if (chkGndAlign.checked) then RSmoveToGround selObjs pbn_ground.object spnPercentOfGroundMin.value spnPercentOfGroundMax.value
else RSmoveToGround selObjs pbn_ground.object 0 0
)
)
--------------------------------------------------------------
-- Random Rotate
--------------------------------------------------------------
on btnRandRotZ pressed do
(
undo "random rotate" on
(
for obj in selection do
(
local rndRot = eulerangles 0 0 (random -180.0 180.0)
case radAxisChoice.state of
(
1:(in coordSys local rotate obj rndRot)
2:(in coordSys world rotate obj rndRot)
)
)
)
)
--------------------------------------------------------------
-- Random Scale
--------------------------------------------------------------
on btnRandScale pressed do
(
undo "random scale" on
(
local nonDynSel = for obj in selection where
(
case of
(
(isRsRef obj):((obj.refDef != undefined) and (not obj.refDef.isDynamic))
(isInternalRef obj):(not RsMapObjectIsDynamic (getIRefSource obj))
default:(not RsMapObjectIsDynamic obj)
)
)
collect obj
-- Deselect non-dynamic objects:
if nonDynSel.count != selection.count do
(
clearSelection()
select nonDynSel
)
for obj in nonDynSel do
(
local jitterAmt = spnScaleJitter.value / 100.0
local rndScale = random (1 - jitterAmt) (1 + jitterAmt)
scale obj [rndScale, rndScale, rndScale]
)
)
)
--------------------------------------------------------------
-- Instancer
--------------------------------------------------------------
on btnChooseInst picked obj do
(
global RSinstMasterobj = obj
)
on btnChangeModels pressed do
(
if (RSinstMasterobj == undefined) then
(
messagebox "Please select an object to instance."
)
else
(
local models = selection as array
instanceMgr.MakeObjectsUnique models #group
local mastermat=RSinstMasterobj.material
copyAttrs RSinstMasterobj
for model in models do
(
model.objectoffsetpos = [0,0,0]
model.objectoffsetrot = (quat 0 0 0 1)
instancereplace model RSinstMasterobj
model.material=mastermat
pasteAttrs model
)
if isRSrefSuperClass RSinstMasterobj do
(
RsRefFuncs.clearObjRememberNames models
RSrefFuncs.setObjNames models
)
)
)
--------------------------------------------------------------
-- World-Sector Calculator
--------------------------------------------------------------
on spnWorldX changed val do
(
Refresh true
)
on spnWorldY changed val do
(
Refresh true
)
on spnSecX changed val do
(
Refresh false
)
on spnSecY changed val do
(
Refresh false
)
on GtaWorldSectorRoll open do
(
Refresh true
)
)
--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
--------------------------------- CHECKS
--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
rollout ChecksRoll "Checks"
(
--////////////////////////////////////////////////////////////
-- interface
--////////////////////////////////////////////////////////////
hyperlink lnkHelp "Help?" address:"https://devstar.rockstargames.com/wiki/index.php/Placement_Toolkit#Checks" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255)
group "Path Node Checker"
(
spinner spnDeviation "Deviation " width:97 align:#right
button btnFixPos "Fix Positions" width:130 across:2 offset:[-2,0]
button btnCheck "Check Link Deviation"width:130 offset:[2,0]
)
group "Bad Object Position Finder"
(
button btnFind "Find" width:130 across:2 offset:[-2,0]
button btnSelect "Select" width:130 offset:[2,0]
)
progressBar barLoad "ProgressBar" width:280 offset:[-10,0] height:10 color:red
--////////////////////////////////////////////////////////////
-- methods
--////////////////////////////////////////////////////////////
--------------------------------------------------------------
-- Path Node Checker
--------------------------------------------------------------
fn RecGetFixNodes rootobj setlist = (
for i = 1 to rootobj.children.count do (
obj = rootobj.children[i]
if obj.ishidden == false then (
if classof obj == Col_Mesh then (
append RsCollisionList obj
) else if classof obj == VehicleNode then (
append setlist obj
)
)
RecGetFixNodes obj setlist
)
)
fn RecGetCheckNodes rootobj setlist = (
for i = 1 to rootobj.children.count do (
obj = rootobj.children[i]
if obj.ishidden == false then (
if classof obj == Col_Mesh then (
append RsCollisionList obj
) else if classof obj == VehicleLink then (
append setlist obj
)
)
RecGetCheckNodes obj setlist
)
)
--////////////////////////////////////////////////////////////
-- events
--////////////////////////////////////////////////////////////
--------------------------------------------------------------
-- Path Node Checker
--------------------------------------------------------------
on btnFixPos pressed do (
local RsPathNodeList = #()
RecGetFixNodes rootnode RsPathNodeList
for i = 1 to RsPathNodeList.count do (
obj = RsPathNodeList[i]
barLoad.value = 100.0 * ((i as float)/ RsPathNodeList.count)
checkNodePathAgainstCollision obj
)
)
on btnCheck pressed do (
local RsPathList = #()
RecGetCheckNodes rootnode RsPathList
RsErrorList = #()
for i = 1 to RsPathList.count do (
obj = RsPathList[i]
barLoad.value = 100.0 * ((i as float)/ RsPathList.count)
if checkLinkAgainstCollision obj spnDeviation.value then (
append RsErrorList obj.name
)
)
rollout RsLinkErrors "Errors"
(
listbox lstErrors "Links:" items:RsErrorList height:10
button btnOK "OK"
on lstErrors selected item do (
foundobj = getnodebyname RsErrorList[item] exact:true
if (foundobj != undefined) then (
if isdeleted foundobj == false then (
select foundobj
max zoomext sel
)
)
)
on btnOK pressed do (
DestroyDialog RsLinkErrors
)
)
CreateDialog RsLinkErrors width:300 modal:false
)
--------------------------------------------------------------
-- Bad Object Position Finder
--------------------------------------------------------------
on btnFind pressed do
(
format "Finding bad objects...\n"
barLoad.value = 0
i = 0
cnt = 0
for obj in $objects do
(
i += 1
barLoad.value = 100.* i / $objects.count
if ( bit.isFinite( obj.pos.x ) and bit.isFinite( obj.pos.y ) and
bit.isFinite( obj.pos.z ) ) then
continue
cnt += 1
format "Invalid object position: %s %s\n" obj.name obj.pos
)
format "% objects found.\n" cnt
)
on btnSelect pressed do
(
clearSelection()
barLoad.value = 0
i = 0
cnt = 0
for obj in $objects do
(
i += 1
barLoad.value = 100.* i / $objects.count
if ( bit.isFinite( obj.pos.x ) and bit.isFinite( obj.pos.y ) and
bit.isFinite( obj.pos.z ) ) then
continue
cnt += 1
selectMore obj
)
format "% objects selected.\n" cnt
)
)
--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
--------------------------------- FILE IMPORTER
--///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
rollout FileImportRoll "File Importer"
(
--////////////////////////////////////////////////////////////
-- interface
--////////////////////////////////////////////////////////////
hyperlink lnkHelp "Help?" address:"https://devstar.rockstargames.com/wiki/index.php/Placement_Toolkit#File_Importer" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255)
group "DAT Importer"
(
button btnImport "Import" width:130 across:2 offset:[-2,0]
button btnExport "Export" width:130 offset:[2,0]
)
group "Handhold Importer"
(
button btnLoadHandHoldXml "Load File" width:260
)
--////////////////////////////////////////////////////////////
-- events
--////////////////////////////////////////////////////////////
--------------------------------------------------------------
-- DAT Importer
--------------------------------------------------------------
on btnImport pressed do (
file = getopenfilename caption:"dat file to import" filename:"x:\\gta\\build\\common\\data\\paths\\" types:"DAT (*.dat)|*.dat|"
if file != undefined then BuildLine file
)
on btnExport pressed do (
file = getsavefilename caption:"dat file to export" filename:"x:\\gta\\build\\common\\data\\paths\\" types:"DAT (*.dat)|*.dat|"
if file != undefined then ExportSpline file
)
--------------------------------------------------------------
-- Handhold Importer
--------------------------------------------------------------
on btnLoadHandHoldXml pressed do (
LoadHandholdFile (getOpenFilename caption:"Handhold File" types:"xml file (*.xml)|*.xml")
)
)
try CloseRolloutFloater PlacementToolkit catch()
global PlacementToolkit = newRolloutFloater "Placement Toolkit" 300 750 50 96
addRollout Eplanter_roll PlacementToolkit
addRollout PlaceHelpersRoll PlacementToolkit
addRollout ChecksRoll PlacementToolkit
addRollout FileImportRoll PlacementToolkit