-- -- File:: selector.ms -- Description:: Object Selector Mk2 -- -- Author:: David Muir -- Date:: 10 July 2007 -- -- Greg Smith -- Rockstar North -- 25/5/2004 -- a whole lot of stuff for selecting objects based on specific attributes such as by -- txd, collision group etc. try destroyDialog RsSelToolRoll catch () filein (RsConfigGetWildWestDir() + "script/3dsMax/_Common_Functions/FN_Objects.ms") filein "pipeline/util/IdeUtils.ms" -- Rs Asset Size utilites filein "Rockstar/util/assetsizes.ms" filein "rockstar/util/collutil.ms" -- Select objects, then redraw: fn RsSelectObjs objs = ( undo "select objects" on ( case ::RsSelToolGeneralRoll.ddlSelectionOperand.selection of ( 1 : -- CLEAR ( clearselection() select objs ) 2 : -- ADD ( selectMore objs ) 3 : -- REMOVE ( deselect objs ) ) completeRedraw() ) ) fn RsAppendToValueString fieldname appendtext = ( idxObjectField = GetAttrIndex "Gta Object" fieldname idxAnimHierField = GetAttrIndex "GtaAnimHierarchy" fieldname for obj in selection do ( idxField = -1 if GetAttrClass obj == "Gta Object" then ( idxField = idxObjectField ) if GetAttrClass obj == "GtaAnimHierarchy" then ( idxField = idxAnimHierField ) if idxField != -1 then ( fieldString = GetAttr obj idxField fieldString = fieldString + appendtext if fieldString.count < 30 then ( SetAttr obj idxField fieldString ) ) ) ) fn RsColourByGroup objLists colourList = ( for i = 1 to objLists.count do ( local numRounded = mod (i - 1) colourlist.count local numDivision = abs((i - 1) / colourlist.count) local numSubtraction = (10 * numDivision) numSubtraction = mod numSubtraction 255 numSubtraction = numSubtraction / 255.0 objLists[i].wirecolor = (1 - numSubtraction) * colourList[numRounded + 1] ) ) fn RsColourByFieldValueString objLists = ( local colourList = #((color 255 0 0),(color 255 0 255),(color 0 0 255),(color 255 255 0),(color 0 255 255),(color 0 255 0),(color 0 128 192)) RsColourByGroup objLists colourList ) rollout RsSelToolGeneralRoll "Selection:" ( --label lblSelAllSome "Select from:" align:#left across:2 --radioButtons rdoSelAllSome "" labels:#("All", "Current Selection") columns:2 align:#left default:1 offset:[-50,0] label lblSelAllSome "Select from:" align:#left across:5 offset:[0,4] dropdownlist ddlSelectionFilter items:#("All", "Selection") offset:[-10,0] label lblSelAnd "and" align:#left offset:[-3,4] dropdownlist ddlSelectionOperand items:#("Clear Selection", "Add To Selection", "Remove From Sel") width:110 align:#left offset:[-50,0] checkBox chkIgnoreHidden "Ignore Hidden" checked:false align:#right offset:[10,4] -- Is 'Current Selection' option selected? fn IsFromSelected = ( return (ddlSelectionFilter.selection == 2) ) -- Returns list of objects matching this rollout's controls: fn objectList attrClass: = ( local selObjs = (if (IsFromSelected()) then Selection else Objects) as array if chkIgnoreHidden.checked do ( selObjs = for obj in selObjs where (not obj.isHidden) collect obj ) -- Filter by Attribute Class: if (attrClass != unsupplied) do ( selObjs = for obj in selObjs where (getAttrClass obj == attrClass) collect obj ) return selObjs ) -- Selects faces of mesh/poly objects that are supplied by Func: fn selObjFaces objs faceLists silent:false = ( undo "select faces" on ( -- Filter out non-selecty objects: local selObjs = #() local selFaceItems = #() local selFaceItemObjs = #() -- Filter out objects with empty face-lists, and make list with no redundant instances: for n = 1 to faceLists.count where (faceLists[n].numberSet != 0) do ( local obj = objs[n] append selObjs obj -- Don't select faces more than once for instances: local doObj = True for otherObj in selFaceItemObjs while doObj do ( doObj = not (areNodesInstances obj otherObj) ) if doObj do ( append selFaceItemObjs obj append selFaceItems (dataPair obj:obj faceList:faceLists[n]) ) ) -- Stop processing if no objects have faces for selection: if (selFaceItems.count == 0) do ( clearSelection() return OK ) if (getCommandPanelTaskMode() != #modify) do ( setCommandPanelTaskMode #modify ) local obj = selFaceItems[1].obj if (selFaceItems.count == 1) and (obj.modifiers.count == 0) and ((isKindOf obj Editable_Poly) or (isKindOf obj Editable_Mesh)) then ( if (getSelectionLevel obj) != #face do ( setSelectionLevel obj #face ) setFaceSelection obj selFaceItems[1].faceList select selObjs ) else ( local selWarn = if (selFaceItems.count == 1) then "Warning: Found object has modifiers." else "Warning: Multiple objects found." if silent or (queryBox (selWarn + "\n\nDo you want to show found faces by adding a \"Poly Select\" modifier?") title:"Add Selection Modifier?") do ( local timeStart = timeStamp() with redraw off ( pushPrompt "Adding Poly Select modifiers" -- Keep note of objects that were originally mesh, and need to have their face-nums converted: local notPolyObjNums = #{} local selObjs = for n = 1 to selFaceItems.count collect ( local thisObj = selFaceItems[n].obj notPolyObjNums[n] = ((RsMeshPolyOp thisObj) != polyOp) thisObj ) select selObjs undo off ( local selMod = Edit_Poly() modPanel.addModtoSelection selMod for n = 1 to selFaceItems.count do ( local promptMsg = stringStream "" format "Adding Poly Select modifiers (% of %)" n selFaceItems.count to:promptMsg pushPrompt (promptMsg as string) local obj = selFaceItems[n].obj local faceList = selFaceItems[n].faceList -- Convert face-selection from mesh to poly if required: if notPolyObjNums[n] do ( local newFaceList = #{} local polyCount = (selMod.getNumFaces node:obj) newFaceList.count = polyCount local triToPolyList = #() for polyNum = 1 to polyCount do ( for polyTriNum = 3 to (selMod.GetFaceDegree polyNum node:obj) do ( append triToPolyList polyNum ) ) for faceNum in faceList do ( newFaceList[triToPolyList[faceNum]] = True ) faceList = newFaceList ) selMod.setSelection #Face faceList node:obj popPrompt() ) ) -- Add Poly Select modifier to objects, which takes on earlier modifiers' face-selections: modPanel.addModtoSelection (poly_select()) popPrompt() -- Remove the temporary edit_poly modifier: pushPrompt "Deleting temp modifiers..." suspendEditing() for obj in selObjs do ( deleteModifier obj selMod ) resumeEditing() popPrompt() format "Face-select: Modifier-add took %s\n" ((timeStamp() - timeStart) / 1000.0) ) ) ) local selNode = modPanel.getCurrentObject() if (isKindOf selNode Poly_Select) or (isKindOf selNode Editable_Poly) or (isKindOf selNode Editable_Mesh) do ( subobjectLevel = 4 ) ) ) -- LOOP ROLLOUTS AND CALL SELECTION CHANGED FUNCTION fn updateRolloutsSelectionChanged = ( local SubRolls = ::RsSelToolRoll.theSubRollout.Rollouts for SubRoll in SubRolls where (isProperty SubRoll #SelOptionChanged) do ( SubRoll.SelOptionChanged() ) ) -- Trigger 'SelOptionChanged' function for shown rollouts that have it: on ddlSelectionFilter selected Val do ( updateRolloutsSelectionChanged() ) -- SAME AS ABOVE BUT FOR THE CHECKBOX on chkIgnoreHidden changed Val do ( updateRolloutsSelectionChanged() ) -- Don't allow rollout to be rolled up: on RsSelToolGeneralRoll rolledUp Down do ( if (not Down) do (RsSelToolGeneralRoll.open = True) ) ) rollout RsSelToolObjRoll "Object Selection" ( local btnWidth = 60 local btnHeight = 18 spinner spinBoundBox "Bounding Box Volume:" range:[0,1000,0] align:#left width:150 across:3 button btnSelBBLarger "Select >" align:#right width:btnWidth height:btnHeight offset:[-10,-1] button btnSelBBSmaller "Select <" align:#left width:btnWidth height:btnHeight offset:[-4,-1] spinner spinPolyCount "Poly Count: " range:[0,100000,0] scale:1.0 align:#left width:150 type:#integer across:3 button btnSelPolyLarger "Select >" align:#right width:btnWidth height:btnHeight offset:[-10,-1] button btnSelPolySmaller "Select <" align:#left width:btnWidth height:btnHeight offset:[-4,-1] spinner spinPriority "Priority: " range:[0,3,3] scale:1 type:#integer across:4 align:#left width:180 button btnSelPriorityLarger "Select >=" width:btnWidth height:btnHeight offset:[66,-1] button btnSelPrioritySmaller "Select <=" width:btnWidth height:btnHeight pos:(btnSelPriorityLarger.pos + [btnWidth + 6,0]) button btnSelPriorityExact "Select ==" width:btnWidth height:btnHeight pos:(btnSelPrioritySmaller.pos + [btnWidth + 6,0]) spinner spinPercent "Reduce Selection %: " range:[0,100,50] scale:1 type:#integer across:2 align:#left width:118 button btnSelPercent "Select" width:btnWidth height:btnHeight offset:[-72,-1] tooltip:"Reduce selection-count to percentage of current selection" button btnSelFromFile "Select From File" width:116 across:3 tooltip:"Select objects named in a textfile" button btnSelRotated "Select Rotated" width:116 tooltip:"Select objects with rotation" button btnSelScaled "Select Scaled" width:116 tooltip:"Select objects with scaling" local idxPriority = GetAttrIndex "Gta Object" "Priority" on btnSelPercent pressed do ( -- Randomise list: local randomisedItems = for obj in selection collect (dataPair obj:obj num:(random 0 10000)) fn sortByNum v1 v2 = (v1.num - v2.num) qsort randomisedItems sortByNum -- Get subset of randomised object-list: local selCount = (spinPercent.value / 100.0) * randomisedItems.count local newSel = for n = 1 to selCount collect randomisedItems[n].obj RsSelectObjs newSel ) on btnSelRotated pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local objsel = for obj in considerSet where (not isIdentity obj.rotation) collect obj RsSelectObjs objSel ) on btnSelScaled pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local objsel = for obj in considerSet where (distance obj.scale [1,1,1] > 0.0001) collect obj RsSelectObjs objSel ) fn getVolume obj = ( local boundbox = (obj.max - obj.min) / 2.0 return (length boundbox) ) on btnSelBBLarger pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local selset = for obj in considerSet where (getVolume obj > spinBoundBox.value) collect obj RsSelectObjs selSet ) on btnSelBBSmaller pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local selset = for obj in considerSet where (getVolume obj < spinBoundBox.value) collect obj RsSelectObjs selSet ) on btnSelPriorityLarger pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selSet = for obj in considerSet where (getattr obj idxPriority) >= spinPriority.value collect obj RsSelectObjs selSet ) on btnSelPrioritySmaller pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selSet = for obj in considerSet where (getattr obj idxPriority) <= spinPriority.value collect obj RsSelectObjs selSet ) on btnSelPriorityExact pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selSet = for obj in considerSet where (getattr obj idxPriority) == spinPriority.value collect obj RsSelectObjs selSet ) on btnSelPolyLarger pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local selset = for obj in considerSet where (isProperty obj #mesh) and (getNumFaces obj.mesh) > spinPolyCount.value collect obj RsSelectObjs selset ) on btnSelPolySmaller pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local selset = for obj in considerSet where (isProperty obj #mesh) and (getNumFaces obj.mesh) < spinPolyCount.value collect obj RsSelectObjs selset ) on btnSelFromFile pressed do ( local filename = getopenfilename caption:"list file" types:"Txt (*.txt)|*.txt|" if filename == undefined do return false local fileHandle = openfile filename if fileHandle == undefined then ( messagebox ("Could not open file" + filename) title:"Textfile load failed" ) else ( local considerSet = RsSelToolGeneralRoll.objectList() local selObjs = #() while not eof fileHandle do ( local currLine = trimRight (trimLeft (readline fileHandle)) local getObjs = getNodeByName currLine all:true for obj in getObjs where (findItem considerSet obj) != 0 do ( append selObjs obj ) ) RsSelectObjs selObjs ) ) ) rollout RsSelToolIplRoll "IPL Selection" ( local groupNames, objLists dropdownlist listIplGroup width:290 across:2 button btnRefresh "Refresh" align:#right button btnSelGroup "Select by" offset:[-14,0] across:3 width:90 button btnAdd2SelGroup "Add to Selection" offset:[-36,0] width:90 button btnColourGroup "Colour wire by" offset:[-58,0] width:90 button buttonAppend "Append To Selected:" offset:[55,0] across:2 edittext appendVal "" fieldWidth:50 offset:[30,2] fn refresh = ( local prevSel = listIplGroup.selected objLists = #() groupNames = RsGetUniqueNames "IPL Group" objListsOut:objLists includeRefs:true listIplGroup.items = groupNames listIplGroup.selection = findItem groupNames prevSel ) on btnColourGroup pressed do ( refresh() RsColourByFieldValueString objLists ) on btnSelGroup pressed do ( refresh() local selGrp = listIplGroup.selection local considerSet = RsSelToolGeneralRoll.objectList() local selObjs = if (selGrp == 0) then #() else ( for obj in objLists[selGrp] where (findItem considerSet obj) != 0 collect obj ) RsSelectObjs selObjs ) on btnAdd2SelGroup pressed do ( refresh() local currentSelection = $selection as array local selGrp = listIplGroup.selection local considerSet = RsSelToolGeneralRoll.objectList() local selObjs = if (selGrp == 0) then #() else ( for obj in objLists[selGrp] where (findItem considerSet obj) != 0 collect obj ) RsSelectObjs selObjs selectMore currentSelection completeRedraw() ) on buttonAppend pressed do ( RsAppendToValueString "IPL Group" appendVal.text refresh() ) on btnRefresh pressed do (refresh()) on RsSelToolIplRoll open do (refresh()) ) rollout RsSelToolTxdRoll "TXD Names:" ( local txdNames, txdObjs dropdownlist listTxdGroup width:290 across:2 button btnRefresh "Refresh" align:#right button btnSelGroup "Select by TXD" offset:[48,-2] across:2 button btnColourGroup "Colour wire by TXD" offset:[-38,-2] checkBox includeRefchk "Include RSRefs" offset:[0,-25] button buttonAppend "Suffix Selected:" offset:[53,2] tooltip:"Append suffix to selected objects' TXDs" across:2 edittext appendVal "" fieldWidth:50 offset:[10,4] fn refresh = ( txdNames = #() txdObjs = #() RsGetTxdList rootnode.children txdNames txdObjs includeRefs:includeRefchk.checked sorted:True listTxdGroup.items = txdNames ) on includeRefchk changed args do ( refresh() ) on btnColourGroup pressed do ( refresh() RsColourByFieldValueString txdObjs ) on btnSelGroup pressed do ( if (listTxdGroup.selection != 0) do ( local considerSet = RsSelToolGeneralRoll.objectList() local selObjs = for obj in txdObjs[listTxdGroup.selection] where (findItem considerSet obj) != 0 collect obj RsSelectObjs selObjs ) ) on buttonAppend pressed do ( RsAppendToValueString "TXD" appendVal.text refresh() ) on btnRefresh pressed do (refresh()) on RsSelToolTxdRoll open do (refresh()) ) rollout RsSelToolAreaCodeRoll "Area Code" ( fn CreateAreaCodeList = ( local areacodelist = #() append areacodelist "Main map" for i = 1 to 12 do ( append areacodelist ("Interior" + (i as string)) ) append areacodelist "Always Drawn" for i = 13 to 17 do ( append areacodelist ("Interior" + (i as string)) ) return areacodelist ) local allAreaCodes = CreateAreaCodeList() dropdownlist listAreaCodes items:allAreaCodes button buttonAreaCode "Select by" offset:[-50,0] button buttonColourAreaCode "Colour wire by" offset:[30,-27] fn colourByFieldValueNumber objLists = ( local colourlist = #((color 255 255 255),(color 255 0 0),(color 0 255 0),(color 0 0 255),(color 255 255 0),(color 0 255 255),(color 255 0 255)) RsColourByGroup objLists colourList ) fn selectByFieldValueNumber fieldname fieldvalue = ( if fieldvalue == undefined then ( return 0 ) selset = #() idxObjectField = GetAttrIndex "Gta Object" fieldname idxAnimHierField = GetAttrIndex "GtaAnimHierarchy" fieldname considerSet = RsSelToolGeneralRoll.objectList() for obj in considerSet do ( idxField = -1 if GetAttrClass obj == "Gta Object" then ( idxField = idxObjectField ) if GetAttrClass obj == "GtaAnimHierarchy" then ( idxField = idxAnimHierField ) if idxField != -1 then ( fieldCompare = GetAttr obj idxField if fieldCompare == fieldvalue then ( append selset obj ) ) ) RsSelectObjs selSet ) on buttonColourAreaCode pressed do ( colourByFieldValueNumber "Area Code" allAreaCodes ) on buttonAreaCode pressed do ( selectByFieldValueNumber "Area Code" (listAreaCodes.selection - 1) ) ) rollout RsDupeSelector "Duplicate Selection" ( button btnSearch "Search For Duplicates" align:#center across:2 width:160 checkbox chkSelectDupes "Select Result" align:#left checked:true edittext txtSetName "Set Name:" align:#left width:160 text:"R* duplicates" across:2 checkbox chkAddToSelSet "Add To Selection Set" align:#left checked:true --functions fn CompareObject objA objB = ( (objA != objB) and -- Same object? (objA.pos == objB.pos) and -- Same position? (isKindOf objA (classof objB)) and -- Same class? ( ((isKindOf objA Editable_Mesh) or (isKindOf objA Editable_Poly)) -- Is objA a mesh/poly? ) and ( (objA.numfaces == objB.numfaces) and -- Same number of faces? (objA.numverts == objB.numverts)and -- Same number of verts? ( -- Are all the verts the same? local sameVerts = True for i = 1 to objA.numverts while sameVerts do ( if objA.verts[i].pos != objB.verts[i].pos do (sameVerts = False) ) sameVerts ) ) ) fn FindDuplicates objlist = ( -- Check for geometry repeats -- local objlist = objects as array local dupeObjs = #() local dupes = #{} progressStart "checking repeats" totalCount = objlist.count for i = 1 to objList.count where not dupes[i] while (progressUpdate (100.0 * i / objList.count)) do ( local obj = objlist[i] for j = (i + 1) to totalCount where not dupes[j] do ( objcompare = objlist[j] if (CompareObject obj objcompare) do ( append dupeObjs objcompare dupes[j] = true ) ) ) progressEnd() if (dupeObjs.count == 0) then ( messageBox "No duplicate meshes found" title:"Duplicate Selection" beep:false ) else ( local setName = txtSetName.Text if (chkAddToSelSet.Checked) then ( if (setName == "") then ( setName = "R* duplicates" txtSetName.Text = setName ) selectionsets[txtSetName.Text] = dupeObjs ) if (chkSelectDupes.Checked) do ( RsSelectObjs dupeObjs ) messageBox (dupeObjs.count as string + " duplicates were found.\nDuplicate objects added to selection-set " + setName) title:"Duplicate Selection" ) ) --events on btnSearch pressed do ( -- local considerSet = RsSelToolGeneralRoll.objectList() -- txtSetName.Text = considerSet.count as string FindDuplicates (RsSelToolGeneralRoll.objectList()) ) on chkAddToSelSet changed state do ( if (state) then ( txtSetName.Visible = true ) else ( txtSetName.Visible = false if (not chkSelectDupes.Checked) then chkSelectDupes.Checked = true ) ) on chkSelectDupes changed state do ( if (not state) then ( chkAddToSelSet.Checked = true txtSetName.Visible = true ) ) ) rollout RsRotationSelector "Rotation selection" ( group "Full-Matrix Rotation:" ( checkbox chkFlagSet "'Use Full Matrix' set" checked:True across:4 checkbox chkDynamics "Dynamic props" checked:True offset:[26,0] checkbox chkRotated "Quat X/Y > 0.05" checked:True offset:[30,0] button btnSelChecked "Select" align:#right offset:[4,-3] tooltip:"Select objects that will use full quaternion rotation in-game.\n\n(instead of just z-axis rotation)" ) local btnWidth = 60 local btnHeight = 18 group "Tilt:" ( spinner spnTilt "Tilt Angle:" range:[0,90,5] align:#left width:110 across:3 button btnSelTiltGT "Select > Angle" align:#right width:80 height:btnHeight offset:[-40,-1] button btnSelTiltLT "Select < Angle" align:#left width:80 height:btnHeight offset:[-34,-1] button btnSuggestFullMatrix "( Not Full Matrix ) and ( > Angle )" height:btnHeight align:#left offset:[0,-2] tooltip:"Select tilted objects that won't currently show as tilted in-game" ) local idxDynamic = GetAttrIndex "Gta Object" "Is Dynamic" local idxUseFullMatrix = GetAttrIndex "Gta Object" "Use Full Matrix" fn isFullMatrixObj obj flagged:True dynamics:True rotated:True = ( (GetAttrClass obj == "Gta Object") and ( ( (flagged) and (getAttr obj idxUseFullMatrix) ) or ( (dynamics) and ( (GetAttr obj idxDynamic) or ( (isRsRef obj) and (obj.refDef != undefined) and (obj.refDef.isDynamic) ) ) ) or ( (rotated) and (RsHasAutoFullMatrix obj) ) ) ) on btnSelChecked pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local flagged = chkFlagSet.checked local dynamics = chkDynamics.checked local rotated = chkRotated.checked -- Filter out objects that don't trigger any of the checked criteria: selObjs = for obj in considerSet where (isFullMatrixObj obj flagged:flagged dynamics:dynamics rotated:rotated) collect obj RsSelectObjs selObjs ) on btnSelTiltGT pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local tiltLimit = spnTilt.value selObjs = for obj in considerSet where ((RsGetObjTilt obj) > tiltLimit) collect obj RsSelectObjs selObjs ) on btnSelTiltLT pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local tiltLimit = spnTilt.value selObjs = for obj in considerSet where ((RsGetObjTilt obj) < tiltLimit) collect obj RsSelectObjs selObjs ) on btnSuggestFullMatrix pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local tiltLimit = spnTilt.value selObjs = for obj in considerSet where (not isFullMatrixObj obj) and ((RsGetObjTilt obj) > tiltLimit) collect obj RsSelectObjs selObjs ) ) rollout RsSelToolLodRoll "LOD Value" ( groupBox grpLodDist "Select by:" width:84 height:64 offset:[7, 0] across:2 spinner spnMinLod "Distance:" range:[0,4000,0] align:#left offset:[-80,3] fieldwidth:40 type:#integer spinner spnMaxLod "Maximum:" range:[0,4000,0] pos:(spnMinLod.pos + [-88, 20]) fieldwidth:40 type:#integer enabled:False checkButton chkLodRange "Range" pos:(spnMaxLod.pos + [16,-3]) button btnLodValue "Lod Distance" pos:(grpLodDist.pos + [4,16]) width:76 button btnChildLodValue "Child Lod Dist." pos:(btnLodValue.pos + [0, 23]) width:76 label dummyA "" offset:[0,-14] button buttonColourLODHierarchy "Colour Wire by Lod" across:3 button btnSelNoChildren "Select With No Children" offset:[4,0] button btnSelUnlodded "Select Unlodded" fn colourlodchildren parent level colmultiple = ( parent.wirecolor = (color 255 0 0) + (level * (color 0 colmultiple colmultiple)) local childnodes = getLODChildren parent for child in childnodes do ( colourlodchildren child (level + 1) colmultiple ) ) on spnMinLod changed val do ( if (chkLodRange.checked) or (val > spnMaxLod.value) do ( spnMaxLod.value = val ) ) on spnMaxLod changed val do ( if (val < spnMinLod.value) do ( spnMinLod.value = val ) ) on chkLodRange changed state do ( spnMinLod.text = if state then "Minimum:" else "Distance:" spnMaxLod.enabled = state if (not state) do ( spnMaxLod.value = spnMinLod.value ) ) on buttonColourLODHierarchy pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() for obj in considerSet do ( parentnode = RsSceneLink.getparent LinkType_LOD obj childnodes = getLODChildren obj if parentnode == undefined do ( if childnodes.count == 0 then ( obj.wirecolor = (color 0 0 255) ) else ( maxlevels = getspnMaxLodLevels obj maxlevels = maxlevels - 1 colmultiple = 255 / maxlevels colourlodchildren obj 0 colmultiple ) ) ) ) on btnSelNoChildren pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selSet = for obj in considerSet where ((getLODChildren obj).count == 0) collect obj RsSelectObjs selSet ) on btnSelUnlodded pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selSet = for obj in considerSet where ( ((getLODChildren obj).count == 0) and (RsSceneLink.getparent LinkType_LOD obj == undefined) ) collect obj RsSelectObjs selSet ) on btnLODvalue pressed do ( local selset = #() local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" for obj in considerSet do ( local fieldValue = RsGetObjLodDistance obj if (fieldValue >= spnMinLod.value) and (fieldValue <= spnMaxLod.value) do ( append selset obj ) ) RsSelectObjs selSet ) on btnChildLodValue pressed do ( local selset = #() local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" for obj in considerSet do ( local fieldValue = getattr obj idxChildLodDistance if (fieldValue >= spnMinLod.value) and (fieldValue <= spnMaxLod.value) do ( append selset obj ) ) RsSelectObjs selSet ) ) rollout RsSelToolDrawLodRoll "Drawable LOD" ( button btnSelHD "Select HD models" width:120 tooltip:"Select HD objects (with drawable-lods)" offset:[-4,0] across:3 button btnSelL1 "Select L1 models" tooltip:"Select L1 drawable-lods" width:120 button btnSelL2 "Select L2 models" tooltip:"Select L2 drawable-lods" width:120 offset:[4,0] fn getDrawableChilds obj = ( local getObjs = #() RsSceneLink.GetChildren LinkType_DRAWLOD obj &getObjs return getObjs ) fn getDrawableParent obj = ( RsSceneLink.GetParent LinkType_DRAWLOD obj ) on btnSelHD pressed do ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selset = for obj in considerSet where ( ((getDrawableParent obj) != undefined) and ((getDrawableChilds obj).count == 0) ) collect obj RsSelectObjs selSet ) on btnSelL1 pressed do ( local selset = #() local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" for obj in considerSet do ( local objChilds = getDrawableChilds obj if (objChilds.count != 0) do ( if ((getDrawableChilds objChilds[1]).count == 0) do ( append selSet obj ) ) ) RsSelectObjs selSet ) on btnSelL2 pressed do ( local selset = #() local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" for obj in considerSet do ( local objChilds = getDrawableChilds obj if (objChilds.count != 0) do ( local objChildChilds = getDrawableChilds objChilds[1] if (objChildChilds.count != 0) do ( if ((getDrawableChilds objChildChilds[1]).count == 0) do ( append selSet obj ) ) ) ) RsSelectObjs selSet ) ) rollout RsSelToolProcAttrRoll "Procedural Attribute" ( button btnSelAllWithProc "Select Procedural ColMeshes" width:170 across:2 tooltip:"Select collision-meshes that have procedural materials" button btnSelProceduralFaces "Select Procedural Faces" width:170 tooltip:"Select faces on selected collision-mesh that have procedural materials" on btnSelProceduralFaces pressed do ( if selection.count != 1 do ( messagebox "please select just one object" return false ) local obj = selection[1] if classof obj != Col_Mesh do ( messagebox "only works on a collision mesh" return false ) col2mesh obj facesout = #() RsGetProceduralFaces obj obj.material facesout select obj -- mesh2col obj setCommandPanelTaskMode mode:#modify subobjectlevel = 4 setfaceselection obj facesout ) on btnSelAllWithProc pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local selSet = for obj in considerSet where (isKindOf obj Col_Mesh) and (RsGetDoesObjUseProcedurals obj obj.material) collect obj RsSelectObjs selSet ) ) rollout RsSelToolIrefsRoll "Internal Refs" ( local irefSources, irefObjs group "Select from Iref group:" ( button btnSelIRefs "Irefs:" width:80 align:#left offset:[-4,-3] dropdownlist lstIrefObjs width:232 offset:[80,-14] across:2 button btnRefresh "Refresh" pos:(lstIrefObjs.pos + [lstIrefObjs.width + 2,0]) button btnSelSrc "Source:" width:80 align:#left pos:(btnSelIRefs.pos + [0,24]) ) button btnSelAllIRefs "Select All Irefs" fn refresh = ( irefSources = #() irefObjs = #() for obj in objects where isInternalRef obj do ( local src = getIRefSource obj local irefIdx = findItem irefSources src if (irefIdx == 0) do ( if (src == undefined) then ( insertItem undefined irefSources 1 insertItem #() irefObjs 1 irefIdx = 1 ) else ( append irefSources src append irefObjs #() irefIdx = irefSources.count ) ) append irefObjs[irefIdx] obj ) local itemNames = for n = 1 to irefSources.count collect ( local itemName = if (irefSources[n] == undefined) then "[unassigned]" else irefSources[n].name itemName + (" (" + irefObjs[n].count as string + " objs)") ) lstIrefObjs.items = itemNames ) on btnRefresh pressed do (refresh()) on btnSelSrc pressed do ( if (lstIrefObjs.selection != 0) do ( clearSelection() if isValidNode irefSources[lstIrefObjs.selection] do ( RsSelectObjs irefSources[lstIrefObjs.selection] ) ) ) on btnSelIRefs pressed do ( if (lstIrefObjs.selection != 0) do ( local considerSet = RsSelToolGeneralRoll.objectList() local selObjs = for obj in irefObjs[lstIrefObjs.selection] where (findItem considerSet obj != 0) collect obj RsSelectObjs selObjs ) ) on btnSelAllIRefs pressed do ( local selObjs = for obj in (RsSelToolGeneralRoll.objectList()) where isInternalRef obj collect obj RsSelectObjs selObjs ) on RsSelToolIrefsRoll open do (refresh()) ) ------------------------------------------------------------------------------------------- -- RsRefs Rollout ------------------------------------------------------------------------------------------- -- Refs Selection Rollout rollout RsSelToolRsRefsRoll "RsRefs" ( ------------------------------------------------------------------------------- -- User Interface ------------------------------------------------------------------------------- local attrBtnWidth = 90 group "Select by attribute:" ( button btnSelRefs "Select RsRefs:" width:80 tooltip:"Select RsRef objects matching filter" across:2 align:#left dropdownlist lstPropType "" tooltip:"Only select RsRef with specific attributes" align:#left pos:(btnSelRefs.pos + [84,0]) width:(RsSelToolRsRefsRoll.width - 110) button btnSelNonRefs "Select Non-RsRef geometry" tooltip:"Select all non-RsRef geometry-objects" align:#left ) group "Select by Source LOD Distance:" ( checkBox chkScaleLodDist "Apply Prop Scales to Distances" checked:True align:#center across:2 checkBox chkDrawableDists "Use Drawable LOD-distances" checked:True align:#center button btnSelLTLodDist "Select < Distance" width:attrBtnWidth offset:[-10,0] across:3 tooltip:"Select RsRefs with Source LOD Distance below or equal to value" spinner spnLodDist "LOD Distance:" range:[0,10000,100] offset:[8,3] button btnSelGTLodDist "Select > Distance" width:attrBtnWidth offset:[10,0] tooltip:"Select RsRefs with Source LOD Distance above or equal to value" button btnListByLodDist "List Props by LOD Distance" tooltip:"Show RsRefs listed by LOD Distance" width:160 ) group "Select by Volume:" ( checkBox chkScaleVols "Apply Prop Scales to Volumes" checked:True align:#center button btnSelLTVol "Select < Volume" width:attrBtnWidth offset:[-10,0] across:3 tooltip:"Select RsRefs with volume below or equal to value" spinner spnVolume "Volume (m^3):" range:[0,100000000,100] offset:[8,3] button btnSelGTVol "Select > Volume" width:attrBtnWidth offset:[10,0] tooltip:"Select RsRefs with volume above or equal to value" button btnListByVolume "List Props by Volume" tooltip:"Show RsRefs listed by volume" width:160 ) local clrBtnWidth = 100 group "Randomise RsRef Colours:" ( button btnRefClrAll "All Different" width:clrBtnWidth across:3 button btnRefClrObjNames "By Objectname" width:clrBtnWidth button btnRefClrFilename "By Filename" width:clrBtnWidth ) group "Misc" ( button btnSelForceBakeColRefs "Select veg for 'Force Bake Collision'" \ tooltip:"Selects non-scaled non-dynamic vegetation Rsrefs, which will need to have 'Face Bake Collision' attribute set" ) ------------------------------------------------------------------------------- -- Functions ------------------------------------------------------------------------------- -- Returns volume of prop: fn getVolume obj scaled:chkScaleVols.checked = ( local retVal = if (obj.refDef == undefined) then 0.0 else obj.refDef.volume if scaled do (retVal = retVal * obj.scale.x * obj.scale.y * obj.scale.z) return retVal ) fn gtVol obj = (getVolume obj >= spnVolume.value) fn ltVol obj = (getVolume obj <= spnVolume.value) -- Returns lod-distance for prop: fn getLodDist obj scaled:chkScaleLodDist.checked drawable:chkDrawableDists.checked = ( local objRef = obj.refDef local retVal = case of ( (objRef == undefined):0.0 (drawable and (objRef.drawableLodRefs.count != 0)):(objRef.drawableLodRefs[objRef.drawableLodRefs.count].lodDistance) Default:objRef.lodDistance ) if (retVal == undefined) do (retVal = 0.0) if scaled do (retVal = retVal * obj.scale.z) return retVal ) fn gtDist obj = ( getLodDist obj >= spnLodDist.value ) fn ltDist obj = ( getLodDist obj <= spnLodDist.value ) fn getRefObjs func: getVolumes:False getDistances:False doSelect:False drawable:chkDrawableDists.checked = ( local propFilter local dontFilter = true local filtersList = RsRefFuncs.getFiltersList() local filterNum = lstPropType.selection - 1 -- Get filter-function to use on RsRefs: if (filterNum > 0) do ( propFilter = filtersList[lstPropType.selection - 1].filter dontFilter = false ) local considerSet = RsSelToolGeneralRoll.objectList() local selObjs = for obj in considerSet where (isRefObj obj) collect obj if (not dontFilter) do ( selObjs = for obj in selObjs where ((obj.refDef != undefined) and (propFilter obj.refDef)) collect obj ) if (getDistances or getVolumes) and (RsRefFuncs.databaseActive toolName:"Object Selector") do ( local refDefs = makeUniqueArray (for obj in selObjs collect obj.refDef) case of ( getDistances: ( local distRefDefs = #() if drawable do ( -- Get last drawable-ref for each ref: for refDef in refDefs where (refDef != undefined) do ( append distRefDefs refDef local drawables = refDef.drawableLodRefs if (drawables.count != 0) do ( append distRefDefs drawables[drawables.count] ) ) ) RsRefFuncs.loadLodDistances distRefDefs ) getVolumes: ( RsRefFuncs.loadBoundingBoxes refDefs:refDefs ) ) ) if (func != unsupplied) do ( selObjs = for obj in selObjs where (func obj) collect obj ) if doSelect do ( RsSelectObjs selObjs ) return selObjs ) fn selNonRefs = ( local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Object" local selObjs = for obj in considerSet where not (isRefObj obj includeDelegates:true) collect obj RsSelectObjs selObjs ) -- Called when list is double-clicked: fn listDoubleClick Sender Args = ( if (args.RowIndex >= 0) do ( local objName = Sender.Rows.Item[args.RowIndex].Cells.Item[2].Value RsSelectObjs (getNodeByName objName all:True) ) ) ------------------------------------------------------------------------------- -- Event Handlers ------------------------------------------------------------------------------- on btnSelRefs pressed do (getRefObjs doSelect:True) on btnSelNonRefs pressed do (selNonRefs()) on btnSelLTVol pressed do (getRefObjs doSelect:True func:ltVol getVolumes:True) on btnSelGTVol pressed do (getRefObjs doSelect:True func:gtVol getVolumes:True) on btnListByVolume pressed do ( local refObjs = getRefObjs getVolumes:True local dataList = for obj in refObjs collect #(getVolume obj, obj.name) local listChks = #{} if (dataList.count != 0) do (listChks = #{1..dataList.count}) local retVal = RsQueryBoxMultiBtn "[double-click to select]" title:"RsRef Prop Volumes" labels:#("Select", "Cancel") timeout:-1 listItems:dataList listCheckStates:listChks listTitles:#("Select?", "Volume (m^3)", "Object Name") listDoubleClickFunc:listDoubleClick if (retVal == 1) do ( RsSelectObjs (for n = listChks collect refObjs[n]) ) ) on btnSelLTLodDist pressed do (getRefObjs doSelect:True func:ltDist getDistances:True) on btnSelGTLodDist pressed do (getRefObjs doSelect:True func:gtDist getDistances:True) on btnListByLodDist pressed do ( local refObjs = getRefObjs getDistances:True local dataList = for obj in refObjs collect #(getLodDist obj, obj.name) local listChks = #{} if (dataList.count != 0) do (listChks = #{1..dataList.count}) local retVal = RsQueryBoxMultiBtn "[double-click to select]" title:"RsRef Prop LOD Distances" labels:#("Select", "Cancel") timeout:-1 listItems:dataList listCheckStates:listChks listTitles:#("Select?", "LOD Distance (m)", "Object Name") listDoubleClickFunc:listDoubleClick if (retVal == 1) do ( RsSelectObjs (for n = listChks collect refObjs[n]) ) ) fn setObjColours objLists = ( local usedColours = #() for objList in objLists do ( local newClr = RsGetRandomColour excludes:usedColours objList.wireColor = newClr append usedColours newClr ) ) on btnRefClrAll pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local useObjs = for obj in considerSet where isRsRef obj collect obj local objLists = for obj in useObjs collect #(obj) setObjColours objLists ) on btnRefClrObjNames pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local useObjs = for obj in considerSet where isRsRef obj collect obj local objNames = #() local objLists = #() for obj in useObjs do ( local findNum = findItem objNames obj.objectName if findNum == 0 do ( append objNames obj.objectName append objLists #() findNum = objLists.count ) append objLists[findNum] obj ) setObjColours objLists ) on btnRefClrFilename pressed do ( local considerSet = RsSelToolGeneralRoll.objectList() local useObjs = for obj in considerSet where isRsRef obj collect obj local objFilenames = #() local objLists = #() for obj in useObjs do ( local findNum = findItem objFilenames obj.filename if findNum == 0 do ( append objFilenames obj.filename append objLists #() findNum = objLists.count ) append objLists[findNum] obj ) setObjColours objLists ) on btnSelForceBakeColRefs pressed do ( if not RsRefFuncs.DatabaseActive toolName:"Object Selector" do return False -- Get rsrefs from scene local considerSet = RsSelToolGeneralRoll.ObjectList() local objs = for obj in considerSet where (IsKindOf obj RsrefObject) collect obj -- Ignore non-vegetation props objs = for obj in objs where (MatchPattern obj.filename pattern:"*vegetation*") collect obj -- Ignore dynamics objs = for obj in objs where not obj.refDef.isDynamic collect obj -- Ignore scaled nodes objs = for obj in objs where (Distance obj.scale [1,1,1] < 0.001) collect obj -- Edit selection RsSelectObjs objs ) on RsSelToolRsRefsRoll open do ( local propFilterNamesList = #("All") join propFilterNamesList (for item in (RsRefFuncs.getFiltersList()) collect item.name) lstPropType.items = propFilterNamesList ) ) -- Refs Selection Rollout Rollout RsSelToolRefsRoll "All Refs" ( Group "Select floating props:" ( Spinner SpnFloatDist "Float Distance:" Value:[0,10,0.01] Scale:0.01 FieldWidth:40 Align:#Right Tooltip:"Select Rsref/Internalref objects that are floating at least this far above a 'ground' object (a non-lodded, unreferenced, collisioned object) or aren't above any ground." Across:2 Button BtnSelFloaters "Select Props" Align:#Left Offset:[6,-3] Tooltip:"Select Rsref/Internalref objects that are floating too far above a 'ground' object (a non-lodded, unreferenced, collisioned object) or aren't above any ground." ) -- GetFloatingProps: -- 'FloatDist' is the minimum distance an object must be from a ground-object for it to be considered to be floating. -- 'ConsiderSet' is the list of objects that prop-list will be restricted to. fn GetFloatingProps FloatDist:0.01 ConsiderSet: = ( -- Was a 'ConsiderSet' supplied? local NoConsiderSet = (not isKindOf ConsiderSet Array) local PropObjs = #() local GroundObjs = #() -- Process scene's geometry-list: for Obj in Geometry do ( case of ( -- Skip objects that have lod-children: ((RsGetLodChildren Obj).Count != 0): () -- Do nothing -- Collect ref-objects: ((isRefObj Obj includeDelegates:False) or (isInternalRef Obj)): ( -- Only collect objects found in the ConsiderSet, if given: if NoConsiderSet or (FindItem ConsiderSet Obj != 0) do ( append PropObjs Obj ) ) -- Skip objects that don't have collision: ( local NoCollision = True for Child in Obj.Children while NoCollision do ( NoCollision = (GetAttrClass Child != "Gta Collision") ) -- Returns 'True' if obj has no collision-children NoCollision ): () -- Do nothing -- Collect objects that props will be tested against: Default: ( append GroundObjs Obj ) ) ) -- Return empty array if no props were found: if (PropObjs.Count == 0) do return #() -- Return all props if no ground was found if (PropObjs.Count == 0) do return PropObjs -- Collect list of objects, plus a point on the top of each prop's bounding-box: struct PropData (Obj, TopPos, Height, MinFloatDist, IsFloating = True) local PropInfoList = for PropObj in PropObjs collect ( PropData Obj:PropObj TopPos:[PropObj.Pos.X, PropObj.Pos.Y, PropObj.Max.Z] Height:(PropObj.Max.Z - PropObj.Min.Z) ) -- Process ground-objects in turn: for GroundObj in GroundObjs do ( local GroundMin = GroundObj.Min local GroundMax = GroundObj.Max -- Find props that may be above this ground-object: -- (skip props that have already been confirmed as grounded) local CheckProps = for Item in PropInfoList where Item.IsFloating collect ( local PropPos = Item.TopPos -- Is prop above base of ground's bounding-box? local CollectObj = case of ( (PropPos.Z < GroundMin.Z):False (PropPos.X < GroundMin.X):False (PropPos.X > GroundMax.X):False (PropPos.Y < GroundMin.Y):False (PropPos.Y > GroundMax.Y):False Default:True ) -- Collect item is prop was over the base of ground-object's bounding-box: if CollectObj then Item else DontCollect ) -- If ground potentially has objects floating over it, test them against its surface: if (CheckProps.Count != 0) do ( -- Build RayMesh for this ground-object: local RayMesh = RayMeshGridIntersect() RayMesh.Initialize 10 RayMesh.AddNode GroundObj RayMesh.BuildGrid() for PropItem in CheckProps do ( -- Item.IsFloating = False -- Cast ray down to see if ground is nearby: local RayHits = RayMesh.IntersectRay (PropItem.TopPos) [0,0,-1] True -- Check ray-hits against prop's minimum-float distance: if (RayHits != 0) do ( for HitIdx = 1 to RayHits do ( local HitDist = (RayMesh.GetHitDist HitIdx) -- Collect distance if it's closer than previously-found hits: if (PropItem.MinFloatDist == undefined) or (HitDist < PropItem.MinFloatDist) do ( PropItem.MinFloatDist = HitDist ) ) -- Set prop as non-floating if its float-distance is less than 'FloatDist', -- or if its bounding-box is embedded in the ground: PropItem.IsFloating = ((PropItem.MinFloatDist - PropItem.Height) > FloatDist) ) ) --format "%: %\n" GroundObj.Name CheckProps.count-- (CheckProps as string) ) ) local FloatProps = for PropItem in PropInfoList where (PropItem.IsFloating) collect PropItem.Obj return FloatProps ) on BtnSelFloaters pressed do ( -- Set 'ConsiderSet' to unsupplied if we don't want to bother filtering to selection: local ConsiderSet = if (RsSelToolGeneralRoll.IsFromSelected()) then (RsSelToolGeneralRoll.ObjectList()) else Unsupplied -- Find props floating over scenery: local FloatingProps = GetFloatingProps FloatDist:SpnFloatDist.Value ConsiderSet:ConsiderSet Clearselection() Select FloatingProps ) ) ------------------------------------------------------------------------------------------- -- Lights Rollout ------------------------------------------------------------------------------------------- rollout RsSelToolLightsRoll "Lights" ( button btnSelDay "Select Day Lights" align:#left across:3 button btnSelNight "Select Night Lights" align:#centre offset:[-24,0] button btnSelShadows "Select Lights With Shadows" align:#right button btnSelVolume "Select Vol Lights > Multiplier:" align:#left across:2 spinner spnMultiplier "" range:[0,1000,0] align:#left width:60 offset:[-26,2] button btnSelAttenLess "Select Attenuation <" align:#left across:3 spinner spnAttenuation "" range:[0,1000,0] align:#left width:60 offset:[-2,2] button btnSelAttenMore "Select Attenuation >" align:#left offset:[-56,0] button btnRename "Rename Lights" align:#left tooltip:"Rename lights to include Day/Night/Shadow labels" local lightClassStructs -- Lights have multiple classes, this allows quick access to indexes for both: fn setClassStructs = ( struct lightIdxsStruct ( class, objs = #(), idxDay, idxNight, idxStatShad, idxDynShad, idxVolDraw, idxVolSize ) lightClassStructs = for lightClass in #("Gta Light", "Gta LightPhoto") collect ( local idxStruct = lightIdxsStruct class:lightClass idxStruct.idxDay = getattrindex lightClass "Day" idxStruct.idxNight = getattrindex lightClass "Night" idxStruct.idxStatShad = getattrindex lightClass "Cast Static Object Shadow" idxStruct.idxDynShad = getattrindex lightClass "Cast Dynamic Object Shadow" idxStruct.idxVolDraw = getattrindex lightClass "Volume Drawing" idxStruct.idxVolSize = getattrindex lightClass "Volume Size" idxStruct ) return lightClassStructs ) -- Get objects by light-class: fn getLightStructs = ( local considerList = RsSelToolGeneralRoll.objectList() for item in lightClassStructs do (item.objs = #()) for obj in considerList do ( local notLight = true local objClass = getattrclass obj for item in lightClassStructs while notLight where (objClass == item.class) do ( append item.objs obj notLight = false ) ) return lightClassStructs ) on btnSelDay pressed do ( getLightStructs() local selObjs = #() for item in lightClassStructs do ( local idx = item.idxDay local objs = for obj in item.objs where (getattr obj idx == true) collect obj join selObjs objs ) RsSelectObjs selObjs ) on btnSelNight pressed do ( getLightStructs() local selObjs = #() for item in lightClassStructs do ( local idx = item.idxNight local objs = for obj in item.objs where (getattr obj idx == true) collect obj join selObjs objs ) RsSelectObjs selObjs ) on btnSelShadows pressed do ( getLightStructs() local selObjs = #() for item in lightClassStructs do ( local idxA = item.idxStatShad local idxB = item.idxDynShad local objs = for obj in item.objs where (getattr obj idxA == true) or (getattr obj idxB == true) collect obj join selObjs objs ) RsSelectObjs selObjs ) on btnSelVolume pressed do ( getLightStructs() local selObjs = #() local spnVal = spnMultiplier.value for item in lightClassStructs do ( local idxA = item.idxVolDraw local idxB = item.idxVolSize local objs = for obj in item.objs where (getattr obj idxA == true) or (getattr obj idxB > spnVal) collect obj join selObjs objs ) RsSelectObjs selObjs ) on btnSelAttenLess pressed do ( getLightStructs() local selObjs = #() local spnVal = spnAttenuation.value for item in lightClassStructs do ( local objs = for obj in item.objs where (hasProperty obj "farattenend") and (obj.farattenend < spnVal) collect obj join selObjs objs ) RsSelectObjs selObjs ) on btnSelAttenMore pressed do ( getLightStructs() local selObjs = #() local spnVal = spnAttenuation.value for item in lightClassStructs do ( local objs = for obj in item.objs where (hasProperty obj "farattenend") and (obj.farattenend > spnVal) collect obj join selObjs objs ) RsSelectObjs selObjs ) on btnRename pressed do ( getLightStructs() for item in lightClassStructs do ( for obj in item.objs do ( local objName = "Light" if getattr obj item.idxDay do (objName += "_day") if getattr obj item.idxNight do (objName += "_night") if (getattr obj item.idxStatShad) or (getattr obj item.idxDynShad) do (objName += "_shadow") objName += "_" obj.name = (uniqueName objName) ) ) ) on RsSelToolLightsRoll open do ( setClassStructs() ) ) rollout RsSelToolShaderRoll "Shaders:" ( ------------------------------------------------------------------------------- -- UI ------------------------------------------------------------------------------- button btnSel "Select objs using:" across:3 align:#left dropdownlist lstAvailableShaders width:190 offset:[-15,0] button btnRefresh "Refresh" align:#right ------------------------------------------------------------------------------- -- Event Handlers ------------------------------------------------------------------------------- fn ObjectList = ( RsSelToolGeneralRoll.ObjectList attrClass:"Gta Object" ) fn refresh = ( -- Get selection from selection rollout, get shaders off those objects local mapShaders = RsGetShaderListForObjects (ObjectList()) sort mapShaders lstAvailableShaders.items = mapShaders ) -- Called whenever RsSelToolGeneralRoll's 'Select From' option is changed: fn SelOptionChanged = ( Refresh() ) on btnSel pressed do ( PushPrompt "Searching for shader..." local considerList = RsSelToolGeneralRoll.objectList() local shaderName = lstAvailableShaders.selected local selObjs = #() -- For each object find objects that use selected shader for obj in considerList do ( local ObjShaders = RsGetShaderListForObjects #(Obj) if (FindItem ObjShaders ShaderName != 0) do ( append selObjs obj ) ) RsSelectObjs selObjs PopPrompt() ) on btnRefresh pressed do ( refresh() ) on RsSelToolShaderRoll open do ( refresh() ) ) rollout RsSelToolMemoryRoll "Memory" ( ------------------------------------------------------------------------------- -- Data ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- UI ------------------------------------------------------------------------------- edittext edtMemorySize "Memory (KB):" width:180 across:2 checkbox chkXRefsIncl "Include Refs" align:#right offset:[0,2] checked:false local btnWidth = 150 group "Select Objects:" ( button btnSelMemGTEQ "Model-size >= [Memory]" width:btnWidth align:#right offset:[-2,0] across:2 button btnSelMemLTEQ "Model-size <= [Memory]" width:btnWidth align:#left offset:[2,0] button btnSelTxdGTEQ "Txd-size >= [Memory]" width:btnWidth align:#right offset:[-2,0] across:2 button btnSelTxdLTEQ "Txd-size <= [Memory]" width:btnWidth align:#left offset:[2,0] button btnSelGTEQ "Model + Txd >= [Memory]" width:btnWidth align:#right offset:[-2,0] across:2 button btnSelLTEQ "Model + Txd <= [Memory]" width:btnWidth align:#left offset:[2,0] progressbar barProgress ) ------------------------------------------------------------------------------- -- Functions ------------------------------------------------------------------------------- -- Get mem size from edit UI controls fn getMemSize = ( memSize = 0 try ( if ( edtMemorySize.text as integer != undefined ) then memSize = ( edtMemorySize.text as integer ) * 1024 ) catch () memSize ) fn getObjMemSizes memSize:true txdSize:true = ( local considerSet = RsSelToolGeneralRoll.objectList() local objSizes = for obj in considerSet collect (dataPair obj:obj size:0) local getSizes = #() if memSize do ( join getSizes (RsAssetDrawableMemSize considerSet inclRefs:chkXRefsIncl.checked progress:barProgress) ) if txdSize do ( join getSizes (RsAssetDrawableMemSize considerSet inclRefs:chkXRefsIncl.checked progress:barProgress) ) for item in getSizes do ( local objNum = findItem considerSet item.obj objSizes[objNum].size += item.size ) return objSizes ) ------------------------------------------------------------------------------- -- Event Handlers ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- -- Drawable ------------------------------------------------------------------------------- on btnSelMemGTEQ pressed do ( local memSize = getMemSize() if ( undefined == memSize or 0 == memSize ) then ( messageBox ("Invalid memory size of " + memSize as string + ". Enter a value in the memory edit box.") title:"Invalid value" return false ) local considerSet = getObjMemSizes memSize:true txdSize:false local selset = for item in considerSet where (item.size != 0) and (item.size >= memSize) collect item.obj RsSelectObjs selSet ) on btnSelMemLTEQ pressed do ( local memSize = getMemSize() if ( undefined == memSize or 0 == memSize ) then ( messageBox ("Invalid memory size of " + memSize as string + ". Enter a value in the memory edit box.") title:"Invalid value" return false ) local considerSet = getObjMemSizes memSize:true txdSize:false local selset = for item in considerSet where (item.size != 0) and (item.size <= memSize) collect item.obj RsSelectObjs selSet ) ------------------------------------------------------------------------------- -- TXD ------------------------------------------------------------------------------- on btnSelTxdGTEQ pressed do ( local memSize = getMemSize() if ( undefined == memSize or 0 == memSize ) then ( messageBox ("Invalid memory size of " + memSize as string + ". Enter a value in the memory edit box.") title:"Invalid value" return false ) local considerSet = getObjMemSizes memSize:false txdSize:true local selset = for item in considerSet where (item.size != 0) and (item.size >= memSize) collect item.obj RsSelectObjs selSet ) on btnSelTxdLTEQ pressed do ( local memSize = getMemSize() if ( undefined == memSize or 0 == memSize ) then ( messageBox ("Invalid memory size of " + memSize as string + ". Enter a value in the memory edit box.") title:"Invalid value" return false ) local considerSet = getObjMemSizes memSize:false txdSize:true local selset = for item in considerSet where (item.size != 0) and (item.size <= memSize) collect item.obj RsSelectObjs selSet ) ------------------------------------------------------------------------------- -- Drawable + Txd ------------------------------------------------------------------------------- on btnSelGTEQ pressed do ( local memSize = getMemSize() if ( undefined == memSize or 0 == memSize ) then ( messageBox ("Invalid memory size of " + memSize as string + ". Enter a value in the memory edit box.") title:"Invalid value" return false ) local considerSet = getObjMemSizes memSize:true txdSize:true local selset = for item in considerSet where (item.size != 0) and (item.size >= memSize) collect item.obj RsSelectObjs selSet ) on btnSelLTEQ pressed do ( local memSize = getMemSize() if ( undefined == memSize or 0 == memSize ) then ( messageBox ("Invalid memory size of " + memSize as string + ". Enter a value in the memory edit box.") title:"Invalid value" return false ) local considerSet = getObjMemSizes memSize:true txdSize:true local selset = for item in considerSet where (item.size != 0) and (item.size <= memSize) collect item.obj RsSelectObjs selSet ) ) --////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- COLLISION --////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// rollout RsSelToolCollClassRoll "Collision Classes:" ( button btnSel "Select Objects:" align:#left dropdownlist lstCollClasses "" width:270 align:#right offset:[0,-26] label lblAlsoFilters "v Also used to filter other tools v" local colClasses = #(ColMesh, ColBox, ColSphere, ColCylinder, ColCapsule) local colClassItems = #( dataPair name:"All" classes:#{1..5}, dataPair name:"* Collision Mesh" classes:#{1}, dataPair name:"* Primitives" classes:#{2..5}, dataPair name:" * Box" classes:#{2}, dataPair name:" * Sphere" classes:#{3}, dataPair name:" * Cylinder" classes:#{4}, dataPair name:" * Capsule" classes:#{5} ) fn getSelClasses = ( local selNum = lstCollClasses.selection for classNum = colClassItems[selNum].classes collect colClasses[classNum] ) fn objectList = ( local selClasses = getSelClasses() local considerSet = RsSelToolGeneralRoll.objectList attrClass:"Gta Collision" for obj in considerSet where (findItem selClasses (classOf obj) != 0) collect obj ) on btnSel pressed do ( RsSelectObjs (objectList()) ) on RsSelToolCollClassRoll open do ( lstCollClasses.items = for item in colClassItems collect item.name ) ) rollout RsSelToolCollGrpRoll "Collision Group:" ( local groupNames, objLists button buttonTxdGroup "Select by:" offset:[0,0] align:#left across:3 dropdownlist listCollGroup width:220 offset:[-40, 0] button btnRefresh "Refresh" align:#left offset:[65, 0] button buttonColourTxdGroup "Colour wire" align:#left offset:[0,0] across:3 button buttonAppend "Append To Selected:" offset:[-40,0] edittext appendVal "" fieldWidth:93 offset:[-40,2] groupbox grpBorder width:290 height:63 offset:[76, -63] fn refresh = ( local prevSel = listCollGroup.selected objLists = #() groupNames = RsGetUniqueNames "Collision Group" objListsOut:objLists listCollGroup.items = groupNames listCollGroup.selection = findItem groupNames prevSel ) on buttonColourTxdGroup pressed do ( refresh() RsColourByFieldValueString objLists ) on buttonTxdGroup pressed do ( refresh() local selGrp = listCollGroup.selection local selObjs = if (selGrp == 0) then #() else ( local considerSet = RsSelToolGeneralRoll.objectList() for obj in objLists[selGrp] where (findItem considerSet obj != 0) collect obj ) RsSelectObjs selObjs ) on buttonAppend pressed do ( RsAppendToValueString "Collision Group" appendVal.text refresh() ) on btnRefresh pressed do (refresh()) on RsSelToolCollGrpRoll open do (refresh()) ) rollout RsSelToolCollFlagRoll "Collision Flags:" ( ------------------------------------------------------------------------------- -- Constants ------------------------------------------------------------------------------- local idxCollType = getAttrIndex "Gta Collision" "Coll Type" groupBox grpFlagsA "Object/Material Attributes:" height:140 width:(RsSelToolCollFlagRoll.width - 8) offset:[-9,0] button btnRefreshLists "Refresh:" tooltip:"Refresh lists to match values used in scene" offset:[-2, 27 - grpFlagsA.height] height:23 across:3 align:#left dropDownList lstSurfTypes "Surface Type:" offset:[-63, 10 - grpFlagsA.height] width:((RsSelToolCollFlagRoll.width / 2) - 42) dropDownList lstProcTypes "Procedural Type:" offset:[2, 10 - grpFlagsA.height] width:((RsSelToolCollFlagRoll.width / 2) - 42) align:#right checkbox chkStairs "Stairs" across:3 --offset:[0, 12 - grpFlagsA.height] checkbox chkNonCover "Not Cover" --offset:[0, 12 - grpFlagsA.height] checkbox chkSeeThrough "See Through" --offset:[0, 12 - grpFlagsA.height] checkbox chkNonClimable "Non Climable" across:3 checkbox chkPath "Path" checkbox chkShootThrough "Shoot Through" checkbox chkNonCameraCollidable "Non Camera Collide" across:3 checkbox chkNoDecal "No Decal" checkBox chkDUMMYA "DUMMY" visible:false checkbox chkUseProcTint "Use Procedural Tint" across:3 checkbox chkIsClothColl "Is Cloth Collision" checkBox chkDUMMYB "DUMMY" visible:false -- Attribute-flag controls, and their matching index in RsCollisionAttrs: local attrCtrls = #( dataPair ctrl:chkStairs idx:(findItem RsCollisionAttrs "Stairs"), dataPair ctrl:chkNonClimable idx:(findItem RsCollisionAttrs "Non Climbable"), dataPair ctrl:chkSeeThrough idx:(findItem RsCollisionAttrs "See Through"), dataPair ctrl:chkShootThrough idx:(findItem RsCollisionAttrs "Shoot Through"), dataPair ctrl:chkNonCover idx:(findItem RsCollisionAttrs "Does Not Provide Cover"), dataPair ctrl:chkPath idx:(findItem RsCollisionAttrs "Path"), dataPair ctrl:chkNonCameraCollidable idx:(findItem RsCollisionAttrs "Non Camera Collidable"), dataPair ctrl:chkNoDecal idx:(findItem RsCollisionAttrs "No Decal"), dataPair ctrl:chkUseProcTint idx:(findItem RsCollisionAttrs "Use Procedural Tint"), dataPair ctrl:chkIsClothColl idx:(findItem RsCollisionAttrs "Is Cloth Collision") ) groupBox grpFlagsB "Object Types:" height:188 width:grpFlagsA.width pos:(grpFlagsA.pos + [0, grpFlagsA.height + 2]) -- TYPE-FILTER BUTTON-NAMES MUST MATCH FLAG-NAMES! -- (case-insensitive, underscores can be spaces) checkBox chkMover "Mover" across:3 offset:[0, 12 - grpFlagsB.height] checkBox chkWeapon "Weapons" offset:[0, 12 - grpFlagsB.height] checkBox chkRiver "River" offset:[0, 12 - grpFlagsB.height] checkBox chkFoliage "Foliage" across:3 checkBox chkHorse "Horse" checkBox chkCover "Cover" checkBox chkVehicle "Vehicle" across:3 checkBox chkStairSlope "Stair Slope" checkBox chkDeepMatSurf "Deep Material Surface" checkBox chkMaterial "Material" -- List of controls used for filtering by collision-type flags: local typeCtrls = #(chkMover, chkWeapon, chkRiver, chkFoliage, chkHorse, chkCover, chkVehicle, chkStairSlope, chkDeepMatSurf, chkMaterial) listBox colObjTypePresetList "Presets: (Double-Click to Select)" height:5 offset:[0,-2] selection:0 label lblAnyAllFlags "Select objects with:" across:2 align:#left offset:[-4,8] radioButtons btnsAnyAllFlags "" labels:#("ANY flags", "ALL flags") default:1 pos:(lblAnyAllFlags.pos + [98,0]) button btnSelObjects "Select Matching Objects" offset:[0,0] width:150 across:2 button btnSelFaces "Select Matching Faces" offset:[0,0] width:150 button btnSelMovObjs "Select 'Mover'" tooltip:"Select non-Weapon Mover-collision" across:3 offset:[-2,0] button btnSelMovWepObjs "Select 'Mover & Weapons'" tooltip:"Select Mover&Weapons-collision" offset:[-6,0] button btnSelWepObjs "Select 'Weapons'" tooltip:"Select non-Mover Weapons-collision" offset:[-3,0] ------------------------------------------------------------------------------- -- Functions ------------------------------------------------------------------------------- fn setGrpsEnabled = ( grpFlagsA.enabled = False for item in attrCtrls while not grpFlagsA.enabled do ( grpFlagsA.enabled = item.ctrl.checked ) grpFlagsB.enabled = False for ctrl in typeCtrls while not grpFlagsB.enabled do ( grpFlagsB.enabled = ctrl.checked ) ) on chkStairs changed state do (setGrpsEnabled()) on chkNonCover changed state do (setGrpsEnabled()) on chkSeeThrough changed state do (setGrpsEnabled()) on chkNonClimable changed state do (setGrpsEnabled()) on chkPath changed state do (setGrpsEnabled()) on chkShootThrough changed state do (setGrpsEnabled()) on chkNonCameraCollidable changed state do (setGrpsEnabled()) on chkNoDecal changed state do (setGrpsEnabled()) on chkUseProcTint changed state do (setGrpsEnabled()) on chkIsClothColl changed state do (setGrpsEnabled()) on chkMover changed state do (setGrpsEnabled()) on chkWeapon changed state do (setGrpsEnabled()) on chkRiver changed state do (setGrpsEnabled()) on chkFoliage changed state do (setGrpsEnabled()) on chkHorse changed state do (setGrpsEnabled()) on chkCover changed state do (setGrpsEnabled()) on chkVehicle changed state do (setGrpsEnabled()) on chkStairSlope changed state do (setGrpsEnabled()) on chkDeepMatSurf changed state do (setGrpsEnabled()) on chkMaterial changed state do (setGrpsEnabled()) -- Set surface/procedural-type lists to show values used in scene: fn refreshLists = ( local colObjs = for obj in objects where (getAttrClass obj == "Gta Collision") collect obj local surfList = #() local procList = #() for obj in colObjs do ( -- For collision-meshes, check materials; for primitives, check attributes if (isKindOf obj Col_Mesh) then ( -- Get list of collision-materials on object: local objColMats = RSgetMatClassSubMats obj.material RexBoundMtl local objSurfs = for mat in objColMats collect (toUpper (RexGetCollisionName mat)) local objProcs = for mat in objColMats collect (toUpper (RexGetProceduralName mat)) join surfList objSurfs join procList objProcs ) else ( -- There's no Procedural Type attribute for primitives... append surfList (toUpper (getAttr obj idxCollType)) ) ) surfList = makeUniqueArray surfList procList = makeUniqueArray procList for thisArray in #(surfList, procList) do ( sort thisArray insertItem "ANY" thisArray 1 ) lstSurfTypes.items = surfList lstProcTypes.items = procList return OK ) -- Get flag-integer from current attribute-checkbox states: fn getAttrCtrlFlags justMat:false = ( local colFlags = 0 -- "justMat" limits flagging to material-flags: for ctrlNum = 1 to attrCtrls.count where (not justMat) or (ctrlNum <= RsCollisionFlags.count) do ( local item = attrCtrls[ctrlNum] colFlags = bit.set colFlags item.idx item.ctrl.state ) return colFlags ) -- List the collision Geometry attributes fn getGtaCollisionGeoAttributes obj flags matchAll:false = ( fn getCollGeoAttr obj attr = ( theIdx = GetAttrIndex "Gta Collision" attr theReturn = (GetAttr obj theIdx) theReturn ) local hasFlag = false local geoFlags = #( datapair idx:1 attrib:"Stairs", datapair idx:2 attrib:"Non Climable", datapair idx:3 attrib:"See Through", datapair idx:9 attrib:"No Decal" ) local flagArray = #() for i in geoFlags do ( flagVal = bit.get flags i.idx if flagVal == true then ( if (getCollGeoAttr obj i.attrib) == true then append flagArray 1 else append flagArray 0 ) ) if finditem flagArray 1 != 0 then hasFlag = true if matchAll == true then if finditem flagArray 0 != 0 then hasFlag = false hasFlag ) -- Returns bitarray of object-numbers for matching objects: fn filterByCollAttrs objs matchAll:false = ( local flags = getAttrCtrlFlags() local retVal = #{} retVal.count = objs.count if (flags != 0) do ( for n = 1 to objs.count do ( local obj = objs[n] if classof obj == Col_Mesh then retVal[n] = (RsDoesObjectUseFlags obj flags matchAll:matchAll) else retVal[n] = (getGtaCollisionGeoAttributes obj flags matchAll:matchAll) ) ) return retVal ) -- Returns bitarray of object-numbers for matching objects: fn filterByCollTypes objs matchAll:false = ( local retVal = #{} retVal.count = objs.count -- Build string containing button-names: local typeFlagsString = "" for ctrl in typeCtrls do ( if ctrl.state do ( typeFlagsString += ctrl.text typeFlagsString += "|" ) ) -- Generate flags-bitarray from button-names string: local getTypes = (gRsCollTypes.getFlagNumsFromString typeFlagsString) --format "getTypes: %\n" (getTypes as string) if (getTypes.numberSet != 0) do ( for n = 1 to objs.count do ( local obj = objs[n] -- Get type-flags used by object, and match against filter: local objTypes = gRsCollTypes.getObjFlagNums obj local matchedCount = (getTypes * objTypes).numberSet --format "getObjFlagNums: %\n" (objTypes as string) local isMatch = true if matchAll then ( -- Did all reported object-flags match against 'getTypes'? isMatch = (matchedCount == objTypes.numberSet) ) else ( -- Did any of object's flags match 'getTypes'? isMatch = (matchedCount != 0) ) if isMatch do ( retVal[n] = true ) ) ) return retVal ) -- Returns bitarray of object-numbers for objects with matching surface OR procedural type -- Attributes are checked for collision-primitives; materials are checked for collsion meshes. fn filterByProcSurfType objs mode:#surf = ( local retVal = #{} retVal.count = objs.count local doSurfMatch = (mode == #surf) local matchCtrl = if doSurfMatch then lstSurfTypes else lstProcTypes if (matchCtrl.selection <= 1) do return retVal -- Get selected surface/procedural-type from control: local matchVal = matchCtrl.selected -- Set appropriate material-command for this mode: local getMatVal = if doSurfMatch then RexGetCollisionName else RexGetProceduralName for n = 1 to objs.count do ( local obj = objs[n] -- For collision-meshes, check materials; for primitives, check attributes if (isKindOf obj Col_Mesh) then ( -- Get list of collision-materials on object: local objColMats = RSgetMatClassSubMats obj.material RexBoundMtl for mat in objColMats while (not retVal[n]) do ( local matVal = getMatVal mat if (matchPattern matVal pattern:matchVal) do ( retVal[n] = True ) ) ) else ( -- There's no Procedural Type attribute for primitives... if doSurfMatch do ( -- Does Collision Type value match? local getVal = getAttr obj idxCollType if (matchPattern getVal pattern:matchVal) do ( retVal[n] = True ) ) ) ) return retVal ) ------------------------------------------------------------------------------- -- Events ------------------------------------------------------------------------------- on btnRefreshLists pressed do ( refreshLists() ) on btnSelObjects pressed do ( -- Get collision-objects, filtered by class: local considerSet = RsSelToolCollClassRoll.objectList() local matchAll = (btnsAnyAllFlags.state == 2) -- Only filter attribs/types if any of their flags are ticked: setGrpsEnabled() local doAttribFilter = grpFlagsA.enabled local doTypeFilter = grpFlagsB.enabled local doSurfFilter = (lstSurfTypes.selection > 1) local doProcFilter = (lstProcTypes.selection > 1) local flagMatches = if doAttribFilter then (filterByCollAttrs considerSet matchAll:matchAll) else #{} local typeMatches = if doTypeFilter then (filterByCollTypes considerSet matchAll:matchAll) else #{} local surfMatches = if doSurfFilter then (filterByProcSurfType considerSet mode:#surf) else #{} local procMatches = if doProcFilter then (filterByProcSurfType considerSet mode:#proc) else #{} -- Get list of used match-lists - inactive searches will not be included in combined match: local useMatchLists = #(doAttribFilter, doTypeFilter, doSurfFilter, doProcFilter) local matchLists = #(flagMatches, typeMatches, surfMatches, procMatches) matchLists = for i = 1 to matchLists.count where useMatchLists[i] collect matchLists[i] local combinedMatches = #{} combinedMatches.count = considerSet.count local firstFilter = True for thisList in matchLists do ( if matchAll and (not firstFilter) then ( combinedMatches *= thisList ) else ( combinedMatches += thisList ) firstFilter = False ) /* if doAttribFilter do (format "Flag Matches: %\n" flagMatches) if doTypeFilter do (format "Type-Flag Matches: %\n" typeMatches) if doSurfFilter do (format "Surface Type Matches: %\n" surfMatches) if doProcFilter do (format "Procedural Type Matches: %\n" procMatches) format "Combined Matches: %\n\n" combinedMatches */ local selectlist = for n = combinedMatches collect considerSet[n] RsSelectObjs selectlist ) fn selMovWepObjs mover:True weapons:True = ( -- Get collision-objects, filtered by class: local considerSet = RsSelToolCollClassRoll.objectList() local selObjs = for obj in considerSet where (gRsCollTypes.hasMoverWeaponsCombo obj mover:mover weapons:weapons) collect obj RsSelectObjs selObjs ) on btnSelMovObjs pressed do (selMovWepObjs mover:True weapons:False) on btnSelWepObjs pressed do (selMovWepObjs mover:False weapons:True) on btnSelMovWepObjs pressed do (selMovWepObjs mover:True weapons:True) -- on btnSelFaces pressed do ( if selection.count != 1 do ( messagebox "Can only be run on one object" title:"Selection error" return False ) obj = selection[1] -- Does this object have collision materials? local colMats = #() if (isProperty obj #material) do ( colMats = RSgetMatClassSubMats obj.material RexBoundMtl ) if (colMats.count == 0) do ( messagebox "Object must have collision materials applied to it!" title:"Selection error" return False ) local surfType = undefined local procType = undefined if (lstSurfTypes.selection > 1) do ( surfType = lstSurfTypes.selected ) if (lstProcTypes.selection > 1) do ( procType = lstProcTypes.selected ) local flagval = getAttrCtrlFlags justMat:true if (flagval == 0) and (surfType == undefined) and (procType == undefined) do ( messagebox "Cancelling: No material-flags selected." title:"Flags error" return false ) -- No need to convert if this is an editable mesh/poly: if (isKindOf obj Col_Mesh) do ( if not querybox "Object will be converted to an Editable Mesh.\n\nCarry on?" title:"Convert to editable?" do ( return False ) ) local matchAll = (btnsAnyAllFlags.state == 2) undo "select collision-faces" on ( select obj -- needed to allow correct collision-convert undo RsSetFacesByFlags obj flagval surfType:surfType procType:procType matchAll:matchAll ) ) on colObjTypePresetList doubleClicked selItem do ( -- Get list of flag-names to tick: local selPresets = filterString (toLower colObjTypePresetList.selected) "|" -- FOR REF, FROM ABOVE: typeCtrls = #(chkMover, chkWeapon, chkRiver, chkFoliage, chkHorse, chkCover, chkVehicle, chkStairSlope, chkDeepMatSurf, chkMaterial) local ctrlNames = for ctrl in typeCtrls collect ctrl.text for n = 1 to typeCtrls.count do ( local ctrlFlagName = toLower typeCtrls[n].text typeCtrls[n].checked = (findItem selPresets ctrlFlagName != 0) ) setGrpsEnabled() ) on RsSelToolCollFlagRoll open do ( -- Get flag-preset names from global list: local flagPresets = for item in gRsCollTypes.typesList collect item.name -- Remove any bracketed comments: flagPresets = for item in flagPresets collect (filterString item "()" splitEmptyTokens:true)[1] colObjTypePresetList.items = flagPresets refreshLists() setGrpsEnabled() ) ) -- Collision Faces Selection Utility rollout RsSelToolCollVolRoll "Collision Faces" ( ------------------------------------------------------------------------------- -- Constants ------------------------------------------------------------------------------- local fEPSILON = 0.0001 ------------------------------------------------------------------------------- -- UI ------------------------------------------------------------------------------- button btnLT "Select Faces <= Ratio" width:120 across:3 tooltip:"Select colmeshes containing faces with smaller size-ratios" spinner spnRatio "Ratio:" range:[0.01, 200.0, 40.0] type:#float width:100 offset:[-20,2] button btnGT "Select Faces >= Ratio" width:120 tooltip:"Select colmeshes containing faces with greater size-ratios" ------------------------------------------------------------------------------- -- Functions ------------------------------------------------------------------------------- -- -- name: localMatrix -- desc: Returns the transformation matrix which will transform a point from -- world into the coordinate system derived from p1-p2 and p1-p3 -- fn localMatrix p1 p2 p3 = ( local v1 = normalize (p2 - p1) local v3 = normalize (cross v1 (normalize (p3 - p1))) local v2 = normalize (cross v3 v1) return matrix3 v1 v2 v3 p1 ) -- -- name: RoundVecZero -- desc: Round float vector's components to zero if they are almost zero - as -- defined by fEPSILON constant above. -- fn RoundVecZero v = ( local p = v if ( p.x < fEPSILON ) and ( p.x > -fEPSILON ) then p.x = 0.0 if ( p.y < fEPSILON ) and ( p.y > -fEPSILON ) then p.y = 0.0 if ( p.z < fEPSILON ) and ( p.z > -fEPSILON ) then p.z = 0.0 return p ) ------------------------------------------------------------------------------- -- Events ------------------------------------------------------------------------------- fn getRatioComparedObjs comparitor = ( local retVal = #() local considerSet = for obj in RsSelToolCollClassRoll.objectList() where (isKindOf obj colMesh) collect obj for obj in considerSet do ( local theMesh = getColMesh obj local facesel = #{} local numFaces = getNumFaces theMesh local selectObj = false for i = 1 to numFaces do ( local face = getFace theMesh i local r = ( getVert theMesh face.x ) local g = ( getVert theMesh face.y ) local b = ( getVert theMesh face.z ) local p3mat = localMatrix r g b local p3inv = inverse p3mat -- Transform points onto a horizontal plane to easily find -- bounds. Only need to consider x and y from now on. local v1 = ( RoundVecZero ( r * p3inv ) ) local v2 = ( RoundVecZero ( g * p3inv ) ) local v3 = ( RoundVecZero ( b * p3inv ) ) -- Get bounds local minx = amin v1.x v2.x v3.x local maxx = amax v1.x v2.x v3.x local miny = amin v1.y v2.y v3.y local maxy = amax v1.y v2.y v3.y local xsize = maxx - minx local ysize = maxy - miny -- Create face selection if ( comparitor ( xsize / ysize ) ) or ( comparitor ( ysize / xsize ) ) do ( facesel[i] = true selectObj = true ) ) if selectObj do ( -- Set face selection --col2Mesh obj --setFaceSelection o facesel append retVal obj ) ) return retVal ) on btnGT pressed do ( undo "select collision by face-size ratio" on ( local selObjects = getRatioComparedObjs (fn compare val = (val >= spnRatio.value)) RsSelectObjs selObjects ) ) -- End of btnGT pressed on btnLT pressed do ( undo "select collision by face-size ratio" on ( local selObjects = getRatioComparedObjs (fn compare val = (val <= spnRatio.value)) RsSelectObjs selObjects ) ) -- End of btnLT pressed ) --////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- /COLLISION --////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -- Collision Volume Selection Utility rollout RsSelToolTexMapRoll "Texturemap Name:" ( ------------------------------------------------------------------------------- -- UI ------------------------------------------------------------------------------- label txtInfo "Enter part of the texture map name to find:\n * Includes extension (e.g. .psd)\n * File path is not searched\n * Can use wildcards (*, ?)" height:52 align:#left edittext txtTexMap "" across:2 width:(RsSelToolTexMapRoll.width - 116) align:#left button btnSel "Select" width:80 offset:[0,-2] align:#right checkbox chkExact "Exact Match (includes manual wildcards)" ------------------------------------------------------------------------------- -- Functions ------------------------------------------------------------------------------- fn texSearch findText = ( local considerSet = for obj in (RsSelToolGeneralRoll.objectList()) where (isKindOf obj GeometryClass) collect obj -- Ignore collision local selObjs = #() if not chkExact.checked do (findText = "*" + findText + "*") local objCount = considerSet.count local doProgress = (objCount > 0) local notCancelled = True if doProgress do ( progressStart "Matching texture-names" ) for n = 1 to objCount while (notCancelled = progressUpdate (100.0 * n / objCount)) do ( local obj = considerSet[n] local texmaplist = #() RsGetTexMapsFromObjNoStrip obj texmaplist #() #() -- Split diffuse/alpha pairs: local texmapsSplit = #() for mapName in texmaplist do (join texmapsSplit (filterString mapName "+")) local notFound = true for mapName in texmapsSplit while notFound do ( if matchPattern (filenameFromPath mapName) pattern:findText do ( append selObjs obj notFound = false ) ) ) if doProgress do ( progressEnd() ) if notCancelled do ( RsSelectObjs selObjs ) return OK ) ------------------------------------------------------------------------------- -- Events ------------------------------------------------------------------------------- on btnSel pressed do ( texSearch txtTexMap.text ) ) -- Object Class Type or Attribute Class Selection rollout RsSelToolClassRoll "Select By Class:" ( ------------------------------------------------------------------------------- -- UI ------------------------------------------------------------------------------- group "Select by Max Class:" ( button btnSelMxClass "Select >" align:#left width:60 across:3 dropDownList lstMxClasses "" align:#center width:(RsSelToolClassRoll.width - 156) button btnRefreshA "Refresh" align:#right width:60 ) group "Select by Attribute Class:" ( button btnSelAttrClass "Select >" align:#left width:60 across:3 dropDownList lstAttrClasses "" align:#center width:(RsSelToolClassRoll.width - 156) button btnRefreshB "Refresh" align:#right width:60 groupBox grpRollAttrSel "Select by single attribute:" height:200 width:(RsSelToolClassRoll.width - 28) editText txtAttrFilter "Filter:" pos:(grpRollAttrSel.pos + [6,16]) width:(grpRollAttrSel.width - 10) subrollout rollAttrSel "" pos:(grpRollAttrSel.pos + [4,36]) height:(grpRollAttrSel.height - 40) width:(grpRollAttrSel.width - 8) label lblPaddingA "" height:4 ) local mxClassObjs, attrClassObjs fn showAttrClassRolls = ( -- Clear current subrollouts: for subRoll in rollAttrSel.rollouts do ( removeSubRollout rollAttrSel subRoll ) local considerSet = RsSelToolGeneralRoll.objectList() local selName = lstAttrClasses.selected if (selName != undefined) do ( local addRolls = RsCreateObjEditRoll #() propClassName:selName rollType:#attr selector:true filterName:txtAttrFilter.text for subRoll in addRolls do ( -- Set new sub-rollout as rolled-up, if the user has previously rolled it up: local rolledUp = RsSettingsReadBoolean subRoll.name "rollup" false addSubRollout rollAttrSel subRoll rolledUp:rolledUp ) ) ) fn refresh = ( local mxClasses = #() local attrClasses = #() mxClassObjs = #() attrClassObjs = #() for obj in objects do ( local mxClass = classOf obj local attrClass = getAttrClass obj local findNum = findItem mxClasses mxClass if (findNum == 0) do ( append mxClasses mxClass append mxClassObjs #() findNum = mxClasses.count ) append mxClassObjs[findNum] obj local findNum = findItem attrClasses attrClass if (findNum == 0) do ( append attrClasses attrClass append attrClassObjs #() findNum = attrClasses.count ) append attrClassObjs[findNum] obj ) local maxClassPrevSel = lstMxClasses.selected local attrClassPrevSel = lstAttrClasses.selected lstMxClasses.items = (for item in mxClasses collect item as string) lstAttrClasses.items = (for item in attrClasses collect item as string) -- Select most-likely classes by default, to minimise clicks -- If refreshing, select the previous selection, if it's still there for ctrlData in #(#(lstMxClasses, maxClassPrevSel, #()), #(lstAttrClasses, attrClassPrevSel, #("Gta Object", "Gta Collision", "RS Container"))) do ( local ctrl = ctrlData[1] local prevSel = ctrlData[2] local defSels = ctrlData[3] local selList = if (prevSel == undefined) then defSels else #(prevSel) local selNum = 0 for item in selList while (selNum == 0) do ( selNum = findItem ctrl.items item ) if (selNum != 0) do ( ctrl.selection = selNum ) ) showAttrClassRolls() ) ------------------------------------------------------------------------------- -- Event Handlers ------------------------------------------------------------------------------- on txtAttrFilter changed newText do ( showAttrClassRolls() ) on btnSelMxClass pressed do ( local selClass = lstMxClasses.selected refresh() local classNum = findItem lstMxClasses.items selClass if (classNum == 0) do return false local classObjs = mxClassObjs[classNum] local considerSet = RsSelToolGeneralRoll.objectList() local selSet = for obj in classObjs where (findItem considerSet obj != 0) collect obj RsSelectObjs selSet ) on btnSelAttrClass pressed do ( local selClass = lstAttrClasses.selected refresh() local classNum = findItem lstAttrClasses.items selClass if (classNum == 0) do return false local classObjs = attrClassObjs[classNum] local considerSet = RsSelToolGeneralRoll.objectList() local selSet = for obj in classObjs where (findItem considerSet obj != 0) collect obj RsSelectObjs selSet ) on lstAttrClasses selected num do ( showAttrClassRolls() ) on btnRefreshA pressed do ( refresh() ) on btnRefreshB pressed do ( refresh() ) on RsSelToolClassRoll open do ( refresh() ) ) rollout RsSelToolUVRoll "UVs:" ( group "Select Faces:" ( button btnUVRatioDiff "Mismatched UV-Proportions" across:2 align:#left tooltip:"Find faces with mismatched\nmapping/geometry proportions" spinner spnMaxRatioDiff "Max Ratio Difference:" range:[0,50.0,1.6] scale:0.01 align:#right offset:[0,3] button btnZeroSizeUVs "Zero-sized UVs" align:#left tooltip:"Find faces with zero-area UVs" ) -- Function returns faces on obj with UV face-proportions that are different to their geometry faces: fn findBadFaceMapRatios obj chan:1 maxRatioDiff:1.6 = ( local faceCount = getNumFaces obj local objOp = RsMeshPolyOp obj local objGetFace = RsGetFaceFunc obj local objGetMapFace = RsGetMapFaceFunc obj local objGetVert = RsGetVertFunc obj local objGetFaceMatID = RsGetFaceMatIDFunc obj local dodgyFaceNums = #{} dodgyFaceNums.count = faceCount -- Skip face-checks if object lacks this map-channel: if (not objOp.getMapSupport obj chan) do return #{} local bmpSizeRatios = #() local objMat = obj.material for faceNum = 1 to faceCount do ( local matId = objGetFaceMatID obj faceNum local bmpSizeRatio = bmpSizeRatios[matId] -- Get the aspect-ratio of this face's texturemap, if found: if (bmpSizeRatio == undefined) do ( bmpSizeRatio = 1.0 local subMat = RsGetSubmatByMatId objMat matId if (isKindOf subMat Rage_Shader) do ( local numVars = RstGetVariableCount subMat local texMapNum = 0 -- Check variables: local notFound = True for varNum = 1 to numVars while notFound do ( local varType = RstGetVariableType subMat varNum if (varType == "texmap") do ( local varName = RstGetVariableName subMat varNum texMapNum += 1 if (RsIsDiffuseMap varName) do ( local notFound = False local texMap = getSubTexmap subMat texMapNum if (isKindOf texMap Bitmaptexture) and (texMap.filename != "") do ( local loadBmp = openBitmap texMap.filename if (loadBmp != undefined) do ( bmpSizeRatio = (loadBmp.width as float) / loadBmp.height close loadBmp ) ) ) ) ) ) bmpSizeRatios[matId] = bmpSizeRatio ) local geoVerts = for vertNum in (objGetFace obj faceNum) collect (objGetVert obj vertNum) local mapVerts = for vertNum in (objGetMapFace obj chan faceNum) collect (objOp.getMapVert obj chan vertNum) -- Make map-verts use texture-map's aspect-ratio, and remove Z-axis: mapVerts = for item in mapVerts collect [item.x * bmpSizeRatio, item.y] local edgeVertIdxList = for n = 1 to (geoVerts.count - 1) collect #(n, n + 1) append edgeVertIdxList #(geoVerts.count, 1) local geoEdgeLengths = for edgeVertIdxs in edgeVertIdxList collect ( distance geoVerts[edgeVertIdxs[1]] geoVerts[edgeVertIdxs[2]] ) local mapEdgeLengths = for edgeVertIdxs in edgeVertIdxList collect ( distance mapVerts[edgeVertIdxs[1]] mapVerts[edgeVertIdxs[2]] ) local edgeCount = edgeVertIdxList.count local refLength = geoEdgeLengths[1] local geoRatios = for n = 2 to edgeCount collect (geoEdgeLengths[n] / refLength) local faceMaxRatioDiff = maxRatioDiff / refLength local refLength = mapEdgeLengths[1] local mapRatios = for n = 2 to edgeCount collect (mapEdgeLengths[n] / refLength) local similarRatio = True for n = 1 to geoRatios.count while similarRatio do ( local thisRatio = abs (geoRatios[n] - mapRatios[n]) similarRatio = (thisRatio < faceMaxRatioDiff) --if not similarRatio do (format "% (%, %) %\n" thisRatio geoRatios[n] mapRatios[n] thisMaxRatioDiff) ) if not similarRatio do ( dodgyFaceNums[faceNum] = True ) ) return dodgyFaceNums ) -- Function returns faces on obj with zero-area UV faces: fn findZeroSizeFaces obj chan:1 = ( local faceCount = getNumFaces obj local objOp = RsMeshPolyOp obj local objGetMapFace = RsGetMapFaceFunc obj local dodgyFaceNums = #{} dodgyFaceNums.count = faceCount -- Skip face-checks if object lacks this map-channel: if (not objOp.getMapSupport obj chan) do return #{} -- Find zero-sized faces: for faceNum = 1 to faceCount do ( local zeroSize = True local faceMapVerts = objGetMapFace obj chan faceNum local vertPosList = for thisVert in faceMapVerts collect (objOp.getMapVert obj chan thisVert) vertPosList.Z = 0 local firstVert = vertPosList[1] for n = 2 to vertPosList.count while zeroSize do ( zeroSize = ((distance firstVert vertPosList[n]) < 0.00001) ) if zeroSize do ( dodgyFaceNums[faceNum] = True ) ) return dodgyFaceNums ) fn runFaceFindFunc faceFindFunc = ( local notCancelled = True local objs = for obj in RsSelToolGeneralRoll.objectList() where (isEditPolyMesh obj) collect obj -- Make a note of which is the first-first instance of an object local primeInstObjs = #() local primeInstObjIdxs = #() local objPrimeIdxs = for objIdx = 1 to objs.count collect ( local obj = objs[objIdx] local objPrimeIdx = 0 for primeIdx = 1 to primeInstObjs.count while (objPrimeIdx == 0) do ( if (areNodesInstances obj primeInstObjs[primeIdx]) do ( objPrimeIdx = primeIdx ) ) if (objPrimeIdx == 0) do ( append primeInstObjs obj append primeInstObjIdxs objIdx objPrimeIdx = primeInstObjs.count ) objPrimeIdx ) -- If objects are instanced, we only need to examine one instance for each mesh: progressStart "Examining geometry..." local primeObjFaceLists = for n = 1 to primeInstObjs.count while (notCancelled = progressUpdate (100.0 * n / primeInstObjs.count)) collect ( faceFindFunc primeInstObjs[n] maxRatioDiff:spnMaxRatioDiff.value ) progressEnd() if notCancelled do ( faceLists = for idx in objPrimeIdxs collect primeObjFaceLists[idx] RsSelToolGeneralRoll.selObjFaces objs faceLists ) ) on btnUVRatioDiff pressed do ( runFaceFindFunc findBadFaceMapRatios ) on btnZeroSizeUVs pressed do ( runFaceFindFunc findZeroSizeFaces ) ) rollout RsSelToolRoll "Object Selector" ( dotNetControl rsBannerPanel "Panel" pos:[0,0] height:32 width:RsSelToolRoll.width local banner = makeRsBanner dn_Panel:rsBannerPanel versionNum:1.23 versionName:"Powerful Frog" local rollWidth dotNetControl dnTabs "system.windows.forms.tabControl" width:RsSelToolRoll.width height:25 align:#left pos:[0, 34] subRollout theSubRollout height:250 offset:[0,10] local rolloutPages = #( DataPair name:"General" rollouts:#( RsSelToolClassRoll, RsSelToolObjRoll, RsDupeSelector, RsRotationSelector, RsSelToolIplRoll, RsSelToolAreaCodeRoll ), DataPair name:"Memory" rollouts:#( RsSelToolMemoryRoll ), DataPair name:"LOD" rollouts:#( RsSelToolLodRoll, RsSelToolDrawLodRoll ), DataPair name:"Refs" rollouts:#( RsSelToolIrefsRoll, RsSelToolRsRefsRoll, RsSelToolRefsRoll ), DataPair name:"Materials" rollouts:#( RsSelToolTxdRoll, RsSelToolShaderRoll, RsSelToolTexMapRoll, RsSelToolUVRoll ), DataPair name:"Collision" rollouts:#( RsSelToolCollClassRoll, RsSelToolCollFlagRoll, RsSelToolCollGrpRoll, RsSelToolCollVolRoll, RsSelToolProcAttrRoll ), DataPair name:"Lights" rollouts:#( RsSelToolLightsRoll ) ) local rolloutCurrentPages = #() fn SetPage idx = ( if ( idx <= rolloutPages.count ) then ( for roll in rolloutCurrentPages do ( removeSubRollout theSubRollout roll ) rolloutCurrentPages = #() for roll in rolloutPages[idx].rollouts do ( addSubRollout theSubRollout roll append rolloutCurrentPages roll ) ) else ( MessageBox "Internal Error. Tabs misconfigured. Contact tools." ) ) on dnTabs Click do ( local tabNum = dnTabs.SelectedIndex + 1 RsSettingWrite "RsSelToolRoll" "tabNum" tabNum SetPage tabNum ) fn arrangeCtrls = ( theSubRollout.height = RsSelToolRoll.height - theSubRollout.pos.y - theSubRollout.pos.x ) -- Only allow vertical resizing: on RsSelToolRoll Resized newSize do ( if (newSize.x != rollWidth) do ( RsSelToolRoll.width = rollWidth ) RsSettingWrite "RsSelToolRoll" "height" newSize.y arrangeCtrls() ) on RsSelToolRoll open do ( banner.setup() rollWidth = RsSelToolRoll.width arrangeCtrls() dnTabs.tabPages.clear() for item in rolloutPages do ( dnTabs.tabPages.add item.name ) local tabNum = RsSettingsReadInteger "RsSelToolRoll" "tabNum" 1 if (tabNum > dnTabs.tabPages.count) do ( tabNum = 1 ) dnTabs.SelectedIndex = (tabNum - 1) addSubRollout theSubRollout RsSelToolGeneralRoll SetPage tabNum ) fn create = ( destroyDialog RsSelToolRoll local rollHeight = RsSettingsReadInteger "RsSelToolRoll" "height" 320 CreateDialog RsSelToolRoll modal:false width:430 height:rollHeight style:#(#style_resizing, #style_titlebar, #style_border, #style_sysmenu ) ) ) -- End rollout RsSelToolRoll.create() -- pipeline/util/selector.ms