1590 lines
51 KiB
Plaintext
Executable File
1590 lines
51 KiB
Plaintext
Executable File
--
|
|
-- Description:: Rockstar Collision Set
|
|
-- Utility for setting collision attributes by the collision type
|
|
-- Rockstar North
|
|
-- 13/10/2006
|
|
-- by Greg Smith
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Uses
|
|
-----------------------------------------------------------------------------
|
|
filein "rockstar/export/settings.ms"
|
|
fileIn (::RsConfigGetWildWestDir() + "script/3dsMax/General_tools/Collision/ProcPainter_Funcs.ms")
|
|
fileIn (::RsConfigGetWildWestDir() + "script/3dsMax/General_tools/Collision/Collision_FromTexture.ms")
|
|
fileIn (::RsConfigGetWildWestDir() + "script/3dsMax/General_tools/Collision/NavPaths_LoadFuncs.ms")
|
|
RSTA_LoadCommonFunction #("fn_RSTA_vertexColours.ms")
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Rollout
|
|
-----------------------------------------------------------------------------
|
|
try (destroyDialog RsCollisionSetRoll) catch ()
|
|
try (destroyDialog collSet_showBmpRoll) catch ()
|
|
|
|
-- Predefine rollout-name:
|
|
global collSet_showBmpRoll
|
|
|
|
-- Set default vert/face-count limits, if they're not available from labelled tool-scripts yet:
|
|
if (RsMaxCollVerts == undefined) do
|
|
(
|
|
global RsMaxCollVerts = 32768
|
|
global RsMaxCollFaces = 100000
|
|
)
|
|
|
|
rollout RsCollisionSetRoll "Collision Setter" width:430 height:570
|
|
(
|
|
dotNetControl RsBannerPanel "Panel" pos:[0,0] height:32 width:RsCollisionSetRoll.width
|
|
local bannerStruct = makeRsBanner dn_Panel:RsBannerPanel wiki:"Collision_Set" versionNum:1.43 versionName:"Wary Ham"
|
|
|
|
local progressContinue = true
|
|
|
|
-- We will question user's intentions to regenerate collision on a late-stage project:
|
|
local MatureProjWarn = (matchPattern (RsConfigGetProjectName()) pattern:"gta5*")
|
|
|
|
local collCatItems = #()
|
|
local selectedSurface
|
|
|
|
-- Texmap-descriptors for current name-list:
|
|
local TexInfos = #()
|
|
local ShownTexInfos = #()
|
|
|
|
label lblTexture "" across:3 align:#left
|
|
label lblSurface "Select Surface Type:" offset:[88,0]
|
|
|
|
listbox LstTextures pos:[10, lblTexture.pos.Y + 18] width:235 height:16 align:#center
|
|
dropDownList lstCategories "" pos:(LstTextures.pos + [240,0]) width:170
|
|
listbox lstSurfaces pos:(lstCategories.pos + [0, 26]) width:lstCategories.width height:14 align:#center
|
|
label lblSetUnset "List Textures:" across:4 align:#Left
|
|
checkbox chkSetTexs "Set" align:#Left offset:[-30,-1] checked:True
|
|
checkbox chkUnsetTexs "Unset" align:#Left offset:[-90,-1] checked:True
|
|
label lblStatus "" width:218 align:#Right
|
|
|
|
checkButton btnEditLock "[ ALLOW EDITS ]" width:220 align:#right -- Editing is turned off by default
|
|
|
|
button BtnReassignCollMats "Reassign Collision-Mats For Selected" width:220 align:#right offset:[0,-4] \
|
|
Tooltip:"Reassign materials for selected object's collision-meshes, based on object's texturemaps"
|
|
|
|
button BtnCreateCollObjs "Create Collision For Selected" width:220 align:#right offset:[0,-2] \
|
|
Tooltip:"Generate collision-mesh for selected objects, with collision-materials based on object's texturemaps"
|
|
checkButton BtnCreateForFaces "Just for Sel. Faces" width:110 align:#left offset:[BtnCreateCollObjs.Pos.X - 13,-5] across:2 \
|
|
Tooltip:"On Create Collision, just generate new collision for selected faces"
|
|
checkButton BtnAddDeepness "Push DEEP Faces" width:110 align:#right offset:[0,-5] Checked:False \
|
|
Tooltip:"On Create Collision, push down verts for collision-faces that have \"*DEEP*\" surfacetypes"
|
|
|
|
checkButton btnToggleGlobal "Toggle Global" width:220 enabled:true align:#right offset:[0,-2] \
|
|
Tooltip:"Toggles whether or not selected surface-assignment is stored to global.xml"
|
|
button btnDeleteDef "Remove this definition" width:220 enabled:true align:#right offset:[0,-4] \
|
|
Tooltip:"Remove selected surface-assignment from shown xml-file"
|
|
button btnGetFolder "Get Tex List From Folder" width:220 align:#right offset:[0,-4] \
|
|
Tooltip:"Show list of textures from a given folder.\nThis allows you to assign surfacetypes to textures that aren't on selected objects."
|
|
button btnSnapPivot "Snap Pivots of Selected to Parent" width:220 align:#right offset:[0,-4]
|
|
|
|
imgTag bmpUi "" width:178 height:178 tooltip:"Double-click to show full-size texture" pos:[13, btnEditLock.pos.y]
|
|
|
|
groupBox grpFlags "RexBounds Values:" width:400 height:144
|
|
|
|
checkbox chkStairs "Stairs" pos:(grpFlags.pos + [10, 20]) tooltip:"Stairs"
|
|
checkbox chkClimable "Non Climable" pos:(grpFlags.pos + [140, 20]) tooltip:"Non Climable"
|
|
checkbox chkSeeThrough "See Through" pos:(grpFlags.pos + [230, 20]) tooltip:"See Through"
|
|
checkbox chkShootThrough "Shoot Through" pos:(grpFlags.pos + [10, 40]) tooltip:"Shoot Through"
|
|
checkbox chkShootThroughFX "Shoot Through FX" pos:(grpFlags.pos + [10, 40]) tooltip:"Shoot Through FX"
|
|
checkbox chkPath "Path" pos:(grpFlags.pos + [140, 40]) tooltip:"Path"
|
|
checkbox chkSetPedPop "Set Ped Density" pos:(grpFlags.pos + [230, 40]) tooltip:"Ped population"
|
|
spinner spnPedDensity range:[0,7,0] pos:(chkSetPedPop.pos + [105, 0]) width:50 type:#integer
|
|
checkbox chkCamera "Non Camera Collidable" pos:(grpFlags.pos + [10, 60]) tooltip:"Non Camera Collide"
|
|
checkbox chkCover "Doesn't Provide Cover" pos:(grpFlags.pos + [230, 60]) tooltip:"Doesn't Provide Cover"
|
|
checkbox chkProcCullImmune "Proc Cull Immune" pos:(grpFlags.pos + [10, 80]) tooltip:"Faces are ignored by procedural-cull"
|
|
checkbox chkRoadRoad "Road - Road" pos:(grpFlags.pos + [10, 100]) tooltip:"Road - Road"
|
|
checkbox chkRoadPath "Road - Path" pos:(grpFlags.pos + [140, 100]) tooltip:"Road - Path"
|
|
checkbox chkRoadTrail "Road - Trail" pos:(grpFlags.pos + [230, 100]) tooltip:"Road - Trail"
|
|
checkbox chkNotHorseWalkable "Not Horse Walkable" pos:(grpFlags.pos + [10, 120]) tooltip:"Not Horse Walkable"
|
|
|
|
groupBox grpPreserve "Preserve UV-channels on 'Reassign Collision':" pos:(grpFlags.pos + [0, grpFlags.height + 2]) width:grpFlags.width height:42
|
|
checkbox chkPreserve11 "11: Proc Tint" checked:false pos:(grpPreserve.pos + [10, 17]) enabled:grpPreserve.enabled \
|
|
tooltip:"Clear collision's Procedural Tint channel on reassign?\n\n(All other channels are cleared)"
|
|
checkbox chkPreserve12 "12: Proc Scaling/Density" checked:false pos:(chkPreserve11.pos + [84, 0]) enabled:grpPreserve.enabled \
|
|
tooltip:"Clear collision's Procedural Scaling/Density channel on reassign?\n\n(All other channels are cleared)"
|
|
local preserveCtrls = #(grpPreserve, chkPreserve11, chkPreserve12)
|
|
|
|
progressBar totalProgBar "" offset:[0,12] width:grpFlags.width
|
|
progressBar subProgBar "" offset:[0,-4] width:grpFlags.width
|
|
label lblProgStatus "" width:RsCollisionSetRoll.width align:#left offset:[0,-4]
|
|
|
|
-- These listed controls are only enabled if 'ALLOW EDITS' is active.
|
|
-- 'RexControls' also needs a non-blank 'Surface Type' to be selected (i.e. textures need a surfacetype before they're allowed flags)
|
|
local EditControls = #(lstCategories, lstSurfaces, btnToggleGlobal, btnDeleteDef)
|
|
local RexControls = #(chkStairs, chkClimable, chkSeeThrough, chkShootThrough, chkShootThroughFX, chkPath, chkCover, chkCamera, chkSetPedPop, spnPedDensity, chkProcCullImmune, chkRoadRoad, chkRoadPath, chkRoadTrail, chkNotHorseWalkable)
|
|
|
|
--////////////////////////////////////////////////////////////
|
|
-- methods
|
|
--////////////////////////////////////////////////////////////
|
|
|
|
-- Enables 'RexBounds Values' controls if 'ALLOW EDITS' is active, and a valid/non-blank Surface Type is selected:
|
|
fn SetRexCtrlsEnabled =
|
|
(
|
|
RexControls.Enabled = (BtnEditLock.Checked and (LstSurfaces.Selection != 0) and (LstSurfaces.Selected != RsBlankCollName))
|
|
)
|
|
|
|
-- Returns texmap-descriptor associated with selected texture-list item:
|
|
fn GetSelTexInfo =
|
|
(
|
|
local SelNum = LstTextures.Selection
|
|
if (SelNum == 0) then (return undefined) else (return ShownTexInfos[SelNum])
|
|
)
|
|
|
|
-- Build flags-integer from 'RexBounds Values' checkboxes:
|
|
fn GetFlagsUI =
|
|
(
|
|
local flagNames = #()
|
|
|
|
if chkStairs.checked do (append flagNames "Stairs")
|
|
if chkClimable.checked do (append flagNames "Non Climbable")
|
|
if chkSeeThrough.checked do (append flagNames "See Through")
|
|
if chkShootThrough.checked do (append flagNames "Shoot Through")
|
|
if chkShootThroughFX.checked do (append flagNames "Shoot Through FX")
|
|
if chkPath.checked do (append flagNames "Path")
|
|
if chkCover.checked do (append flagNames "Does Not Provide Cover")
|
|
if chkCamera.checked do (append flagNames "Non Camera Collidable")
|
|
if chkSetPedPop.checked do (append flagNames "Reserved for Tools")
|
|
if chkRoadRoad.checked do (append flagNames "Road - Road")
|
|
if chkRoadPath.checked do (append flagNames "Road - Path")
|
|
if chkRoadTrail.checked do (append flagNames "Road - Trail")
|
|
if chkNotHorseWalkable.checked do (append flagNames "Not Horse Walkable")
|
|
|
|
return (RsCollMatMakeFlagsInt flagNames)
|
|
)
|
|
|
|
-- Convert flags-integer to checkbox-settings:
|
|
fn SetFlagsUI flags =
|
|
(
|
|
chkStairs.checked = RsCollMatFlagged "Stairs" flags:flags
|
|
chkClimable.checked = RsCollMatFlagged "Non Climbable" flags:flags
|
|
chkSeeThrough.checked = RsCollMatFlagged "See Through" flags:flags
|
|
chkShootThrough.checked = RsCollMatFlagged "Shoot Through" flags:flags
|
|
chkShootThroughFX.checked = RsCollMatFlagged "Shoot Through FX" flags:flags
|
|
chkPath.checked = RsCollMatFlagged "Path" flags:flags
|
|
chkCover.checked = RsCollMatFlagged "Does Not Provide Cover" flags:flags
|
|
chkCamera.checked = RsCollMatFlagged "Non Camera Collidable" flags:flags
|
|
chkSetPedPop.checked = RsCollMatFlagged "Reserved for Tools" flags:flags
|
|
chkRoadRoad.checked = RsCollMatFlagged "Road - Road" flags:flags
|
|
chkRoadPath.checked = RsCollMatFlagged "Road - Path" flags:flags
|
|
chkRoadTrail.checked = RsCollMatFlagged "Road - Trail" flags:flags
|
|
chkNotHorseWalkable.checked = RsCollMatFlagged "Not Horse Walkable" flags:flags
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Set up the surfaces and surface-category lists
|
|
--------------------------------------------------------------
|
|
fn SetupList =
|
|
(
|
|
local collCatNames = #("ALL CATEGORIES")
|
|
collCatItems = #(#())
|
|
|
|
local SurfaceTypes = (RsCollMatDef.LoadCollisionEntries Filtered:True)
|
|
|
|
for item in SurfaceTypes do
|
|
(
|
|
local catNum = findItem collCatNames item.category
|
|
|
|
if (catNum == 0) do
|
|
(
|
|
append collCatNames item.category
|
|
append collCatItems #()
|
|
catNum = collCatNames.count
|
|
)
|
|
|
|
append collCatItems[1] item.name
|
|
append collCatItems[catNum] item.name
|
|
)
|
|
|
|
for collNames in collCatItems do
|
|
(
|
|
sort collNames
|
|
)
|
|
|
|
-- Add _BLANK to top of all-surfaces list:
|
|
insertitem RsBlankCollName collCatItems[1] 1
|
|
|
|
lstCategories.items = collCatNames
|
|
lstCategories.selection = 1
|
|
lstSurfaces.items = collCatItems[1]
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Set Surfaces list to show list for this category-num:
|
|
--------------------------------------------------------------
|
|
fn setListToCategory catNum =
|
|
(
|
|
print selectedSurface
|
|
|
|
lstCategories.selection = catNum
|
|
lstSurfaces.items = collCatItems[catNum]
|
|
|
|
lstSurfaces.selection = findItem lstSurfaces.items selectedSurface
|
|
SetRexCtrlsEnabled()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Updates the texture-preview thumbnail
|
|
--------------------------------------------------------------
|
|
fn UpdateTexture =
|
|
(
|
|
-- Create default bitmap:
|
|
local showBmp = bitmap bmpUi.width bmpUi.height color:(white * ((colorMan.getColor #3dLight) * [255,255,255]))
|
|
|
|
local SelTexInfo = GetSelTexInfo()
|
|
if (SelTexInfo != undefined) do
|
|
(
|
|
local selItem = SelTexInfo.TexMap
|
|
|
|
if (selItem != undefined) do
|
|
(
|
|
local bmpSet
|
|
|
|
if (isKindOf selItem bitmaptexture) then
|
|
(
|
|
bmpSet = selItem
|
|
)
|
|
else
|
|
(
|
|
bmpset = bitmaptexture()
|
|
bmpset.filename = selItem
|
|
bmpset.reload()
|
|
)
|
|
|
|
rendermap bmpset into:showBmp filter:true
|
|
)
|
|
)
|
|
|
|
bmpUi.bitmap = showBmp
|
|
)
|
|
|
|
fn setMatStatus =
|
|
(
|
|
local textureName = undefined
|
|
|
|
local SelTexInfo = GetSelTexInfo()
|
|
if (SelTexInfo != undefined) do
|
|
(
|
|
textureName = SelTexInfo.TexName
|
|
)
|
|
|
|
if (textureName == undefined) then
|
|
(
|
|
-- Get number of select-objects' containers:
|
|
local ContsCount = (MakeUniqueArray (for Obj in (GetCurrentSelection()) collect (RsGetObjContainer Obj))).Count
|
|
|
|
case of
|
|
(
|
|
(ContsCount == 0):(lblStatus.text = "(No objects selected)")
|
|
(ContsCount > 1):(lblStatus.text = "Warning: Multiple-container objs selected")
|
|
default:(lblStatus.text = "(No texture selected)")
|
|
)
|
|
)
|
|
else
|
|
(
|
|
local FindNum = (gRsTexToColl.GetFindTexNum TextureName)
|
|
|
|
if (FindNum == 0) do
|
|
(
|
|
lblStatus.text = "(No saved surface-type found)"
|
|
)
|
|
)
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Set material-flag controls
|
|
--------------------------------------------------------------
|
|
fn UpdateMaterial matClicked:false =
|
|
(
|
|
local SurfaceDef
|
|
local idxFind = 0
|
|
|
|
local SelTexInfo = GetSelTexInfo()
|
|
if (SelTexInfo != undefined) do
|
|
(
|
|
-- Find surface-definition for this texname:
|
|
SurfaceDef = gRsTexToColl.GetTexDataFromXML SelTexInfo.TexName MatClicked:MatClicked CollSetRoll:RsCollisionSetRoll
|
|
)
|
|
|
|
-- Set controls to match loaded settings (or set to defaults if no definition was found)
|
|
if (SurfaceDef != undefined) then
|
|
(
|
|
idxFind = finditem lstSurfaces.items SurfaceDef.Material
|
|
|
|
-- Change category to ALL and try searching again:
|
|
if (idxFind == 0) and (lstCategories.selection != 1) do
|
|
(
|
|
setListToCategory 1
|
|
idxFind = finditem lstSurfaces.items SurfaceDef.Material
|
|
)
|
|
|
|
spnPedDensity.value = SurfaceDef.PedPop
|
|
chkProcCullImmune.checked = SurfaceDef.ProcCullImmune
|
|
|
|
SetFlagsUI SurfaceDef.flags
|
|
)
|
|
else
|
|
(
|
|
SetFlagsUI 0
|
|
spnPedDensity.value = 0
|
|
chkProcCullImmune.checked = false
|
|
)
|
|
|
|
setMatStatus()
|
|
|
|
lstSurfaces.selection = idxFind
|
|
selectedSurface = lstSurfaces.selected
|
|
SetRexCtrlsEnabled()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Update texmap-names list-control
|
|
--------------------------------------------------------------
|
|
fn UpdateTexMapList ShowSetTexs:(chkSetTexs.Checked) ShowUnsetTexs:(chkUnsetTexs.Checked) =
|
|
(
|
|
SetWaitCursor()
|
|
local LblWas = LblStatus.Text
|
|
LblStatus.Text = "Processing texmap-names..."
|
|
|
|
-- Show all TexInfos by default
|
|
ShownTexInfos = TexInfos
|
|
|
|
-- Find out if texmaps have surfaces assigned for them yet:
|
|
if (not ShowSetTexs) or (not ShowUnsetTexs) do
|
|
(
|
|
-- Get surface-definition data for these textures:
|
|
local TexMapNames = for Item in TexInfos collect Item.TexName
|
|
local TexElems = gRsTexToColl.GetTexListDataFromXML TexMapNames CollSetRoll:RsCollisionSetRoll
|
|
|
|
-- Filter out texmaps with non-matching definition-state:
|
|
if ShowUnsetTexs then
|
|
(
|
|
ShownTexInfos = for n = 1 to TexElems.count where (TexElems[n] == undefined) collect TexInfos[n]
|
|
)
|
|
else
|
|
(
|
|
ShownTexInfos = for n = 1 to TexElems.count where (TexElems[n] != undefined) collect TexInfos[n]
|
|
)
|
|
)
|
|
|
|
local GlobalNames = undefined
|
|
|
|
local NewListItems = for TexInfo in ShownTexInfos collect
|
|
(
|
|
local DisplayName = (Copy TexInfo.TexName)
|
|
|
|
if (GlobalNames == undefined) do
|
|
(
|
|
GlobalNames = gRsTexToColl.GetGlobalTextureNames()
|
|
)
|
|
|
|
-- Add suffix to display-name if it's on the globals-list:
|
|
if (FindItem GlobalNames (ToLower DisplayName)) != 0 do
|
|
(
|
|
DisplayName += " (Global)"
|
|
)
|
|
|
|
-- Collect name to be shown in list:
|
|
DisplayName
|
|
)
|
|
|
|
-- Update list-items:
|
|
LstTextures.Items = NewListItems
|
|
LblStatus.Text = LblWas
|
|
|
|
SetArrowCursor()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Updates the Select Texture list:
|
|
--------------------------------------------------------------
|
|
fn UpdateSelectionSet Objs =
|
|
(
|
|
Free TexInfos
|
|
Free ShownTexInfos
|
|
|
|
try (destroyDialog collSet_showBmpRoll) catch ()
|
|
LstTextures.selection = 0
|
|
lstSurfaces.selection = 0
|
|
selectedSurface = undefined
|
|
|
|
-- Filter out invalid objects:
|
|
Objs = for Obj in Objs where (isEditPolyMesh Obj) collect Obj
|
|
|
|
-- Get number of selected-objects' containers:
|
|
local ContsCount = (MakeUniqueArray (for Obj in Objs collect (RsGetObjContainer Obj))).Count
|
|
|
|
-- Only get texturenames if selected objects are only in one container:
|
|
if (ContsCount == 1) do
|
|
(
|
|
-- Collect the texture-data from selected objects:
|
|
TexInfos = (gRsTexToColl.GetDiffTexInfosForObjs Objs)
|
|
|
|
-- Sort texture-descriptors alphabetically:
|
|
fn SortTexNames v1 v2 = (StriCmp v1.TexName v2.TexName)
|
|
qsort TexInfos SortTexNames
|
|
)
|
|
|
|
-- Update controls:
|
|
if RsCollisionSetRoll.open do
|
|
(
|
|
lblTexture.text = if (ContsCount == 1) then "Textures on selected objects:" else ""
|
|
|
|
UpdateTexMapList()
|
|
UpdateTexture()
|
|
UpdateMaterial()
|
|
)
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
--
|
|
--------------------------------------------------------------
|
|
fn UpdateSelectionSetFromFolder =
|
|
(
|
|
lblTexture.text = "Textures from selected folder:"
|
|
|
|
-- User chooses texture-folder to process:
|
|
local Rootpath = getsavepath initialDir:(RsConfigGetArtDir() + "/textures/")
|
|
|
|
-- Get all texmap-paths from folder:
|
|
if (Rootpath != undefined) do
|
|
(
|
|
SetWaitCursor()
|
|
PushPrompt "Collecting texmap paths..."
|
|
|
|
local WildCards = for Ext in gSupportedTextureTypes collect ("*" + Ext)
|
|
local TexPaths = (RsFindFilesRecursive Rootpath Wildcards)
|
|
|
|
local TexNames = #()
|
|
TexInfos = for TexPath in TexPaths collect
|
|
(
|
|
local TexName = (ToLower (GetFilenameFile TexPath))
|
|
|
|
-- Just collect one path per unique TexName:
|
|
if (AppendIfUnique TexNames TexName) then (RsTexMapItem TexName:TexName Filename:TexPath) else DontCollect
|
|
)
|
|
|
|
-- Sort texture-descriptors alphabetically:
|
|
fn SortTexNames v1 v2 = (StriCmp v1.TexName v2.TexName)
|
|
qsort TexInfos SortTexNames
|
|
|
|
PopPrompt()
|
|
SetArrowCursor()
|
|
)
|
|
|
|
UpdateTexMapList()
|
|
UpdateTexture()
|
|
UpdateMaterial()
|
|
)
|
|
|
|
-------------------------------------------------------------------------------------
|
|
-- Gets xml-elements for all selected objects, before assigning materials:
|
|
-- Warn
|
|
-------------------------------------------------------------------------------------
|
|
fn GetTexListDataFromXMLForObjs Objs =
|
|
(
|
|
local RetVal = True
|
|
|
|
-- Update TexInfos list:
|
|
UpdateSelectionSet objs
|
|
|
|
lblProgStatus.text = "Loading material data [Esc to cancel]"
|
|
|
|
-- Load surfacenames from xml for all objects' diffusemaps:
|
|
local TexMapNames = for Item in TexInfos collect Item.TexName
|
|
local SurfaceNames = gRsTexToColl.GetTexListDataFromXML TexMapNames CollSetRoll:RsCollisionSetRoll
|
|
|
|
lblProgStatus.text = ""
|
|
lblStatus.text = ""
|
|
|
|
-- Are any texmaps undefined?
|
|
local Unassigneds = 0
|
|
for Item in SurfaceNames where (Item == undefined) do
|
|
(
|
|
Unassigneds += 1
|
|
)
|
|
|
|
if (Unassigneds != 0) do
|
|
(
|
|
local Plural = if (Unassigneds == 1) then "Texmap" else ((Unassigneds as String) + " texmaps")
|
|
|
|
RetVal = (QueryBox (Plural + " found with no SurfaceName assigned to it!\n\nTool will use DEFAULT instead.\n\nContinue?") Title:"WARNING: Texmaps with undefined SurfaceNames")
|
|
|
|
-- If user says show unset texmaps:
|
|
if (not RetVal) and (not ((not chkSetTexs.Checked) and chkUnsetTexs.Checked)) do
|
|
(
|
|
chkSetTexs.Checked = False
|
|
chkUnsetTexs.Checked = True
|
|
RsCollisionSetRoll.DoListUpdate()
|
|
)
|
|
)
|
|
|
|
return RetVal
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Save changes to surface-settings:
|
|
--------------------------------------------------------------
|
|
fn SetMaterial =
|
|
(
|
|
local SelTexInfo = GetSelTexInfo()
|
|
if (SelTexInfo == undefined) do return False
|
|
|
|
local TexName = SelTexInfo.TexName
|
|
local flags = GetFlagsUI()
|
|
|
|
local texElem = (RsCollSurfaceDef name:TexName material:selectedSurface flags:flags procCullImmune:chkProcCullImmune.checked)
|
|
|
|
if chkSetPedPop.checked do
|
|
(
|
|
texElem.pedPop = spnPedDensity.value
|
|
)
|
|
|
|
gRsTexToColl.SetDataSettingXml TexElem CollSetRoll:RsCollisionSetRoll
|
|
)
|
|
|
|
--------------------------------------------------------------------
|
|
-- Get faces that are painted black/white on a given channel
|
|
--------------------------------------------------------------------
|
|
fn GetOverrideFaces obj chan blackFaces:#{} whiteFaces:#{} =
|
|
(
|
|
if not MeshOp.GetMapSupport obj chan do
|
|
return #{}
|
|
|
|
local faceCount = MeshOp.GetNumFaces obj
|
|
whiteFaces.count = faceCount
|
|
blackFaces.count = faceCount
|
|
|
|
local MeshGetMapFace = MeshOp.GetMapFace
|
|
local MeshGetMapVert = MeshOp.GetMapVert
|
|
|
|
for faceNum = 1 to faceCount do
|
|
(
|
|
local faceMapVerts = (MeshGetMapFace obj chan faceNum)
|
|
|
|
local overrideVal = 0.0
|
|
for vertIdx = 1 to 3 do
|
|
(
|
|
-- Get vertex-colour for tri-corner:
|
|
local vertNum = faceMapVerts[vertIdx]
|
|
local vertClr = MeshGetMapVert obj chan vertNum
|
|
|
|
-- Collect override-value
|
|
-- Defaults to 0.5 if non-greyscale or out-of-bounds
|
|
local vertVal = vertClr[1]
|
|
if (vertVal < 0.0) or (vertVal > 1.0) or (Distance vertClr [vertVal, vertVal, vertVal] > 0.0001) do
|
|
vertVal = 0.5
|
|
|
|
overrideVal += vertVal
|
|
)
|
|
|
|
case of
|
|
(
|
|
(overrideVal <= 0.5):(blackFaces[faceNum] = True)
|
|
(overrideVal >= 2.5):(whiteFaces[faceNum] = True)
|
|
)
|
|
)
|
|
|
|
return whiteFaces
|
|
)
|
|
|
|
----------------------------------------------------------------------------
|
|
-- Creates a new collision-mesh for obj, with correct materials set:
|
|
----------------------------------------------------------------------------
|
|
fn CreateObjectMat obj delExisting:true DoDeepness:True JustSelFaces:False =
|
|
(
|
|
lblProgStatus.text = "Creating collision: " + obj.name + " [Esc to cancel]"
|
|
|
|
local timestart = timestamp()
|
|
|
|
-- Delete object's existing collision-objects:
|
|
if delExisting do
|
|
(
|
|
local DelObjs = (for childObj in obj.children where (getattrclass childobj == "Gta Collision") collect childObj)
|
|
delete DelObjs
|
|
)
|
|
|
|
lblProgStatus.text = "Collecting face data: " + obj.name + " [Esc to cancel]"
|
|
local surfaceData = gRsTexToColl.GetObjFaceData Obj AsMesh:True JustSelFaces:JustSelFaces CollSetRoll:RsCollisionSetRoll
|
|
|
|
-- Abort if 'GetObjFaceData' was cancelled:
|
|
if (SurfaceData == False) do
|
|
return False
|
|
|
|
local Msg = ("Creating collision: " + obj.name)
|
|
lblProgStatus.text = (Msg + " [Esc to cancel]")
|
|
format "%\n" Msg
|
|
|
|
-- Convert the face-info lists to BitArrays
|
|
local matFaceLists = #()
|
|
for n = 1 to surfaceData.colSurfaceFaces.count do
|
|
(
|
|
local faceItems = surfaceData.colSurfaceFaces[n]
|
|
local faceNums = for item in faceItems collect item.num
|
|
Append matFaceLists (faceNums as Bitarray)
|
|
faceItems.count = 0
|
|
faceNums.count = 0
|
|
)
|
|
|
|
-- Create object that'll be turned in collisionmesh:
|
|
newobj = copy obj
|
|
convertToMesh newObj
|
|
meshops.unhideall newObj
|
|
setfaceselection newObj #{1..newObj.numFaces}
|
|
resetXForm newObj
|
|
collapseStack newObj
|
|
|
|
-- Get flag-bits
|
|
local collFlagNames = RexGetCollisionFlagNames()
|
|
local nonClimbableFlagIdx = FindItem collFlagNames "Non Climbable"
|
|
local tooSteepFlagIdx = FindItem collFlagNames "Too Steep"
|
|
|
|
-- Faces painted as black or white on channel 23 will be given Too Steep or Non Climbable flags
|
|
local steepChan = RsGetChanByName #SteepOverride
|
|
local steepFaces = #{}
|
|
local noClimbFaces = #{}
|
|
GetOverrideFaces newObj steepChan blackFaces:steepFaces whiteFaces:noClimbFaces
|
|
local hasSteepFaces = (steepFaces.numberSet + noClimbFaces.numberSet != 0)
|
|
|
|
-- Turn off all new object's mapping-channels:
|
|
MeshOp.SetNumMaps newObj 0
|
|
|
|
local removeFaces = #{1..newObj.numFaces}
|
|
|
|
local isRiverObj = False
|
|
local collisionEditor
|
|
|
|
-- Collect list of bound-materials and their faces
|
|
local newMatFaceItemsList = #()
|
|
|
|
struct sMatListItem (mat, faces, priority)
|
|
|
|
for surfaceNum = 1 to surfaceData.colSurfaceNames.count do
|
|
(
|
|
local surfaceName = surfaceData.colSurfaceNames[surfaceNum]
|
|
local collFlags = surfaceData.colSurfaceFlags[surfaceNum]
|
|
local cullImmune = surfaceData.colSurfaceProcCullImmune[surfaceNum]
|
|
local pedPop = surfaceData.colSurfacePops[surfaceNum]
|
|
|
|
local matFaces = matFaceLists[surfaceNum]
|
|
|
|
-- These faces will NOT be deleted from the final mesh
|
|
removeFaces -= matFaces
|
|
|
|
local newsubmat = RexBoundMtl name:surfaceName
|
|
RexSetCollisionFlags newsubmat collFlags
|
|
RexSetCollisionName newsubmat surfaceName
|
|
RexSetProcCullImmune newSubMat cullImmune
|
|
|
|
if (pedPop != -1) and (pedPop != undefined) do
|
|
RexSetPopDensity newsubmat pedPop
|
|
|
|
-- We're going to mark objects with water-surfaces with "river" flag:
|
|
if (not isRiverObj) and (surfaceName == "WATER") do
|
|
isRiverObj = True
|
|
|
|
-- Do we need to make a steep version of this material too?
|
|
if hasSteepFaces do
|
|
(
|
|
for overrideName in #(#Steep, #NoClimb) do
|
|
(
|
|
-- Get overlap of face-lists
|
|
local overrideFaces = if (overrideName == #Steep) then (matFaces * steepFaces) else (matFaces * noClimbFaces)
|
|
|
|
-- Make a new material for any matching faces
|
|
if (overrideFaces.numberSet != 0) do
|
|
(
|
|
-- Remove faces from original material's faces
|
|
matFaces -= overrideFaces
|
|
|
|
-- Add the steepness-flags to material's flags
|
|
-- (Both types have the non-climbable flag)
|
|
local newFlags = Bit.Set collFlags nonClimbableFlagIdx True
|
|
if (overrideName == #Steep) do
|
|
newFlags = Bit.Set newFlags tooSteepFlagIdx True
|
|
|
|
local overrideMat = Copy newSubMat
|
|
overrideMat.name = (surfaceName + "_" + (ToUpper overrideName))
|
|
RexSetCollisionFlags overrideMat newFlags
|
|
|
|
-- Collect material with extra flags
|
|
local sortPriority = if (overrideName == #Steep) then 2 else 3
|
|
Append newMatFaceItemsList (sMatListItem mat:overrideMat faces:overrideFaces priority:sortPriority)
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Collect this material (unless all its faces were Steep)
|
|
if (matFaces.numberSet != 0) do
|
|
Append newMatFaceItemsList (sMatListItem mat:newSubMat faces:matfaces priority:1)
|
|
)
|
|
|
|
-- Sort materials by name, and sort steep etc types together
|
|
fn SortMatItems v1 v2 =
|
|
(
|
|
local sortVal = (v1.priority - v2.priority)
|
|
if (sortVal == 0) do
|
|
sortVal = StriCmp v1.mat.name v2.mat.name
|
|
return sortVal
|
|
)
|
|
Qsort newMatFaceItemsList SortMatItems
|
|
|
|
-- Set material ids for faces
|
|
for matId = 1 to newMatFaceItemsList.count do
|
|
(
|
|
local matFaces = newMatFaceItemsList[matId].faces
|
|
for faceNum in matFaces do
|
|
SetfaceMatID newobj faceNum matId
|
|
)
|
|
|
|
if (removeFaces.numberSet == newobj.numfaces) do
|
|
(
|
|
format "Not creating collision for: % (no suitable faces)\n" obj.name
|
|
|
|
-- Don't create empty collision-meshes:
|
|
delete newobj
|
|
|
|
return True
|
|
)
|
|
|
|
NewObj.Name = UniqueName (Obj.Name + "_ColMesh")
|
|
NewObj.Parent = Obj
|
|
|
|
-- Remove non-collision faces:
|
|
if (removeFaces.numberSet != 0) do
|
|
Meshop.DeleteFaces newobj removeFaces
|
|
|
|
-- Delete isolated verts - these can play havok with the object-middle detection:
|
|
Meshop.DeleteIsoVerts NewObj
|
|
|
|
local matName = "CollSet:" + newobj.name + " - Multimaterial"
|
|
local newmat = Multimaterial name:matName
|
|
local subMatList = for item in newMatFaceItemsList collect item.mat
|
|
newmat.numsubs = submatlist.count
|
|
newmat.materialList = submatlist
|
|
newobj.material = newmat
|
|
|
|
-- Make sure that edges between materials are visible,
|
|
-- to avoid messing up matids if collision is converted to Poly in future:
|
|
-- (required by Deepener methods)
|
|
if (RsMakeMeshMatBordersVisible != undefined) do
|
|
(
|
|
RsMakeMeshMatBordersVisible NewObj
|
|
)
|
|
|
|
-- Apply appropriate road-flags to faces found near NavPaths:
|
|
gRsNavPathLoad.ApplyRoadFlagsToCollision NewObj
|
|
|
|
-- Object-list; initially just includes the first generated object:
|
|
local NewObjs = #(NewObj)
|
|
local WeaponObjs = #{}
|
|
local MoverObjs = #{}
|
|
|
|
-- Are we generating any weapon/mover-specific collision?
|
|
local HasWeaponObjs = False
|
|
local HasMoverObjs = False
|
|
|
|
-- Push down verts for faces with "*DEEP*" surfacetypes:
|
|
if DoDeepness do
|
|
(
|
|
-- Get Deep-collision faces on object:
|
|
local DeepFaces = (RsGetCollFacesByName NewObj "*DEEP*")
|
|
|
|
if (DeepFaces.NumberSet != 0) do
|
|
(
|
|
HasWeaponObjs = True
|
|
HasMoverObjs = True
|
|
|
|
-- Detach copy of deep-faces, to use as Weapon collision:
|
|
local WepObj = (Copy NewObj)
|
|
ConvertToMesh WepObj
|
|
WepObj.Mesh = Meshop.DetachFaces NewObj DeepFaces Delete:False AsMesh:True
|
|
append NewObjs WepObj
|
|
WeaponObjs[NewObjs.Count] = True
|
|
|
|
-- Deepen faces on main mesh (i.e. mover-collision)
|
|
-- (Modify-tab needs to be active to make required changes)
|
|
ResumeEditing()
|
|
RsCollDeepenFaces NewObj DeepFaces:DeepFaces
|
|
SuspendEditing()
|
|
|
|
ConvertToMesh NewObj
|
|
DeepFaces = (RsGetCollFacesByName NewObj "*DEEP*")
|
|
|
|
if (DeepFaces.NumberSet == NewObj.NumFaces) then
|
|
(
|
|
-- If the whole mesh was Deep, there no need to separate its faces off:
|
|
MoverObjs[1] = True
|
|
)
|
|
else
|
|
(
|
|
-- Detach deepened faces from mesh, to be set as just Mover collision:
|
|
local MovObj = (Copy NewObj)
|
|
ConvertToMesh MovObj
|
|
MovObj.Mesh = Meshop.DetachFaces NewObj DeepFaces Delete:True AsMesh:True
|
|
append NewObjs MovObj
|
|
MoverObjs[NewObjs.Count] = True
|
|
)
|
|
)
|
|
|
|
Free DeepFaces
|
|
)
|
|
|
|
-- Split up mesh(es) until each has fewer than the max number of verts/faces:
|
|
local RedoLast = false
|
|
local doSplit = false
|
|
local NewObjNum = 0
|
|
|
|
while (NewObjNum < NewObjs.count) do
|
|
(
|
|
if not RedoLast then
|
|
(
|
|
NewObjNum += 1
|
|
)
|
|
|
|
-- Is this mesh going to be Weapon/Mover collision?
|
|
local isWeaponColl = WeaponObjs[NewObjNum]
|
|
local isMoverColl = MoverObjs[NewObjNum]
|
|
|
|
local ThisNewObj = NewObjs[NewObjNum]
|
|
local NumVerts = (GetNumVerts ThisNewObj.Mesh)
|
|
local NumFaces = (GetNumFaces ThisNewObj.Mesh)
|
|
|
|
local OverVerts = (NumVerts > RsMaxCollVerts)
|
|
local OverFaces = (NumFaces > RsMaxCollFaces)
|
|
doSplit = (OverVerts or OverFaces)
|
|
|
|
if (doSplit) then
|
|
(
|
|
-- Split mesh along its longest axis:
|
|
RsMesh_SplitLongestAxis ThisNewObj &NewObjs
|
|
|
|
-- Set the newly-generated mesh as weapon/mover-collision, if the original object was:
|
|
WeaponObjs[NewObjs.Count] = isWeaponColl
|
|
MoverObjs[NewObjs.Count] = isMoverColl
|
|
|
|
RedoLast = true
|
|
)
|
|
else
|
|
(
|
|
RedoLast = false
|
|
)
|
|
)
|
|
|
|
-- Apply collision-type flags to object(s) UDP:
|
|
if (HasWeaponObjs or HasMoverObjs or isRiverObj) do
|
|
(
|
|
-- Initialise collision-flagging struct:
|
|
CollisionEditor = RsCollTypes()
|
|
CollisionEditor.Init()
|
|
|
|
-- Set 'weapon' type-flag for objects that require it:
|
|
if HasWeaponObjs do
|
|
(
|
|
WeaponObjs = for n in WeaponObjs collect NewObjs[n]
|
|
CollisionEditor.SetObjCollTypeFlags WeaponObjs "Weapons"
|
|
for Obj in WeaponObjs do (Obj.Name += "_WEAPON")
|
|
)
|
|
-- Set 'mover' type-flag for objects that require it:
|
|
if HasMoverObjs do
|
|
(
|
|
MoverObjs = for n in MoverObjs collect NewObjs[n]
|
|
CollisionEditor.SetObjCollTypeFlags MoverObjs "Mover|Horse|Vehicle|Cover"
|
|
for Obj in MoverObjs do (Obj.Name += "_MOVER")
|
|
)
|
|
-- Set 'river' type-flag on object(s) if collision included water-surfaces:
|
|
if isRiverObj do
|
|
(
|
|
collisionEditor.setObjCollTypeFlags NewObjs "river"
|
|
for Obj in NewObjs do (Obj.Name += "_RIVER")
|
|
)
|
|
)
|
|
|
|
--format "Objs: %\n" (NewObjs as string)
|
|
--format "WeaponObjs: %\n" WeaponObjs
|
|
--format "MoverObjs: %\n" MoverObjs
|
|
|
|
-- Convert all meshes to collision, and print their names:
|
|
for NewObj in NewObjs do
|
|
(
|
|
-- Convert generated mesh to collision:
|
|
mesh2col NewObj
|
|
|
|
format " Created: %\n" NewObj.Name
|
|
)
|
|
|
|
format "[Create Collision took: % seconds] %\n" (0.001 * (Timestamp() - TimeStart)) obj.name
|
|
return True
|
|
)
|
|
|
|
---------------------------------------------------------------------------------------
|
|
-- Reassigns collision-mesh materials to match current parent-mesh textures:
|
|
---------------------------------------------------------------------------------------
|
|
fn AssignObjectMat obj =
|
|
(
|
|
local timestart = timestamp()
|
|
|
|
if not isEditPolyMesh obj do
|
|
(
|
|
return false
|
|
)
|
|
|
|
local collisionChildren = for childObj in obj.children where (isKindOf childObj Col_Mesh) collect childObj
|
|
if (collisionChildren.count == 0) do
|
|
(
|
|
return false
|
|
)
|
|
|
|
-- Read surface-data for parent object:
|
|
lblProgStatus.text = "Collecting face data: " + obj.name + " [Esc to cancel]"
|
|
local surfaceData = gRsTexToColl.GetObjFaceData Obj AsMesh:True GetPos:true CollSetRoll:RsCollisionSetRoll
|
|
|
|
if (SurfaceData != False) do
|
|
(
|
|
for childObj in collisionChildren while (progressContinue = not keyboard.escPressed) do
|
|
(
|
|
lblProgStatus.text = "Reassigning surfaces: " + childObj.name + " [Esc to cancel]"
|
|
subProgBar.value = 0
|
|
|
|
local savetrans = childobj.transform
|
|
col2mesh childobj
|
|
|
|
local numFaces = getnumfaces childobj
|
|
|
|
local matchingFaces = for n = 1 to surfaceData.colSurfaceNames.count collect #()
|
|
local removeFaces = #{}
|
|
|
|
-- This sort is used for bsearch, which tends to be faster than findItem and can make inexact matches:
|
|
fn sortPointItem v1Item v2Item =
|
|
(
|
|
v1 = v1Item[1]
|
|
v2 = v2Item[1]
|
|
|
|
case of
|
|
(
|
|
-- Matches just-off values:
|
|
((distance v1 v2) < 0.001):0
|
|
(v1[1] < v2[1]):-1
|
|
(v1[1] > v2[1]):1
|
|
(v1[2] < v2[2]):-1
|
|
(v1[2] > v2[2]):1
|
|
(v1[3] < v2[3]):-1
|
|
(v1[3] > v2[3]):1
|
|
default:0
|
|
)
|
|
)
|
|
|
|
local searchPointItems = #()
|
|
|
|
for listNum = 1 to surfaceData.colSurfaceFaces.count do
|
|
(
|
|
local faceList = surfaceData.colSurfaceFaces[listNum]
|
|
join searchPointItems (for faceItem in faceList collect #(faceItem.pos, faceItem))
|
|
)
|
|
|
|
qsort searchPointItems sortPointItem
|
|
|
|
local searchPoints = for item in searchPointItems collect item[1]
|
|
|
|
local matchingFace, nearFaceDist, newDist
|
|
for faceNum = 1 to numFaces while (progressContinue = not keyboard.escPressed) do
|
|
(
|
|
subProgBar.value = 100.0 * faceNum / numFaces
|
|
|
|
-- Find centre of face:
|
|
local faceCentre = meshOp.getFaceCenter childobj faceNum node:childobj
|
|
|
|
-- Search for exact(ish) point-match, if possible:
|
|
local matchingFace = bsearch #(faceCentre) searchPointItems sortPointItem
|
|
|
|
if (matchingFace != undefined) then
|
|
(
|
|
matchingFace = matchingFace[2]
|
|
)
|
|
else
|
|
(
|
|
-- If match wasn't found, look for the closest point instead:
|
|
local pointDists = for item in searchPoints collect (distance item faceCentre)
|
|
local findItemNum = findItem pointDists (amin pointDists)
|
|
|
|
matchingFace = searchPointItems[findItemNum][2]
|
|
)
|
|
|
|
if (matchingFace.colSurfaceNum == 0) then
|
|
(
|
|
removeFaces[faceNum] = true
|
|
)
|
|
else
|
|
(
|
|
matchingFace.matchedFace = faceNum
|
|
append matchingFaces[matchingFace.colSurfaceNum] matchingFace
|
|
)
|
|
)
|
|
|
|
-- Clear/retain uv-channel data:
|
|
(
|
|
-- Make array of channels to be preserved, if they've already been assigned:
|
|
local keepChannels = #{}
|
|
keepChannels[11] = chkPreserve11.checked
|
|
keepChannels[12] = chkPreserve12.checked
|
|
|
|
-- Deactivate all mapping-channels that haven't been marked for preservation:
|
|
for n = 1 to ((meshop.getNumMaps childobj) - 1) where not keepChannels[n] do
|
|
(
|
|
meshop.setMapSupport childobj n false
|
|
)
|
|
|
|
local topChannelNum = (meshop.getNumMaps childobj) - 1
|
|
keepChannels = keepChannels as array
|
|
|
|
-- Remove any vertex-channels above the maximum keep-channel:
|
|
if (keepChannels.count != 0) and (topChannelNum > keepChannels[keepChannels.count]) do
|
|
(
|
|
meshop.setNumMaps childobj (topChannelNum + 1)
|
|
)
|
|
|
|
-- Remove all vertex-channels if no keep-channel is in use:
|
|
if (keepChannels.count == 0) or (topChannelNum < keepChannels[1]) do
|
|
(
|
|
meshop.setNumMaps childobj 0
|
|
)
|
|
)
|
|
|
|
local submatlist = #()
|
|
for surfaceNum = 1 to surfaceData.colSurfaceNames.count where (matchingFaces[surfaceNum].count != 0) do
|
|
(
|
|
local collName = surfaceData.colSurfaceNames[surfaceNum]
|
|
local collFlags = surfaceData.colSurfaceFlags[surfaceNum]
|
|
local cullImmune = surfaceData.colSurfaceProcCullImmune[surfaceNum]
|
|
local pedPop = surfaceData.colSurfacePops[surfaceNum]
|
|
local matFaces = matchingFaces[surfaceNum]
|
|
|
|
local newsubmat = RexBoundMtl name:collName
|
|
RexSetCollisionName newsubmat collName
|
|
|
|
RexSetCollisionFlags newsubmat collFlags
|
|
RexSetProcCullImmune newSubMat cullImmune
|
|
|
|
if (pedPop != -1) and (pedPop != undefined) do
|
|
RexSetPopDensity newsubmat pedPop
|
|
|
|
-- Set the matids for all faces
|
|
for faceInfo in matFaces do
|
|
(
|
|
local faceNum = faceInfo.matchedFace
|
|
SetfaceMatID childobj faceNum surfaceNum
|
|
)
|
|
|
|
-- Mark the collisionmesh as RIVER if it uses any WATER collision-materials
|
|
if (collName == "WATER") do
|
|
(
|
|
local collisionEditor = RsCollTypes()
|
|
collisionEditor.setObjCollTypeFlags #(obj) "river"
|
|
collisionEditor.findBadFlagObjs #(obj) fix:true
|
|
)
|
|
|
|
Append submatlist newsubmat
|
|
)
|
|
|
|
if (removeFaces.numberSet == childobj.numfaces) then
|
|
(
|
|
-- Remove empty collision-meshes:
|
|
delete childobj
|
|
)
|
|
else
|
|
(
|
|
-- Delete unwanted faces:
|
|
if (removeFaces.numberSet != 0) do
|
|
(
|
|
meshop.deleteFaces childobj removeFaces
|
|
)
|
|
|
|
local matName = "CollSet:" + childobj.name + " - Multimaterial"
|
|
local newmat = Multimaterial name:matName
|
|
|
|
newmat.numsubs = submatlist.count
|
|
newmat.materialList = submatlist
|
|
childobj.material = newmat
|
|
|
|
-- Make sure that edges between materials are visible,
|
|
-- to avoid messing up matids if collision is converted to Poly in future:
|
|
if (RsMakeMeshMatBordersVisible != undefined) do
|
|
(
|
|
RsMakeMeshMatBordersVisible ChildObj
|
|
)
|
|
|
|
mesh2col childobj
|
|
childobj.transform = savetrans
|
|
)
|
|
)
|
|
)
|
|
|
|
format "[Reassign took: % seconds] %\n" (0.001 * (Timestamp() - TimeStart)) obj.name
|
|
)
|
|
|
|
--////////////////////////////////////////////////////////////
|
|
-- events
|
|
--////////////////////////////////////////////////////////////
|
|
|
|
--------------------------------------------------------------
|
|
-- Reassign Collision-Surfaces for Collision Object(s):
|
|
--------------------------------------------------------------
|
|
on BtnReassignCollMats pressed do
|
|
(
|
|
SuspendEditing()
|
|
SetWaitCursor()
|
|
progressContinue = true
|
|
|
|
local selObjs = (GetCurrentSelection())
|
|
|
|
-- Don't generate collision for LOD-objects:
|
|
local doObjs = for obj in selObjs where (getattrclass obj == "Gta Object") and ((getLODChildren obj).count == 0) collect obj
|
|
|
|
-- Load surface-assignments, offering to cancel if any are unassigned:
|
|
progressContinue = GetTexListDataFromXMLForObjs doObjs
|
|
|
|
if progressContinue do
|
|
(
|
|
undo "Reassign Collision Materials" on
|
|
(
|
|
totalProgBar.value = 0.0
|
|
for i = 1 to doObjs.count while (progressContinue = not keyboard.escPressed) do
|
|
(
|
|
local obj = doObjs[i]
|
|
subProgBar.value = 0.0
|
|
|
|
AssignObjectMat obj
|
|
|
|
totalProgBar.value = (100.0 * i / doObjs.count)
|
|
)
|
|
)
|
|
)
|
|
|
|
if progressContinue then
|
|
(
|
|
totalProgBar.value = 100.0
|
|
subProgBar.value = 100.0
|
|
lblProgStatus.text = "Reassign Completed"
|
|
)
|
|
else
|
|
(
|
|
totalProgBar.value = 0.0
|
|
subProgBar.value = 0.0
|
|
lblProgStatus.text = "Reassign Cancelled"
|
|
)
|
|
|
|
SetArrowCursor()
|
|
ResumeEditing()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Create Collision-Object(s):
|
|
--------------------------------------------------------------
|
|
on BtnCreateCollObjs pressed do
|
|
(
|
|
local idxHandEdited = getattrindex "Gta Object" "Hand edited collision"
|
|
|
|
local selObjs = (GetCurrentSelection())
|
|
|
|
-- Don't generate collision for LOD-objects:
|
|
local doObjs = for obj in selObjs where (getattrclass obj == "Gta Object") and ((getLODChildren obj).count == 0) collect obj
|
|
|
|
-- Store current subobject-level:
|
|
local SubObjWas = SubObjectLevel
|
|
|
|
-- Will we limit collision-create to selected faces?
|
|
local JustSelFaces = False
|
|
if (BtnCreateForFaces.Checked) and (doObjs.Count == 1) do
|
|
(
|
|
-- Only bother turning on this mode if we're currently in Face subobject mode:
|
|
JustSelFaces = ((GetSelectionLevel doObjs[1]) == #Face)
|
|
)
|
|
|
|
-- Load surface-assignments, offering to cancel if any are unassigned:
|
|
local success = (GetTexListDataFromXMLForObjs doObjs)
|
|
if (not success) do return False
|
|
|
|
-- Warn user if attempting to regenerate collision in a late-stage project:
|
|
if MatureProjWarn and (doObjs.Count != 0) do
|
|
(
|
|
local Msg = "This operation will create brand new collision meshes for the selected objects.\n\nWe are at a critical stage in the project where most collision has been hand edited/authored.\n\nDo you still want to continue with this operation?"
|
|
if (querybox Msg Title:"Warning: Should you be doing this?") then
|
|
(
|
|
-- Don't bother warning again:
|
|
MatureProjWarn = False
|
|
)
|
|
else
|
|
(
|
|
return False
|
|
)
|
|
)
|
|
|
|
-- Collect objects that have collision:
|
|
local hasColObjs = #()
|
|
for obj in doObjs do
|
|
(
|
|
local findCol = true
|
|
for childObj in obj.children where (getattrclass childobj == "Gta Collision") while findCol do
|
|
(
|
|
append hasColObjs obj
|
|
findCol = false
|
|
)
|
|
)
|
|
|
|
local delExistingCols = True
|
|
|
|
if (hasColObjs.count != 0) do
|
|
(
|
|
local msg = stringStream ""
|
|
local plural = if (hasColObjs.count != 1) then "s" else ""
|
|
|
|
format "Delete existing collision from object% before creating new?\n\n" plural to:msg
|
|
|
|
for objNum = 1 to hasColObjs.count do
|
|
(
|
|
format "%" hasColObjs[objNum].name to:msg
|
|
|
|
if (objNum != hasColObjs.count) do (format "\n" to:msg)
|
|
)
|
|
|
|
RsMapExportCancelled = false
|
|
delExistingCols = RsQueryBoxTimeOut (msg as string) title:"Delete existing collision?" timeout:-1 cancel:true
|
|
|
|
if RsMapExportCancelled do return false
|
|
)
|
|
|
|
undo "Create Collision Meshes" on
|
|
(
|
|
progressContinue = true
|
|
totalProgBar.value = 0.0
|
|
|
|
SuspendEditing()
|
|
SetWaitCursor()
|
|
|
|
for i = 1 to doObjs.count while (progressContinue = not keyboard.escPressed) do
|
|
(
|
|
local obj = doObjs[i]
|
|
subProgBar.value = 0.0
|
|
|
|
local createCol = true
|
|
|
|
if delExistingCols and (getattr obj idxHandEdited) then
|
|
(
|
|
createCol = querybox (obj.name + " has hand edited collision which will be destroyed. Continue?")
|
|
)
|
|
|
|
if createCol do
|
|
(
|
|
ProgressContinue = CreateObjectMat obj delExisting:delExistingCols DoDeepness:BtnAddDeepness.Checked JustSelFaces:JustSelFaces
|
|
)
|
|
|
|
totalProgBar.value = (100.0 * i / doObjs.count)
|
|
)
|
|
|
|
if progressContinue then
|
|
(
|
|
totalProgBar.value = 100.0
|
|
subProgBar.value = 100.0
|
|
lblProgStatus.text = "Create Completed"
|
|
)
|
|
else
|
|
(
|
|
totalProgBar.value = 0.0
|
|
subProgBar.value = 0.0
|
|
lblProgStatus.text = "Create Cancelled"
|
|
)
|
|
|
|
-- Filter out deleted nodes, as we can't select those...
|
|
SelObjs = for Obj in SelObjs where (isValidNode Obj) collect Obj
|
|
Select SelObjs
|
|
|
|
SetArrowCursor()
|
|
ResumeEditing()
|
|
)
|
|
|
|
-- Restore subobject-level:
|
|
if (SubObjWas != undefined) do
|
|
(
|
|
SubObjectLevel = SubObjWas
|
|
)
|
|
|
|
return OK
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Move selected texname into/out fo global.xml:
|
|
--------------------------------------------------------------
|
|
on btnToggleGlobal changed state do
|
|
(
|
|
local SelTexInfo = GetSelTexInfo()
|
|
if (SelTexInfo != undefined) then
|
|
(
|
|
local TexName = SelTexInfo.TexName
|
|
gRsTexToColl.ToggleGlobalDataSettingXml TexName CollSetRoll:RsCollisionSetRoll
|
|
UpdateTexMapList()
|
|
)
|
|
else
|
|
(
|
|
btnToggleGlobal.checked = false
|
|
)
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Delete Definition:
|
|
--------------------------------------------------------------
|
|
on btnDeleteDef pressed do
|
|
(
|
|
local SelTexInfo = GetSelTexInfo()
|
|
if (SelTexInfo != undefined) do
|
|
(
|
|
local TexName = SelTexInfo.TexName
|
|
gRsTexToColl.DeleteTexnameData TexName
|
|
UpdateMaterial MatClicked:True
|
|
)
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Get Tex List From Folder:
|
|
--------------------------------------------------------------
|
|
on btnGetFolder pressed do
|
|
(
|
|
UpdateSelectionSetFromFolder()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Snap Pivots of Selected To Parent:
|
|
--------------------------------------------------------------
|
|
on btnSnapPivot pressed do
|
|
(
|
|
-- Iterate through the selected objects, looking for collision
|
|
for obj in (selection as array) do
|
|
(
|
|
if getattrclass obj == "Gta Collision" do
|
|
(
|
|
objParent = obj.parent
|
|
obj.pivot = objParent.pivot
|
|
)
|
|
)
|
|
)
|
|
|
|
----------------------------------------------------------------------------------
|
|
-- Update texture-list when Set/Unset checkboxes are toggled:
|
|
----------------------------------------------------------------------------------
|
|
fn DoListUpdate =
|
|
(
|
|
UpdateTexMapList()
|
|
UpdateTexture()
|
|
UpdateMaterial()
|
|
)
|
|
on chkSetTexs changed newval do
|
|
(
|
|
if (not NewVal) do (chkUnsetTexs.Checked = True)
|
|
DoListUpdate()
|
|
)
|
|
on chkUnsetTexs changed newval do
|
|
(
|
|
if (not NewVal) do (chkSetTexs.Checked = True)
|
|
DoListUpdate()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Materials-list:
|
|
--------------------------------------------------------------
|
|
fn showPreviewWindow =
|
|
(
|
|
if (LstTextures.selection == 0) then
|
|
(
|
|
if (collSet_showBmpRoll != undefined) and collSet_showBmpRoll.open do
|
|
(
|
|
destroyDialog collSet_showBmpRoll
|
|
)
|
|
)
|
|
else
|
|
(
|
|
if (collSet_showBmpRoll != undefined) and collSet_showBmpRoll.open then
|
|
(
|
|
collSet_showBmpRoll.updateBmp()
|
|
)
|
|
else
|
|
(
|
|
rollout collSet_showBmpRoll "Bitmap"
|
|
(
|
|
bitmap bigBmpShowCtrl pos:[0,0]
|
|
|
|
fn updateBmp =
|
|
(
|
|
local SelTexInfo = GetSelTexInfo()
|
|
|
|
if (SelTexInfo == undefined) then
|
|
(
|
|
destroyDialog collSet_showBmpRoll
|
|
)
|
|
else
|
|
(
|
|
local bmpSet
|
|
|
|
if (isKindOf SelTexInfo.TexMap bitmaptexture) then
|
|
(
|
|
bmpSet = SelTexInfo.TexMap
|
|
)
|
|
else
|
|
(
|
|
bmpSet = bitmaptexture()
|
|
bmpSet.filename = SelTexInfo.Filename
|
|
bmpSet.reload()
|
|
)
|
|
|
|
local bmpWidth = bmpset.bitmap.width
|
|
local bmpHeight = bmpset.bitmap.height
|
|
|
|
bigBmpShowCtrl.width = collSet_showBmpRoll.width = bmpWidth
|
|
bigBmpShowCtrl.height = collSet_showBmpRoll.height = bmpHeight
|
|
|
|
local img = bitmap bmpWidth bmpHeight
|
|
local rm = rendermap bmpSet into:img size:[bmpWidth, bmpHeight]
|
|
|
|
bigBmpShowCtrl.bitmap = img
|
|
|
|
close rm
|
|
)
|
|
)
|
|
|
|
on collSet_showBmpRoll open do
|
|
(
|
|
updateBmp()
|
|
)
|
|
)
|
|
|
|
destroyDialog collSet_showBmpRoll
|
|
createDialog collSet_showBmpRoll
|
|
)
|
|
)
|
|
)
|
|
|
|
on LstTextures selected idx do
|
|
(
|
|
btnToggleGlobal.checked = false
|
|
|
|
-- Show list-item's bitmap in preview-box;
|
|
UpdateTexture()
|
|
|
|
-- Set tool to show current settings for selected texmap:
|
|
UpdateMaterial matClicked:true
|
|
|
|
-- Update big pop-out preview-window, if it's shown:
|
|
if (collSet_showBmpRoll != undefined) and collSet_showBmpRoll.open then
|
|
(
|
|
collSet_showBmpRoll.updateBmp()
|
|
)
|
|
)
|
|
|
|
-- Show texture-preview window when image or texture-name is double-clicked:
|
|
on LstTextures doubleClicked idx do (showPreviewWindow())
|
|
on bmpUi dblclick do (showPreviewWindow())
|
|
|
|
--------------------------------------------------------------
|
|
-- Surface-categories list:
|
|
--------------------------------------------------------------
|
|
on lstCategories selected idx do
|
|
(
|
|
setListToCategory idx
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Surface-types list:
|
|
--------------------------------------------------------------
|
|
on lstSurfaces selected idx do
|
|
(
|
|
selectedSurface = lstSurfaces.selected
|
|
|
|
if (LstTextures.selection == 0) then
|
|
(
|
|
-- Deselect if no texturename is selected:
|
|
lstSurfaces.selection = 0
|
|
)
|
|
else
|
|
(
|
|
SetMaterial()
|
|
)
|
|
|
|
SetRexCtrlsEnabled()
|
|
)
|
|
|
|
--------------------------------------------------------------
|
|
-- Event-handlers for "RexBounds Flags" controls:
|
|
--------------------------------------------------------------
|
|
on chkStairs changed val do (SetMaterial())
|
|
on chkClimable changed val do (SetMaterial())
|
|
on chkSeeThrough changed val do (SetMaterial())
|
|
on chkShootThrough changed val do (SetMaterial())
|
|
on chkShootThroughFX changed val do (SetMaterial())
|
|
on chkPath changed val do (SetMaterial())
|
|
on chkSetPedPop changed val do (SetMaterial())
|
|
on spnPedDensity changed val do
|
|
(
|
|
chkSetPedPop.checked = true
|
|
SetMaterial()
|
|
)
|
|
on chkCover changed val do (SetMaterial())
|
|
on chkCamera changed val do (SetMaterial())
|
|
on chkProcCullImmune changed val do (SetMaterial())
|
|
on chkRoadRoad changed val do (SetMaterial())
|
|
on chkRoadPath changed val do (SetMaterial())
|
|
on chkRoadTrail changed val do (SetMaterial())
|
|
on chkNotHorseWalkable changed val do (SetMaterial())
|
|
|
|
on btnEditLock changed state do
|
|
(
|
|
-- Disable requested editing if network-sync failed earlier:
|
|
if (gRsTexToColl.NetworkErrorShown) do
|
|
(
|
|
BtnEditLock.Checked = False
|
|
BtnEditLock.Enabled = False
|
|
)
|
|
|
|
EditControls.Enabled = BtnEditLock.Checked
|
|
SetRexCtrlsEnabled()
|
|
)
|
|
|
|
on RsCollisionSetRoll open do
|
|
(
|
|
-- Initialise tech-art banner:
|
|
bannerStruct.setup()
|
|
|
|
Format "\nCollision Setter XMLs saved to folder:\n %\n" (RsMakeBackSlashes gRsTexToColl.collSetXmlPath)
|
|
Format "Locally cached to:\n %\n\n" (RsMakeBackSlashes gRsTexToColl.collSetCachePath)
|
|
|
|
-- Set rollout to fit the flag-changes box:
|
|
RsCollisionSetRoll.height = lblProgStatus.pos.y + 18
|
|
|
|
editControls.enabled = btnEditLock.checked
|
|
RexControls.Enabled = False
|
|
|
|
-- Set default paths:
|
|
gRsTexToColl.SetupPaths()
|
|
|
|
-- Do initial data-sync on startup:
|
|
gRsTexToColl.LocalCacheSync()
|
|
|
|
-- Disable edit-control if network-sync failed:
|
|
BtnEditLock.Enabled = (not gRsTexToColl.NetworkErrorShown)
|
|
|
|
SetupList()
|
|
|
|
callbacks.addscript #selectionSetChanged "RsCollisionSetRoll.updateSelectionSet (selection as array)" id:#collisionSet
|
|
|
|
UpdateSelectionSet (selection as array)
|
|
)
|
|
|
|
on RsCollisionSetRoll close do
|
|
(
|
|
try (destroyDialog collSet_showBmpRoll) catch ()
|
|
callbacks.removescripts id:#collisionSet
|
|
|
|
gc light:true
|
|
(dotnetclass "system.gc").collect()
|
|
)
|
|
)
|
|
|
|
createDialog RsCollisionSetRoll
|