3485 lines
99 KiB
Plaintext
Executable File
3485 lines
99 KiB
Plaintext
Executable File
--
|
|
-- File:: selector.ms
|
|
-- Description:: Object Selector Mk2
|
|
--
|
|
-- Author:: David Muir <david.muir@rockstarnorth.com>
|
|
-- 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
|