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