1802 lines
52 KiB
Plaintext
Executable File
1802 lines
52 KiB
Plaintext
Executable File
--
|
|
-- File:: rockstar/helpers/scenestats.ms
|
|
-- Description:: Gta Scene Statistics
|
|
--
|
|
-- Author:: Greg Smith <greg.smith@rockstarnorth.com
|
|
-- Author:: Luke Openshaw <luke.openshaw@rockstarnorth.com
|
|
-- Date:: 31/1/2007
|
|
--
|
|
-----------------------------------------------------------------------------
|
|
|
|
try (DestroyDialog RsSceneStatsRoll) catch ()
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Uses
|
|
-----------------------------------------------------------------------------
|
|
--filein "rockstar/export/settings.ms"
|
|
filein "pipeline/export/maps/mapsetup.ms"
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Globals
|
|
-----------------------------------------------------------------------------
|
|
struct RsSceneStats_objInfo
|
|
(
|
|
Private
|
|
TexStructs = #(),
|
|
|
|
Public
|
|
showName = "", objName = "", filename = "",
|
|
obj, instances = #(), row,
|
|
materials = #(), matIds = #(),
|
|
faces = 0, collFaces = 0, shaders = 0,
|
|
lodDistance, txd, txdTexCount,
|
|
modelSize = "unknown", txdsize = "unknown", totalSize = "unknown",
|
|
volume = 0,
|
|
faceDensity = 0,
|
|
|
|
refDef,
|
|
isLod = false,
|
|
isRef = false,
|
|
isIref = false,
|
|
isProp = false,
|
|
isInterior = false,
|
|
memDensity = "unknown", txdPercent,
|
|
|
|
commonCount, uniqueCount, nonUniqueCount,
|
|
|
|
fn GetTexStructs = (return TexStructs),
|
|
|
|
-- Functions to provide names and values for object-list columns:
|
|
fn getColNames = #("Name", "Instances", "Textures", "Faces", "Coll Faces", "LOD Dist", "TXD Name", "TXD Textures", "Shaders", "Volume", "Face Density", "TXD %", "Model Size (k)", "TXD Size (k)", "Total Size (k)", "Mem Density"),
|
|
fn getColVals = #(showName, instances.count, texStructs.count, faces, collFaces, loddistance, txd, txdTexCount, shaders, volume, faceDensity, txdPercent, modelSize, txdsize, totalSize, memDensity)
|
|
)
|
|
|
|
struct RsSceneStats_objTexInfo
|
|
(
|
|
name, texmap,
|
|
row,
|
|
faces = 0,
|
|
width, height, size,
|
|
|
|
matids = #(),
|
|
objStructs = #(),
|
|
objFaces = #(),
|
|
|
|
showObjs, showObjCount, showInstCount, showMatId, showTXDCount,
|
|
|
|
-- Functions to provide names and values for texture-list columns:
|
|
fn getColNames = #("Name", "Faces", "Width", "Height", "Size (k)", "MatID", "Objects", "Instances", "TXDs"),
|
|
fn getColVals = #(name, faces, width, height, size, showMatId, showObjCount, showInstCount, showTXDCount)
|
|
)
|
|
|
|
-----------------------------------------------------------------------------
|
|
-- Rollouts
|
|
-----------------------------------------------------------------------------
|
|
rollout RsSceneStatsRoll "Scene Stats"
|
|
(
|
|
--////////////////////////////////////////////////////////////
|
|
-- interface
|
|
--////////////////////////////////////////////////////////////
|
|
hyperlink lnkHelp "Help?" address:"https://devstar.rockstargames.com/wiki/index.php/Scene_Stats" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255)
|
|
|
|
groupBox grpUpdate "Objects:" align:#left offset:[0,-20] height:55 width:666
|
|
|
|
checkbox chkIncludeRefs "Include RsRefs" checked:true pos:(grpUpdate.pos + [10,16])
|
|
checkbox chkIncludeStnd "Include Standard" offset:[110,-20] checked:true
|
|
checkbox chkFragmentChildren "Frag Children" offset:[222,-20]
|
|
checkbox chkFileData "File Data" offset:[316,-20] checked:true enabled:false
|
|
radioButtons rdoSelAll "" labels:#("Selection", "All") columns:1 offset:[384,-28] align:#left
|
|
|
|
local btnWidth = 100
|
|
button cmdUpdate "v Update List v" align:#left width:btnWidth pos:[470,18]
|
|
button cmdRefresh "v Refresh List v" align:#left width:btnWidth pos:[570,18]
|
|
|
|
button cmdSaveCurrent "Save To File" align:#left width:btnWidth pos:(cmdRefresh.pos + [btnWidth + 20,0])
|
|
button cmdSaveReport "Save Report" align:#left width:btnWidth pos:(cmdSaveCurrent.pos + [btnWidth + 10,0])
|
|
|
|
dotNetControl barProgress "Windows.Forms.Progressbar" width:(grpUpdate.width - 12) height:8 pos:(grpUpdate.pos + [6, grpUpdate.height - 14])
|
|
dotNetControl dnObjectList "RsCustomDataGridView" pos:(grpUpdate.pos + [0,grpUpdate.height - 1])
|
|
|
|
label lblTextures "Selected Object's Textures:" align:#left
|
|
dotNetControl dnTextureList "RsCustomDataGridView" align:#left
|
|
|
|
bitmap bmpTex offset:[200,-300] width:200 height:200
|
|
local btnWidth = 200
|
|
button cmdSelectFaces "Show Selected Texture Faces On:" align:#left width:btnWidth
|
|
dropdownlist lstObjectNames width:btnWidth
|
|
button cmdSelectObj "Select Object" align:#left width:btnWidth
|
|
button cmdEdit "Edit in Photoshop" align:#left width:btnWidth
|
|
button cmdPerforce "Perforce check-out" align:#left width:btnWidth
|
|
button btnRefreshTx "Refresh" align:#left width:btnWidth height:60
|
|
local texCtrls = #(cmdSelectFaces, lstObjectNames, cmdSelectObj, cmdEdit, cmdPerforce, btnRefreshTx)
|
|
|
|
--edittext dnCountText readOnly:true height:40
|
|
dotNetControl dnCountText "MaxCustomControls.MaxTextBox"
|
|
|
|
local startSize
|
|
local selObjList = #()
|
|
local selObjTexInfoList = #()
|
|
local showTexInfoList = #()
|
|
|
|
------------------------------------------------------------------------------------
|
|
-- DotNet values:
|
|
------------------------------------------------------------------------------------
|
|
local textFont, dingFont, textFontBold
|
|
|
|
local DNknownColour = dotNetClass "system.Drawing.KnownColor"
|
|
local DNcolour = dotNetClass "System.Drawing.Color"
|
|
|
|
local selColour = DNcolour.fromKnownColor DNknownColour.MenuHighlight
|
|
|
|
local textCol = (colorMan.getColor #windowText) * 255
|
|
local windowCol = (colorMan.getColor #window) * 255
|
|
local notLoadedCol = if (windowCol[1] < 128) then (windowCol * 1.5) else (windowCol * 0.85)
|
|
|
|
local textColour = DNcolour.FromArgb textCol[1] textCol[2] textCol[3]
|
|
local backColour = DNcolour.FromArgb windowCol[1] windowCol[2] windowCol[3]
|
|
|
|
--////////////////////////////////////////////////////////////
|
|
-- methods
|
|
--////////////////////////////////////////////////////////////
|
|
fn GetIniRolloutSize =
|
|
(
|
|
RsSettingsReadValue "RsStatsRoll" "size" [1150, 520]
|
|
)
|
|
|
|
fn GetTextureWidth texMap =
|
|
(
|
|
width = 0
|
|
|
|
try
|
|
(
|
|
width = texMap.bitmap.width
|
|
)
|
|
catch
|
|
(
|
|
)
|
|
|
|
width
|
|
)
|
|
|
|
fn GetTextureHeight texMap =
|
|
(
|
|
height = 0
|
|
|
|
try
|
|
(
|
|
height = texMap.bitmap.height
|
|
)
|
|
catch
|
|
(
|
|
)
|
|
|
|
height
|
|
)
|
|
|
|
fn GetTextureSize texMap =
|
|
(
|
|
size = (getfilesize texMap.filename) / 1024
|
|
size
|
|
)
|
|
|
|
fn GetTextureMatID matid =
|
|
(
|
|
result = 0
|
|
if matid == -1 then
|
|
(
|
|
result = 0
|
|
)
|
|
else
|
|
(
|
|
result = matid
|
|
)
|
|
|
|
result
|
|
)
|
|
|
|
fn GetTextureFaces obj matid faces =
|
|
(
|
|
result = 0
|
|
if matid == -1 then
|
|
(
|
|
result = obj.mesh.numFaces
|
|
)
|
|
else
|
|
(
|
|
result = faces
|
|
)
|
|
|
|
result
|
|
)
|
|
|
|
fn RsGetTexMapsInfoFromMaterialSceneStats objStruct allTexNames allTexStructs mat: matid:-1 faces:0 faceidlist: getFileData:true =
|
|
(
|
|
local obj = objStruct.obj
|
|
|
|
if (mat == unsupplied) do (mat = obj.material)
|
|
|
|
if (obj != undefined) and (mat != undefined) do
|
|
(
|
|
case classOf mat of
|
|
(
|
|
MultiMaterial:
|
|
(
|
|
local matlist = #()
|
|
local faceCountList = #()
|
|
local faceIDLists = #()
|
|
|
|
--[R.G] Here we are passing a mesh copy of the obj. Important because we end up with a editable_mesh bit array.
|
|
RsMatGetMapIdsUsedOnObjectWithCount (copy obj.mesh) matlist mat:mat polycount:faceCountList faceidlists:faceIDLists
|
|
|
|
for i = 1 to matlist.count do
|
|
(
|
|
local matidx = finditem mat.materialIDList matlist[i]
|
|
|
|
if matidx != 0 then
|
|
(
|
|
RsGetTexMapsInfoFromMaterialSceneStats objStruct allTexNames allTexStructs mat:mat.materiallist[matidx] matId:matlist[i] faces:faceCountList[i] faceidlist:(faceIDLists[i] as bitArray) getFileData:getFileData
|
|
)
|
|
)
|
|
)
|
|
XRef_Material:
|
|
(
|
|
RsGetTexMapsInfoFromMaterialSceneStats objStruct allTexNames allTexStructs mat:(mat.getsrcitem()) faceidlist:faceidlist getFileData:getFileData
|
|
)
|
|
default:
|
|
(
|
|
append objStruct.materials mat
|
|
append objStruct.matIds matId
|
|
|
|
local numTexMap = getNumSubTexmaps mat
|
|
|
|
for i = 1 to numTexMap do
|
|
(
|
|
local subTexMap = getSubTexmap mat i
|
|
|
|
if classof subTexMap == DistanceAlphaMap then
|
|
(
|
|
--print "validation check 8"
|
|
subTexMap = RsGetDistanceAlphaMapResultMap subTexMap
|
|
)
|
|
if classof subTexMap == Bitmaptexture then
|
|
(
|
|
local TexFilename = SubTexMap.Filename
|
|
|
|
if (TexFilename != undefined) and (TexFilename != "") do
|
|
(
|
|
local texMapName = toLower (getFilenameFile TexFilename)
|
|
local texMatId = GetTextureMatID matId
|
|
|
|
local useTexStruct
|
|
local idxTex = finditem allTexNames texMapName
|
|
local addForObj = true
|
|
|
|
if idxTex == 0 then
|
|
(
|
|
useTexStruct = RsSceneStats_objTexInfo()
|
|
useTexStruct.name = texMapName
|
|
useTexStruct.texMap = subTexMap
|
|
|
|
if getFileData do
|
|
(
|
|
useTexStruct.width = GetTextureWidth subTexMap
|
|
useTexStruct.height = GetTextureHeight subTexMap
|
|
useTexStruct.Size = GetTextureSize subTexMap
|
|
)
|
|
|
|
append allTexNames texMapName
|
|
append allTexStructs useTexStruct
|
|
|
|
idxTex = allTexStructs.count
|
|
)
|
|
else
|
|
(
|
|
useTexStruct = allTexStructs[idxTex]
|
|
|
|
local objTexIdx = findItem useTexStruct.objStructs useTexStruct
|
|
|
|
-- Has this texturemap been applied to this object already, from another material?
|
|
if (objTexIdx != 0) do
|
|
(
|
|
-- Don't process further if this texturemap has already been found on this materialId:
|
|
local objTexMatIdx = findItem useTexStruct.matIds texMatId
|
|
if (objTexMatIdx != 0) do
|
|
(
|
|
addForObj = false
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Only add new matId data:
|
|
if addForObj do
|
|
(
|
|
useTexStruct.faces += (GetTextureFaces obj matid faces)
|
|
append useTexStruct.objStructs objStruct
|
|
append useTexStruct.objFaces faceIdList
|
|
append useTexStruct.matIds texMatId
|
|
appendIfUnique (objStruct.GetTexStructs()) useTexStruct
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
--
|
|
-- fn: sceneStatsGetFilename
|
|
-- desc:Returns the filename with either .zip or not depending on
|
|
-- if it finds the file with .zip at the end
|
|
--
|
|
fn sceneStatsGetFilename basename ext =
|
|
(
|
|
f = basename + ext
|
|
|
|
if (not doesFileExist f ) do
|
|
(
|
|
f = basename + ( substring ext 1 (ext.count - 4) )
|
|
)
|
|
f
|
|
)
|
|
|
|
fn UpdateObjectInfo objlist includeRefs:chkIncludeRefs.checked includeStnd:chkIncludeStnd.checked includeFragChilds:chkFragmentChildren.checked getFileData:chkFileData.checked updateProgress:true =
|
|
(
|
|
barProgress.value = 0.0
|
|
|
|
local idxTXD = getattrindex "Gta Object" "TXD"
|
|
local idxLODDistance = getattrindex "Gta Object" "LOD distance"
|
|
|
|
local objList = for obj in objlist where (not isKindOf obj Container) and (getattrclass obj == "Gta Object") collect obj
|
|
local objNameList = #()
|
|
local objStructList = #()
|
|
|
|
local sceneFilename = getFilenameFile maxfilename
|
|
|
|
-- Collect initial filtered object-data:
|
|
for obj in objList do
|
|
(
|
|
local objStruct = RsSceneStats_objInfo obj:obj instances:#(obj) objName:obj.name filename:sceneFilename
|
|
local objParent = obj.parent
|
|
|
|
-- Skip/include Frag Children?
|
|
if (not includeFragChilds) and (not ((objParent == undefined) or (isKindOf objParent Helper))) do continue
|
|
|
|
local isNew = true
|
|
local sourceObj
|
|
|
|
case of
|
|
(
|
|
-- Ref objects:
|
|
((objStruct.isRef = isRsRef obj includeDelegates:true) or (objStruct.isIRef = isRsInternalRef obj)):
|
|
(
|
|
if includeRefs do
|
|
(
|
|
local objName = obj.objectname
|
|
local nameIdx = finditem objNameList (toLower objName)
|
|
|
|
-- Refs don't have collision:
|
|
objStruct.collFaces = "NA"
|
|
|
|
if (nameIdx == 0) then
|
|
(
|
|
-- Add new struct for ref:
|
|
objStruct.objName = objName
|
|
|
|
append objStructList objStruct
|
|
append objNameList (toLower objName)
|
|
|
|
case of
|
|
(
|
|
(objStruct.isRef):
|
|
(
|
|
objStruct.showName = objName + " {Ref}"
|
|
objStruct.refDef = RsRefFuncs.getRefDefByName objName
|
|
|
|
if getFileData do
|
|
(
|
|
objStruct.filename = getFilenameFile obj.filename
|
|
)
|
|
)
|
|
(objStruct.isIRef):
|
|
(
|
|
objStruct.showName = objName + " {IRef}"
|
|
sourceObj = obj.getSourceObj()
|
|
)
|
|
)
|
|
)
|
|
else
|
|
(
|
|
-- Add to ref instances:
|
|
objStruct = objStructList[nameIdx]
|
|
append objStruct.instances obj
|
|
isNew = false
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Standard objects:
|
|
((isKindOf obj Editable_Mesh) or (isKindOf obj Editable_Poly)):
|
|
(
|
|
if includeStnd do
|
|
(
|
|
sourceObj = obj
|
|
append objStructList objStruct
|
|
append objNameList undefined
|
|
|
|
objStruct.showName = gRsMapExportFragments.getNonFragmentName objStruct.objName
|
|
objStruct.isLod = RsIsAnyLOD obj
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Process model found in scene:
|
|
if (sourceObj != undefined) do
|
|
(
|
|
-- Get "LOD Dist"
|
|
objStruct.lodDistance = getattr obj idxLODDistance
|
|
|
|
-- Get txd for standard/iref objects:
|
|
objStruct.txd = toUpper (getattr sourceObj idxTXD)
|
|
|
|
-- Set obj filename to container's, if used:
|
|
local objCont = Containers.IsInContainer sourceObj
|
|
if (objCont != undefined) do
|
|
(
|
|
--format "Object Is In Container: %\n" objCont.name
|
|
objStruct.filename = RsContFuncs.getContFilename objCont
|
|
|
|
if (maxfilenameForObj != undefined) then
|
|
(
|
|
objStruct.filename = getFilenameFile objStruct.filename
|
|
)
|
|
else
|
|
(
|
|
-- Need to make a guess that the filename will be the container name
|
|
objStruct.filename = objCont.Name
|
|
|
|
if (objStruct.filename == "") do
|
|
(
|
|
format "Unable to find filename for object in container\n"
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Set values for RsRefs:
|
|
(
|
|
local refStructs = for item in objStructList where (item.refDef != undefined) collect item
|
|
|
|
-- Get indexes for prop-dir maxfiles:
|
|
local propFileNums = #{}
|
|
propFileNums.count = RSrefData.maxFiles.count
|
|
for n = 1 to RSrefData.maxFiles.count do
|
|
(
|
|
propFileNums[n] = matchPattern RSrefData.maxFiles[n] pattern:"*props*"
|
|
)
|
|
|
|
-- Make sure that relevant RsRef txd/lod-data is loaded:
|
|
local refDefs = for item in refStructs collect item.refDef
|
|
|
|
RsRefFuncs.loadTxds refDefs
|
|
RsRefFuncs.loadLodDistances refDefs
|
|
|
|
for item in refStructs do
|
|
(
|
|
local refDef = item.refDef
|
|
|
|
item.txd = toUpper refDef.txd
|
|
item.isInterior = (refDef.objType == #milo)
|
|
item.isProp = propFileNums[refDef.maxFileNum]
|
|
item.lodDistance = refDef.lodDistance
|
|
)
|
|
)
|
|
|
|
-- Set values for all items:
|
|
for objStruct in objStructList do
|
|
(
|
|
local obj = objStruct.obj
|
|
|
|
-- Set number of faces:
|
|
if isProperty obj "mesh" do
|
|
(
|
|
local useMesh = copy obj.mesh
|
|
objStruct.faces = useMesh.numFaces
|
|
)
|
|
|
|
-- Collision faces:
|
|
if (isKindOf objStruct.collFaces number) do
|
|
(
|
|
for subobj in obj.children where isKindOf subObj Col_Mesh do
|
|
(
|
|
objStruct.collFaces += getCollPolyCount subobj
|
|
)
|
|
)
|
|
|
|
-- volume
|
|
local dimensions = obj.max - obj.min
|
|
objStruct.volume = dimensions.x * dimensions.y * dimensions.z
|
|
|
|
-- face density
|
|
if (objStruct.faces != 0) do
|
|
(
|
|
objStruct.faceDensity = (objStruct.faces as float) / objStruct.volume
|
|
)
|
|
|
|
if getFileData and (isKindOf objStruct.filename string) do
|
|
(
|
|
local baseFilename = RsConfigGetCacheDir() + "maps/" + objStruct.filename + "/" + objStruct.objName
|
|
local checkExts = #(".idr.zip", ".ift.zip")
|
|
|
|
local getModelSize = 0
|
|
for ext in checkExts while (getModelSize == 0) do
|
|
(
|
|
getModelSize = getFileSize (sceneStatsGetFilename basefilename ext)
|
|
)
|
|
|
|
if (getModelSize != 0) do
|
|
(
|
|
getModelSize /= 1024
|
|
objStruct.modelSize = getModelSize as integer
|
|
objStruct.memDensity = (getModelSize as float) / objStruct.volume
|
|
)
|
|
)
|
|
)
|
|
|
|
local allTexNames = #()
|
|
local allTexStructs = #()
|
|
|
|
-- Per-obj texture-list bits:
|
|
pushPrompt "Getting object textures..."
|
|
|
|
--local timeStart = timeStamp()
|
|
for objNum = 1 to objStructList.count while not keyboard.escPressed do
|
|
(
|
|
if updateProgress then barProgress.value = 100.0 * objNum / objStructList.count
|
|
|
|
local objStruct = objStructList[objNum]
|
|
RsGetTexMapsInfoFromMaterialSceneStats objStruct allTexNames allTexStructs getFileData:getFileData
|
|
|
|
-- Get shader-count:
|
|
objStruct.shaders = objStruct.materials.count
|
|
)
|
|
--format "Time taken: %s\n" ((timeStamp() - timeStart) / 1000.0)
|
|
popPrompt()
|
|
|
|
barProgress.value = 100.0
|
|
if keyboard.escPressed do return #()
|
|
|
|
-- Clear .materials values, as they're no longer required:
|
|
objStructList.materials = undefined
|
|
|
|
-- Get TXD-data:
|
|
(
|
|
local txdFilenames = #()
|
|
local txdItemLists = #()
|
|
|
|
local mapCacheDir = RsConfigGetCacheDir() + "maps/"
|
|
|
|
-- Collect items per Txd filename:
|
|
for item in objStructList where (item.txd != undefined) do
|
|
(
|
|
local txdBaseFilename = mapCacheDir + item.filename + "/" + item.txd
|
|
local txdFileIdx = finditem txdFilenames txdBaseFilename
|
|
|
|
if (txdFileIdx == 0) do
|
|
(
|
|
append txdFilenames txdBaseFilename
|
|
append txdItemLists #()
|
|
txdFileIdx = txdFilenames.count
|
|
)
|
|
|
|
append txdItemLists[txdFileIdx] item
|
|
)
|
|
|
|
for n = 1 to txdFilenames.count do
|
|
(
|
|
local txdItems = txdItemLists[n]
|
|
local txdBaseFilename = txdFilenames[n]
|
|
|
|
local txdTexNames = #()
|
|
|
|
for item in txdItems do
|
|
(
|
|
local texNames = for texItem in (item.GetTexStructs()) collect texItem.name
|
|
join txdTexNames texNames
|
|
)
|
|
|
|
txdTexNames = makeUniqueArray txdTexNames
|
|
|
|
-- Set txdTexcount for all txd-objects:
|
|
local txdTexCount = txdTexNames.count
|
|
txdItems.txdTexCount = txdTexCount
|
|
|
|
-- Set TXD % for each object:
|
|
for objStruct in txdItems do
|
|
(
|
|
objStruct.txdPercent = 0.01 * (integer (10000 * float (objStruct.GetTexStructs()).count / txdTexCount))
|
|
)
|
|
|
|
-- Set txdSize for all txd-objects:
|
|
local txdSize
|
|
if getFileData and (isKindOf txdBaseFilename string) and (txdBaseFilename != "") do
|
|
(
|
|
local txdFilename = sceneStatsGetFilename txdBaseFilename ".itd.zip"
|
|
txdSize = getFileSize txdFilename
|
|
)
|
|
|
|
if (isKindOf txdSize number) and (txdSize != 0) do
|
|
(
|
|
txdSize /= 1024
|
|
txdItems.txdSize = txdSize as integer
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Final processing of list:
|
|
for objStruct in objStructList do
|
|
(
|
|
-- totalsize
|
|
if (isKindOf objStruct.modelSize number) and (isKindOf objStruct.txdSize number) do
|
|
(
|
|
objStruct.totalSize = objStruct.modelSize + objStruct.txdSize
|
|
)
|
|
)
|
|
|
|
return objStructList
|
|
)
|
|
|
|
fn showObjsInList objList =
|
|
(
|
|
dnObjectList.Rows.Clear()
|
|
dnTextureList.Rows.Clear()
|
|
|
|
for objStruct in objList do
|
|
(
|
|
local rowData = objStruct.getColVals()
|
|
|
|
local newRowNum = dnObjectList.Rows.add rowData
|
|
|
|
local newRow = dnObjectList.rows.item[newRowNum]
|
|
newRow.tag = dotNetMXSValue objStruct
|
|
objStruct.row = newRow
|
|
)
|
|
|
|
dnObjectList.AutoResizeColumns()
|
|
dnObjectList.ClearSelection()
|
|
)
|
|
|
|
fn updateObjectList selected:(rdoSelAll.state == 1) =
|
|
(
|
|
local objs = if selected then selection else objects
|
|
selObjList = UpdateObjectInfo objs
|
|
showObjsInList selObjList
|
|
)
|
|
|
|
fn UpdateSelectedTextures =
|
|
(
|
|
local selRowCount = dnObjectList.SelectedRows.Count
|
|
-- Don't Update if nothing is selected
|
|
if (selRowCount != 0) do
|
|
(
|
|
-- Disable the texture-controls, they'll be reactivated once a selection is made:
|
|
texCtrls.enabled = false
|
|
cmdSelectObj.text = "Select Object"
|
|
|
|
local selRows = dnObjectList.SelectedRows
|
|
|
|
local selObjStructs = #()
|
|
for n = 0 to (selRowCount - 1) do
|
|
(
|
|
local tag = dnObjectList.SelectedRows.item[n].tag
|
|
if (tag != undefined) do append selObjStructs tag.value
|
|
)
|
|
|
|
|
|
local objCount = selRows.Count
|
|
showTexInfoList = #()
|
|
|
|
-- Get the texturemap structs from each selected object:
|
|
for i = 0 to (objCount - 1) where (selRows.Item[i].tag != undefined) do
|
|
(
|
|
local objStruct = selRows.Item[i].tag.value
|
|
|
|
if (objStruct != undefined) then
|
|
(
|
|
for texStruct in (objStruct.GetTexStructs()) do
|
|
(
|
|
local texIdx = findItem showTexInfoList texStruct
|
|
|
|
if (texIdx == 0) do
|
|
(
|
|
append showTexInfoList texStruct
|
|
texStruct.showMatId = #()
|
|
texStruct.showObjs = #()
|
|
|
|
texIdx = showTexInfoList.count
|
|
)
|
|
|
|
local objIdx = findItem texStruct.objStructs objStruct
|
|
appendIfUnique texStruct.showMatId texStruct.matIds[objIdx]
|
|
appendIfUnique texStruct.showObjs objStruct
|
|
|
|
objStruct.commonCount = objStruct.uniqueCount = objStruct.nonUniqueCount = 0
|
|
)
|
|
)
|
|
)
|
|
|
|
local commonCount = 0
|
|
local uniqueCount = 0
|
|
local nonUniqueCount = 0
|
|
|
|
for texStruct in showTexInfoList do
|
|
(
|
|
local texTxdList = makeUniqueArray (for objStruct in texStruct.showObjs collect objStruct.txd)
|
|
|
|
texStruct.showTxdCount = texTxdList.count
|
|
texStruct.showObjCount = texStruct.showObjs.count
|
|
|
|
local instances = #()
|
|
for objStruct in texStruct.showObjs do
|
|
(
|
|
join instances objStruct.instances
|
|
)
|
|
texStruct.showInstCount = instances.count
|
|
|
|
case texStruct.showObjCount of
|
|
(
|
|
-- Texture is used on all objects:
|
|
objCount:
|
|
(
|
|
commonCount += 1
|
|
--nonUniqueCount += 1
|
|
|
|
for objStruct in texStruct.showObjs do
|
|
(
|
|
objStruct.commonCount += 1
|
|
--objStruct.nonUniqueCount += 1
|
|
)
|
|
)
|
|
-- Texture is used on only one object:
|
|
1:
|
|
(
|
|
uniqueCount += 1
|
|
|
|
for objStruct in texStruct.showObjs do
|
|
(
|
|
objStruct.uniqueCount += 1
|
|
)
|
|
)
|
|
-- Texture is used on multiple objects:
|
|
default:
|
|
(
|
|
nonUniqueCount += 1
|
|
|
|
for objStruct in texStruct.showObjs do
|
|
(
|
|
objStruct.nonUniqueCount += 1
|
|
)
|
|
)
|
|
)
|
|
|
|
texStruct.showMatId = if (texStruct.showMatId.count > 1) then "Multiple" else texStruct.showMatId[1]
|
|
)
|
|
|
|
-- Write out the stats for each object selected and the common texture count.
|
|
local showText = stringStream ""
|
|
fn plural val = if (val == 1) then "" else "s"
|
|
|
|
case objCount of
|
|
(
|
|
0:
|
|
(
|
|
format "Select from Objects list to see texture-stats" objCount to:showText
|
|
)
|
|
1:
|
|
(
|
|
format "Selected object has % texture%" commonCount (plural commonCount) to:showText
|
|
)
|
|
Default:
|
|
(
|
|
format "Selected % objects have:\r\n" objCount to:showText
|
|
format " % texture% on only one object\r\n" uniqueCount (plural uniqueCount) to:showText
|
|
format " % texture% on multiple objects\r\n" nonUniqueCount (plural nonUniqueCount) to:showText
|
|
format " % texture% in common (on all objects)\r\n\r\n" commonCount (plural commonCount) to:showText
|
|
|
|
for item in selObjStructs do
|
|
(
|
|
format "%: " item.showName to:showText
|
|
if (item.uniqueCount != 0) do
|
|
(
|
|
format "% unique" item.uniqueCount to:showText
|
|
)
|
|
if (item.nonUniqueCount != 0) do
|
|
(
|
|
format "% non-unique " item.nonUniqueCount to:showText
|
|
)
|
|
if (item.commonCount != 0) do
|
|
(
|
|
format "% common " item.commonCount to:showText
|
|
)
|
|
|
|
format "\r\n" to:showText
|
|
)
|
|
)
|
|
)
|
|
|
|
dnCountText.Text = showText as string
|
|
dnCountText.SelectionStart = 0
|
|
dnCountText.SelectionLength = 0
|
|
|
|
-- Remove/add the texture-list:
|
|
dnTextureList.Rows.Clear()
|
|
|
|
for texInfo in showTexInfoList do
|
|
(
|
|
local rowData = texInfo.getColVals()
|
|
|
|
local newRowNum = dnTextureList.Rows.add rowData
|
|
|
|
local newRow = dnTextureList.rows.item[newRowNum]
|
|
newRow.tag = dotNetMXSValue texInfo
|
|
texInfo.row = newRow
|
|
)
|
|
|
|
dnTextureList.AutoResizeColumns()
|
|
dnTextureList.ClearSelection()
|
|
)
|
|
)
|
|
|
|
-- Shows copies of meshes arranged in a line, plus objects showing counts (if not undefined)
|
|
fn showArrangedObjs items counts:#() =
|
|
(
|
|
-- Delete existing temp-objects:
|
|
delete $TmpStats*
|
|
|
|
-- Find scene-objects' bounds:
|
|
local maxSize = [0,0,0]
|
|
|
|
if (objects.count != 0) then
|
|
(
|
|
local objMaxes = for obj in geometry collect obj.max
|
|
|
|
for n = 1 to 3 do
|
|
(
|
|
local axisVals = for item in objMaxes collect item[n]
|
|
maxSize[n] = amax axisVals
|
|
)
|
|
)
|
|
|
|
local idxDontExport = GetAttrIndex "Gta Object" "Dont Export"
|
|
local objPadding = 1.0
|
|
local counterSize = 0.1
|
|
|
|
local placePoint = [maxSize.x + 10.0, maxSize.y + 10.0, 0]
|
|
|
|
local newObjs = #()
|
|
|
|
for itemNum = 1 to items.count do
|
|
(
|
|
local item = items[itemNum]
|
|
|
|
local obj = item.obj
|
|
if not isProperty obj #mesh do continue
|
|
|
|
local objCount = counts[itemNum]
|
|
|
|
-- Create copy-object, set as non-export:
|
|
local copyObj = editable_mesh name:("TmpStatsMesh: " + item.objName) wireColor:obj.wireColor
|
|
setAttr copyObj idxDontExport true
|
|
|
|
copyObj.mesh = obj.mesh
|
|
copyObj.material = obj.material
|
|
update copyObj
|
|
|
|
local objMin = copyObj.min
|
|
local objMax = copyObj.max
|
|
|
|
-- Move object:
|
|
copyObj.pos = placePoint - objMin
|
|
|
|
local objSize = objMax - objMin
|
|
local objCentre = copyObj.center
|
|
|
|
local baseRect = rectangle name:("TmpStats_Base: " + item.objName) wireColor:green width:objSize.x length:objSize.y pos:[objCentre.x, objCentre.y, 0]
|
|
local groupObjs = #(copyObj, baseRect)
|
|
|
|
if (objCount != undefined) do
|
|
(
|
|
-- Add number-column object:
|
|
local segs = if (isKindOf objCount integer) then objCount else 1
|
|
local counterObj = prism pos:(copyObj.pos + objMin) name:("TmpStats_Count: " + item.objName) wireColor:green side1Length:counterSize side2Length:counterSize side3Length:counterSize height:(objCount * counterSize) heightsegs:segs
|
|
append groupObjs counterObj
|
|
|
|
-- Add value-string:
|
|
local textObj = text pos:(copyObj.pos + objMin) name:("TmpStats_Text: " + item.objName) wireColor:green size:(counterSize * 3) text:(objCount as string)
|
|
textObj.pos.y -= (textObj.max.y - textObj.min.y + counterSize)
|
|
append groupObjs textObj
|
|
)
|
|
|
|
local newGrp = group groupObjs name:("TmpStats: " + item.objName)
|
|
append newObjs newGrp
|
|
|
|
-- Set next place-point:
|
|
placePoint.x += (objSize.x + objPadding)
|
|
)
|
|
|
|
-- Zoom extents to new objects:
|
|
if (newObjs.count != 0) do
|
|
(
|
|
local oldSel = selection as array
|
|
|
|
select newObjs
|
|
max zoomext sel
|
|
|
|
clearSelection()
|
|
select oldSel
|
|
)
|
|
)
|
|
|
|
--////////////////////////////////////////////////////////////
|
|
-- events
|
|
--////////////////////////////////////////////////////////////
|
|
|
|
on cmdSaveReport pressed do
|
|
(
|
|
barProgress.value = 0.0
|
|
saveFilename = getsavefilename caption:"Save report to:" filename:"out.csv" types:"Comma Seperated (*.csv)|*.csv"
|
|
|
|
if saveFilename == undefined then return 0
|
|
|
|
local saveFile = openfile saveFilename mode:"wb"
|
|
|
|
if saveFile == undefined then return 0
|
|
|
|
local allobj = #()
|
|
RsGetMapObjectsWithXrefs rootnode.children allobj
|
|
for obj in rootnode.children where isKindOf obj container do
|
|
(
|
|
RsGetMapObjectsWithXrefs obj.children allObj
|
|
)
|
|
|
|
-- create blocks
|
|
|
|
BlockNames = #()
|
|
BlockSelSets = #()
|
|
BlockAreas = #()
|
|
BlockObjects = #()
|
|
|
|
idxBlockID = getattrindex "Gta Object" "BlockID"
|
|
idxBlockIDMilo = getattrindex "Gta MILOTri" "BlockID"
|
|
|
|
for obj in allobj do
|
|
(
|
|
local blockName = case getattrclass obj of
|
|
(
|
|
"Gta Object":(toLower (getattr obj idxBlockID))
|
|
"Gta MILOTri":(toLower (getattr obj idxBlockIDMilo))
|
|
default:""
|
|
)
|
|
|
|
if (blockName == "") do (blockName = "(unassigned)")
|
|
|
|
local blockIdx = finditem BlockNames blockName
|
|
|
|
if blockIdx == 0 then
|
|
(
|
|
local objFoundList = getNodeByName blockName exact:true all:true
|
|
local objValid = #()
|
|
|
|
areaValue = 0.0
|
|
|
|
for objFound in objFoundList do
|
|
(
|
|
if isKindOf objFound GtaBlock do
|
|
(
|
|
append objValid objFound
|
|
areaValue = objFound.plane.length * objFound.plane.width
|
|
)
|
|
)
|
|
|
|
append BlockNames blockName
|
|
append BlockSelSets #()
|
|
append BlockAreas areaValue
|
|
append BlockObjects objValid
|
|
|
|
blockIdx = BlockNames.count
|
|
)
|
|
|
|
append BlockSelSets[blockIdx] obj
|
|
)
|
|
|
|
local blockTotalFaces = #()
|
|
local blockTotalCollFaces = #()
|
|
local blockTotalModelSize = #()
|
|
local blockTotalTXDSize = #()
|
|
local blockTotalTXDCount = #()
|
|
|
|
local blockTotalLodSize = #()
|
|
local blockTotalInteriorSize = #()
|
|
local blockTotalPropSize = #()
|
|
local blockTotalRefSize = #()
|
|
|
|
for i = 1 to BlockNames.count do
|
|
(
|
|
local objList = UpdateObjectInfo BlockSelSets[i] includeRefs:true includeStnd:true updateProgress:false
|
|
local blockName = BlockNames[i]
|
|
barProgress.value = ((i as float) / BlockNames.count) * 100.0
|
|
|
|
format "Block: %\n\n" blockName to:saveFile
|
|
format "Name,Textures,Faces,Collision Faces,LOD Distance,Model Size (k),TXD Name,TXD Size (k),TXD Textures,Shaders,Volume (m^3),Face Density (n/m^3),Mem Density (k/m^3),TXD \%,Is Lod,Is Ref,Is Prop,Is Interior\n" to:saveFile
|
|
|
|
local currTotalFaces = 0
|
|
local currTotalCollFaces = 0
|
|
local currTotalModelSize = 0
|
|
local currTotalTXDSize = 0
|
|
|
|
local currTotalLodSize = 0
|
|
local currTotalInteriorSize = 0
|
|
local currTotalPropSize = 0
|
|
local currTotalRefSize = 0
|
|
|
|
local blockTxds = #()
|
|
|
|
for obj in objList do
|
|
(
|
|
local thisTotalFaces = obj.faces
|
|
local thisTotalCollFaces = obj.collFaces
|
|
local thisTotalModelSize = obj.modelSize
|
|
local thisTotalTXDSize = obj.txdsize
|
|
|
|
if (isKindOf thisTotalFaces number) do currTotalFaces += thisTotalFaces
|
|
if (isKindOf thisTotalCollFaces number) do currTotalCollFaces += thisTotalCollFaces
|
|
|
|
if (isKindOf thisTotalModelSize number) then
|
|
(
|
|
currTotalModelSize += thisTotalModelSize
|
|
|
|
if obj.islod do currTotalLodSize += thisTotalModelSize
|
|
if obj.isRef do currTotalRefSize += thisTotalModelSize
|
|
if obj.isprop do currTotalPropSize += thisTotalModelSize
|
|
if obj.isInterior do currTotalInteriorSize += thisTotalModelSize
|
|
)
|
|
|
|
if finditem blockTxds obj.txd == 0 do
|
|
(
|
|
append blockTxds obj.txd
|
|
|
|
if (isKindOf thisTotalTXDSize number) do
|
|
(
|
|
currTotalTXDSize += thisTotalTXDSize
|
|
if obj.islod do currTotalLodSize += thisTotalTXDSize
|
|
if obj.isRef do currTotalRefSize += thisTotalTXDSize
|
|
if obj.isprop do currTotalPropSize += thisTotalTXDSize
|
|
if obj.isInterior do currTotalInteriorSize += thisTotalTXDSize
|
|
)
|
|
)
|
|
|
|
local valList = #(obj.showName, (obj.GetTexStructs()).count, obj.faces, obj.collFaces, obj.lodDistance, obj.modelSize, obj.txd, obj.txdsize, obj.txdTexCount,
|
|
obj.shaders, obj.volume, obj.faceDensity, obj.memDensity, obj.txdPercent, obj.isLod, obj.isRef, obj.isProp, obj.isInterior)
|
|
|
|
for n = 1 to valList.count do
|
|
(
|
|
format "%%" valList[n] (if (n == valList.count) then "\n" else ",") to:saveFile
|
|
)
|
|
)
|
|
|
|
append blockTotalFaces currTotalFaces
|
|
append blockTotalCollFaces currTotalCollFaces
|
|
append blockTotalModelSize currTotalModelSize
|
|
append blockTotalTXDSize currTotalTXDSize
|
|
|
|
append blockTotalLodSize currTotalLodSize
|
|
append blockTotalRefSize currTotalRefSize
|
|
append blockTotalPropSize currTotalPropSize
|
|
append blockTotalInteriorSize currTotalInteriorSize
|
|
|
|
append blockTotalTXDCount blockTxds.count
|
|
|
|
format ("\n\n") to:saveFile
|
|
)
|
|
|
|
format ("Block Summary\n\n") to:saveFile
|
|
|
|
format ("Name,Object Count,TXD Count,Total Face Count,Total Collision Face Count,Total Model Size (k),Total TXD Size (k),Total (k),Lod Size (k),Ref Size (k),Prop Size (k),Interior Size (k),Volume (m^2),Faces/m^2,Coll Faces/m^2,Memory/m^2\n") to:saveFile
|
|
|
|
local totalObjectCount = 0
|
|
local totalTXDCount = 0
|
|
local totalFaceCount = 0
|
|
local totalCollFaceCount = 0
|
|
local totalModelSize = 0
|
|
local totalTXDSize = 0
|
|
local totalVolume = 0
|
|
|
|
local totalLodSize = 0
|
|
local totalRefSize = 0
|
|
local totalPropSize = 0
|
|
local totalInteriorSize = 0
|
|
|
|
for i = 1 to BlockNames.count do
|
|
(
|
|
local hasArea = (BlockAreas[i] != -1.0)
|
|
|
|
if hasArea do
|
|
(
|
|
totalObjectCount += BlockSelSets[i].count
|
|
totalTXDCount += blockTotalTXDCount[i]
|
|
totalFaceCount += blockTotalFaces[i]
|
|
totalCollFaceCount += blockTotalCollFaces[i]
|
|
totalModelSize += blockTotalModelSize[i]
|
|
totalTXDSize += blockTotalTXDSize[i]
|
|
totalVolume += BlockAreas[i]
|
|
|
|
totalLodSize += blockTotalLodSize[i]
|
|
totalRefSize += blockTotalRefSize[i]
|
|
totalPropSize += blockTotalPropSize[i]
|
|
totalInteriorSize += blockTotalInteriorSize[i]
|
|
)
|
|
|
|
local valList = #(BlockNames[i], BlockSelSets[i].count, blockTotalTXDCount[i], blockTotalFaces[i], blockTotalCollFaces[i], blockTotalModelSize[i], blockTotalTXDSize[i],
|
|
(blockTotalTXDSize[i] + blockTotalModelSize[i]), blockTotalLodSize[i], blockTotalRefSize[i], blockTotalPropSize[i], blockTotalInteriorSize[i], BlockAreas[i])
|
|
|
|
if hasArea then
|
|
(
|
|
join valList #(float blockTotalFaces[i] / BlockAreas[i], float blockTotalCollFaces[i] / BlockAreas[i], float (blockTotalTXDSize[i] + blockTotalModelSize[i]) / BlockAreas[i])
|
|
)
|
|
else
|
|
(
|
|
join valList #(0,0,0)
|
|
)
|
|
|
|
local valList = #()
|
|
|
|
for n = 1 to valList.count do
|
|
(
|
|
format "%%" valList[n] (if (n == valList.count) then "\n" else ",") to:saveFile
|
|
)
|
|
)
|
|
|
|
local valList = #("TOTAL (without unasssigned)", totalObjectCount, totalTXDCount, totalFaceCount, totalCollFaceCount, totalModelSize, totalTXDSize, (totalModelSize + totalTXDSize), totalLodSize,
|
|
totalRefSize, totalPropSize, totalInteriorSize, totalVolume, float totalFaceCount / totalVolume, float totalCollFaceCount / totalVolume, float (totalModelSize + totalTXDSize) / totalVolume)
|
|
|
|
for n = 1 to valList.count do
|
|
(
|
|
format "%%" valList[n] (if (n == valList.count) then "\n" else ",") to:saveFile
|
|
)
|
|
|
|
close saveFile
|
|
|
|
mapName = getFilenameFile maxfilename
|
|
|
|
RsCopyFilename = RsConfigGetCacheDir() + "mapstats_details/" + mapName + ".csv"
|
|
RsMakeSurePathExists RsCopyFilename
|
|
copyfile saveFilename RsCopyFilename
|
|
|
|
RsTotalFilename = RsConfigGetCacheDir() + "mapstats/" + mapName + ".csv"
|
|
|
|
saveFile = openfile RsTotalFilename mode:"w"
|
|
|
|
if saveFile != undefined then
|
|
(
|
|
format "Name,Object Count,TXD Count,Total Face Count,Total Collision Face Count,Total Model Size (k),Total TXD Size (k),Total (k),Lod Size (k),Ref Size (k),Prop Size (k),Interior Size (k),Volume (m^2),Faces/m^2,Coll Faces/m^2,Memory/m^2\n" to:saveFile
|
|
|
|
for i = 1 to BlockNames.count do
|
|
(
|
|
local valList = #(BlockNames[i], BlockSelSets[i].count, blockTotalTXDCount[i], blockTotalFaces[i], blockTotalCollFaces[i], blockTotalModelSize[i], blockTotalTXDSize[i],
|
|
(blockTotalTXDSize[i] + blockTotalModelSize[i]), blockTotalLodSize[i], blockTotalRefSize[i], blockTotalPropSize[i], blockTotalInteriorSize[i], BlockAreas[i])
|
|
|
|
if (BlockAreas[i] != -1.0) then
|
|
(
|
|
join valList #(float blockTotalFaces[i] / BlockAreas[i], float blockTotalCollFaces[i] / BlockAreas[i], float (blockTotalTXDSize[i] + blockTotalModelSize[i]) / BlockAreas[i])
|
|
)
|
|
else
|
|
(
|
|
join valList #(0,0,0)
|
|
)
|
|
|
|
local valList = #()
|
|
|
|
for n = 1 to valList.count do
|
|
(
|
|
format "%%" valList[n] (if (n == valList.count) then "\n" else ",") to:saveFile
|
|
)
|
|
)
|
|
|
|
close saveFile
|
|
)
|
|
|
|
RsTotalFilename = RsConfigGetCacheDir() + "mapstats/blockmap/" + mapName + ".csv"
|
|
RsMakeSurePathExists RsTotalFilename
|
|
|
|
saveFile = openfile RsTotalFilename mode:"w"
|
|
|
|
if (saveFile != undefined) do
|
|
(
|
|
format ("VER2\n") to:saveFile
|
|
|
|
for i = 1 to BlockNames.count do
|
|
(
|
|
if BlockNames[i] == "(unassigned)" then continue
|
|
|
|
local valList = #(BlockNames[i],BlockObjects[i].count)
|
|
|
|
for blockObj in BlockObjects[i] do
|
|
(
|
|
join valList #(blockObj.pos.x, blockObj.pos.y, (blockObj.rotation as eulerangles).z, blockObj.plane.length, blockObj.plane.width)
|
|
)
|
|
|
|
join valList #(BlockNames[i], BlockSelSets[i].count, blockTotalTXDCount[i], blockTotalFaces[i], blockTotalCollFaces[i], blockTotalModelSize[i], blockTotalTXDSize[i],
|
|
(blockTotalTXDSize[i] + blockTotalModelSize[i]), blockTotalLodSize[i], blockTotalRefSize[i], blockTotalPropSize[i], blockTotalInteriorSize[i], BlockAreas[i])
|
|
|
|
if (BlockAreas[i] != -1.0) then
|
|
(
|
|
join valList #(float blockTotalFaces[i] / BlockAreas[i], float blockTotalCollFaces[i] / BlockAreas[i], float (blockTotalTXDSize[i] + blockTotalModelSize[i]) / BlockAreas[i])
|
|
)
|
|
else
|
|
(
|
|
join valList #(0,0,0)
|
|
)
|
|
|
|
local valList = #()
|
|
|
|
for n = 1 to valList.count do
|
|
(
|
|
format "%%" valList[n] (if (n == valList.count) then "\n" else ",") to:saveFile
|
|
)
|
|
)
|
|
|
|
close saveFile
|
|
)
|
|
|
|
format "Saved to: %\n" saveFilename
|
|
messageBox ("Report saved to:\n" + saveFilename) title:"Scene-report save completed"
|
|
)
|
|
|
|
on cmdSaveCurrent pressed do
|
|
(
|
|
saveFilename = getsavefilename caption:"Save report to:" filename:"out.csv" types:"Comma Seperated (*.csv)|*.csv"
|
|
|
|
if saveFilename == undefined then return 0
|
|
|
|
saveFile = openfile saveFilename mode:"wb"
|
|
|
|
if saveFile == undefined then return 0
|
|
|
|
local colNames = RsSceneStats_objInfo.getColNames()
|
|
local colCount = colNames.count
|
|
|
|
for n = 1 to colCount do
|
|
(
|
|
format "%%" colNames[n] (if (n == colCount) then "\n" else ",") to:saveFile
|
|
)
|
|
|
|
for i = 0 to (dnObjectList.Rows.Count - 1) do
|
|
(
|
|
local objStruct = dnObjectList.Rows.Item[i].tag.value
|
|
local rowVals = for item in objStruct.getColVals() collect
|
|
(
|
|
if (item == undefined) then "NA" else (item as string)
|
|
)
|
|
|
|
for n = 1 to colCount do
|
|
(
|
|
format "%%" (rowVals[n] as string) (if (n == colCount) then "\n" else ",") to:saveFile
|
|
)
|
|
)
|
|
|
|
close saveFile
|
|
|
|
format "Saved to: %\n" saveFilename
|
|
messageBox ("List saved to:\n" + saveFilename) title:"List save completed"
|
|
)
|
|
|
|
on cmdUpdate pressed do
|
|
(
|
|
updateObjectList()
|
|
)
|
|
|
|
on cmdRefresh pressed do
|
|
(
|
|
--Record our selected rows indexes
|
|
SelectedRowIndexList = #()
|
|
for RowIndex = 1 to dnObjectList.SelectedRows.Count do
|
|
(
|
|
SelectedRow = dnObjectList.SelectedRows.Item ( RowIndex - 1 )
|
|
Append SelectedRowIndexList SelectedRow.Index
|
|
)
|
|
|
|
--Collect previous objects used
|
|
objs = for selObj in selObjList where isValidNode selObj.Obj collect selObj.Obj
|
|
|
|
--Push the selection back through the system
|
|
selObjList = UpdateObjectInfo objs
|
|
showObjsInList selObjList
|
|
|
|
--Reselect the same rows
|
|
for SelectedRowIndex in SelectedRowIndexList do
|
|
(
|
|
try
|
|
(
|
|
( dnObjectList.Rows.Item SelectedRowIndex ).Selected = true
|
|
) catch ()
|
|
)
|
|
|
|
--Update textures
|
|
UpdateSelectedTextures()
|
|
)
|
|
|
|
on dnObjectList SelectionChanged args do
|
|
(
|
|
dnTextureList.ClearSelection()
|
|
UpdateSelectedTextures()
|
|
)
|
|
|
|
on dnTextureList SelectionChanged args do
|
|
(
|
|
lstObjectNames.Items = #()
|
|
cmdSelectObj.text = "Select Object"
|
|
|
|
local selRows = dnTextureList.SelectedRows
|
|
local selTags = for n = 0 to (selRows.count - 1) collect selRows.Item[n].tag
|
|
local selTexInfos = for tag in selTags where (tag != undefined) collect tag.value
|
|
|
|
-- Enable/disable texture-controls:
|
|
texCtrls.enabled = (selTexInfos.count != 0)
|
|
|
|
if (selTexInfos.count == 0) do
|
|
(
|
|
bmpTex.bitmap = bitmap 200 200
|
|
return 0
|
|
)
|
|
|
|
local texInfo = selTexInfos[1]
|
|
|
|
case of
|
|
(
|
|
(texInfo.Width == texInfo.Height):
|
|
(
|
|
img = bitmap 200 200
|
|
rm = rendermap texInfo.texmap into:img size:[200,200]
|
|
bmpTex.bitmap = img
|
|
close rm
|
|
)
|
|
(texInfo.Height > texInfo.Width):
|
|
(
|
|
height = texInfo.Height as float
|
|
heightPercentage = (height / 200.0) as float
|
|
newWidth = 200
|
|
if heightPercentage != 0 then
|
|
(
|
|
newWidth = (texInfo.Width / heightPercentage) as float
|
|
)
|
|
img = bitmap newWidth 200
|
|
rm = rendermap texInfo.texmap into:img size:[newWidth,200]
|
|
bmpTex.bitmap = img
|
|
close rm
|
|
)
|
|
default:
|
|
(
|
|
width = texInfo.Width as float
|
|
widthPercentage = (width / 200.0) as float
|
|
newHeight = 200
|
|
if widthPercentage != 0 then
|
|
(
|
|
newHeight = (texInfo.Height /widthPercentage) as float
|
|
)
|
|
img = bitmap 200 newHeight
|
|
rm = rendermap texInfo.texmap into:img size:[200,newHeight]
|
|
bmpTex.bitmap = img
|
|
close rm
|
|
)
|
|
)
|
|
|
|
local objList = #()
|
|
for item in selTexInfos do (join objList item.objStructs)
|
|
objList = makeUniqueArray (for item in objList where not item.isRef collect (item.showName as string))
|
|
|
|
-- Add instances-count to Select Objects button:
|
|
local instances = #()
|
|
for texStruct in selTexInfos do (for objStruct in texStruct.objStructs where objStruct.row.selected do (join instances objStruct.instances))
|
|
instances = makeUniqueArray instances
|
|
if (instances.count > 1) do (cmdSelectObj.text = "Select objects (" + (instances.count as string) + ")")
|
|
|
|
-- Disable face-select controls if list is empty:
|
|
if (objList.count == 0) do
|
|
(
|
|
cmdSelectFaces.enabled = false
|
|
lstObjectNames.enabled = false
|
|
)
|
|
|
|
lstObjectNames.items = objList
|
|
)
|
|
|
|
-- Select object-instances on double-clicking a row:
|
|
on dnObjectList DoubleClick sender arg do
|
|
(
|
|
local clickItem = sender.hitTest arg.x arg.y
|
|
local rowIdx = clickItem.RowIndex
|
|
|
|
if (rowIdx != -1) do
|
|
(
|
|
local objData = dnObjectList.Rows.Item[rowIdx].tag.value
|
|
local selObjs = for obj in objData.instances where isValidNode obj collect obj
|
|
|
|
if selObjs.count != 0 do
|
|
(
|
|
select selObjs
|
|
max zoomext sel
|
|
)
|
|
)
|
|
)
|
|
|
|
-- Called when dnObjectList is clicked:
|
|
on dnObjectList CellMouseUp sender arg do
|
|
(
|
|
local rowIdx = arg.rowIndex
|
|
if (rowIdx < 0) do return false
|
|
|
|
local clickRow = sender.Rows.Item[rowIdx]
|
|
|
|
-- If (right)clicked cell is unselected, replace selection with it:
|
|
if not clickRow.Selected do
|
|
(
|
|
sender.ClearSelection()
|
|
clickRow.Selected = True
|
|
)
|
|
|
|
local rightClick = arg.button.equals arg.button.right
|
|
|
|
if rightClick do
|
|
(
|
|
-- Rightclick menu:
|
|
rcmenu RSmenu_SceneStatsObjList
|
|
(
|
|
local selRows = #()
|
|
|
|
menuItem itmSelObjs "Select Objects"
|
|
menuItem itmArrangeObjs "Visual Arrangement"
|
|
|
|
on itmSelObjs picked do
|
|
(
|
|
local selObjs = #()
|
|
|
|
for row in selRows do
|
|
(
|
|
join selObjs row.tag.value.instances
|
|
)
|
|
|
|
selObjs = for obj in selObjs where isValidNode obj collect obj
|
|
|
|
if (selObjs.count != 0) do
|
|
(
|
|
select selObjs
|
|
max zoomext sel
|
|
)
|
|
)
|
|
|
|
on itmArrangeObjs picked do
|
|
(
|
|
local counts = #()
|
|
local sortedColumn = RsSceneStatsRoll.dnObjectList.SortedColumn
|
|
|
|
if (sortedColumn != undefined) do
|
|
(
|
|
local colIdx = sortedColumn.Index
|
|
counts = for row in selRows collect row.Cells.Item[colIdx].value
|
|
|
|
for n = 1 to counts.count where (not isKindOf counts[n] number) do (counts[n] = undefined)
|
|
)
|
|
|
|
local objStructs = for row in selRows collect row.Tag.Value
|
|
showArrangedObjs objStructs counts:counts
|
|
)
|
|
|
|
on RSmenu_SceneStatsObjList open do
|
|
(
|
|
-- Get row-tags, in their current order:
|
|
local rows = RsSceneStatsRoll.dnObjectList.Rows
|
|
|
|
for n = 0 to (rows.count - 1) do
|
|
(
|
|
local row = rows.Item[n]
|
|
|
|
if row.selected do
|
|
(
|
|
append selRows row
|
|
)
|
|
)
|
|
)
|
|
)
|
|
popUpMenu RSmenu_SceneStatsObjList
|
|
)
|
|
)
|
|
|
|
|
|
fn getSelTexFiles =
|
|
(
|
|
local selCount = dnTextureList.SelectedRows.count
|
|
|
|
for n = 0 to (selCount - 1) collect
|
|
(
|
|
local selRow = dnTextureList.SelectedRows.Item[n]
|
|
local selTexStruct = selRow.tag.value
|
|
|
|
selTexStruct.texMap.filename
|
|
)
|
|
)
|
|
|
|
on cmdEdit pressed do
|
|
(
|
|
local pshop = CreateOLEObject "Photoshop.Application"
|
|
|
|
pshop.Visible = False
|
|
pshop.Visible = True
|
|
|
|
for selTexFilename in getSelTexFiles() where (doesFileExist selTexFilename) do
|
|
(
|
|
format "Opening in Photoshop: %\n" selTexFilename
|
|
pshop.open selTexFilename
|
|
)
|
|
|
|
releaseOLEObject pshop
|
|
)
|
|
|
|
on cmdPerforce pressed do
|
|
(
|
|
local selTexFiles = getSelTexFiles()
|
|
gRsPerforce.add_or_edit selTexFiles
|
|
)
|
|
|
|
on btnRefreshTx pressed do
|
|
(
|
|
--object list index
|
|
local objIdx = (dnObjectList.SelectedRows.Item[0]).index
|
|
|
|
--get the selected texture index from the dnTextureList
|
|
local txIdx = (dnTextureList.SelectedRows.Item[0]).index
|
|
|
|
--dnObjectList.
|
|
)
|
|
|
|
fn selectObjs selName: =
|
|
(
|
|
local doSelFaces = (selName != unsupplied)
|
|
|
|
clearSelection()
|
|
subobjectlevel = 0
|
|
|
|
if (dnTextureList.SelectedRows.Count < 1) do (return 0)
|
|
|
|
if (selName == undefined) do (return 0)
|
|
|
|
local keepLooking = true
|
|
local objs = #()
|
|
local faceSel = #{}
|
|
|
|
local selItems = dnTextureList.SelectedRows
|
|
local selTexInfos = for n = 0 to (selItems.count - 1) collect selItems.Item[n].tag.value
|
|
for texInfo in selTexInfos do
|
|
(
|
|
for n = 1 to texInfo.objStructs.count
|
|
where texInfo.objStructs[n].row.selected and ((not doSelFaces) or (matchPattern texInfo.objStructs[n].showName pattern:selName)) do
|
|
(
|
|
local selObj = texInfo.objStructs[n].obj
|
|
join objs texInfo.objStructs[n].instances
|
|
|
|
if doSelFaces do
|
|
(
|
|
faceSel += texInfo.objFaces[n]
|
|
)
|
|
)
|
|
)
|
|
|
|
objs = makeUniqueArray objs
|
|
|
|
if (objs.count == 0) do (return 0)
|
|
|
|
select objs
|
|
|
|
if doSelFaces do
|
|
(
|
|
setCommandPanelTaskMode(#modify)
|
|
|
|
local obj = objs[1]
|
|
|
|
if (obj.modifiers.count != 0) then
|
|
(
|
|
local plural = if (obj.modifiers.count == 1) then "" else "s"
|
|
|
|
messageBox ("Unable to select faces: object has modifier" + plural + " applied") title:("Error: Modifier" + plural + " on object")
|
|
)
|
|
else
|
|
(
|
|
|
|
--[R.G] Altered this logic so the selection of faces is always done on
|
|
-- an editable_mesh level rather than editable_poly.
|
|
-- This makes sense because we collect the face bit array while in mesh form earlier in the script.
|
|
|
|
if (isKindOf obj Editable_Poly) then
|
|
(
|
|
ConvertTo obj Editable_Mesh
|
|
setFaceSelection obj faceSel
|
|
ConvertTo obj Editable_Poly
|
|
subobjectlevel = 4
|
|
)
|
|
|
|
if (isKindOf obj Editable_Mesh) then
|
|
(
|
|
setFaceSelection obj faceSel
|
|
subobjectlevel = 3
|
|
)
|
|
)
|
|
)
|
|
|
|
max zoomExt sel
|
|
)
|
|
|
|
on cmdSelectObj pressed do
|
|
(
|
|
selectObjs()
|
|
)
|
|
|
|
on cmdSelectFaces pressed do
|
|
(
|
|
selectObjs selName:lstObjectNames.selected
|
|
)
|
|
|
|
on dnObjectList MouseUp Button Shift x y do
|
|
(
|
|
--UpdateSelectedTextures()
|
|
)
|
|
|
|
on cmdOK pressed do (
|
|
|
|
DestroyDialog RsSceneStatsRoll
|
|
)
|
|
|
|
fn arrangeCtrls size:[RsSceneStatsRoll.width, RsSceneStatsRoll.height] =
|
|
(
|
|
dnObjectList.height = (size.y - 100) / 2
|
|
dnObjectList.width = size.x - 24
|
|
|
|
lblTextures.pos = [lblTextures.pos[1], dnObjectList.pos[2] + dnObjectList.height + 10]
|
|
|
|
dnTextureList.pos = [dnTextureList.pos.x, lblTextures.pos.y + 20]
|
|
dnTextureList.width = size.x - 434
|
|
dnTextureList.height = size.y - dnTextureList.pos.y - 12
|
|
|
|
bmpTex.pos = dnTextureList.pos + [dnTextureList.width + 5, 0]
|
|
|
|
cmdSelectFaces.pos = bmpTex.pos + [bmpTex.width + 4, 0]
|
|
lstObjectNames.pos = cmdSelectFaces.pos + [0, 22]
|
|
|
|
cmdSelectObj.pos = cmdSelectFaces.pos + [0, 50]
|
|
cmdEdit.pos = cmdSelectObj.pos + [0, 24]
|
|
cmdPerforce.pos = cmdEdit.pos + [0, 24]
|
|
btnRefreshTx.pos = cmdPerforce.pos + [0, 24]
|
|
|
|
dnCountText.pos = bmpTex.pos + [0, bmpTex.height + 4]
|
|
dnCountText.Height = size.y - dnCountText.pos.y - 12
|
|
dnCountText.Width = size.x - dnCountText.pos.x - 12
|
|
|
|
lnkHelp.pos.x = size.x - 40
|
|
)
|
|
|
|
fn setupCols ctrl colNames =
|
|
(
|
|
-- Add blank-named column:
|
|
append colNames ""
|
|
|
|
local colNum
|
|
for item in colNames do
|
|
(
|
|
colNum = ctrl.Columns.add item item
|
|
)
|
|
|
|
local firstCol = ctrl.Columns.item[0]
|
|
local lastCol = ctrl.Columns.item[colNum]
|
|
|
|
firstCol.width = 160
|
|
lastCol.AutoSizeMode = lastCol.AutoSizeMode.Fill
|
|
)
|
|
|
|
fn init =
|
|
(
|
|
barProgress.Style = barProgress.Style.Continuous
|
|
barProgress.ForeColor = textColour
|
|
|
|
dnObjectList.SelectionMode = dnObjectList.SelectionMode.FullRowSelect
|
|
dnObjectList.AllowUserToAddRows = false
|
|
dnObjectList.AllowUserToDeleteRows = false
|
|
dnObjectList.AllowUserToOrderColumns = true
|
|
dnObjectList.AllowUserToResizeRows = false
|
|
dnObjectList.AllowUserToResizeColumns = true
|
|
dnObjectList.AllowDrop = false
|
|
dnObjectList.MultiSelect = true
|
|
dnObjectList.ReadOnly = true
|
|
|
|
dnObjectList.dock = dnObjectList.dock.fill
|
|
dnObjectList.DefaultCellStyle.backColor = backColour
|
|
dnObjectList.DefaultCellStyle.foreColor = textColour
|
|
|
|
textFont = dnObjectList.font
|
|
dingFont = dotNetObject "System.Drawing.Font" "Webdings" textFont.size
|
|
textFontBold = dotnetobject "system.drawing.font" textFont (dotnetclass "system.drawing.fontstyle").bold
|
|
|
|
dnObjectList.EnableHeadersVisualStyles = false
|
|
dnObjectList.ColumnHeadersDefaultCellStyle.backColor = backColour
|
|
dnObjectList.ColumnHeadersDefaultCellStyle.foreColor = textColour
|
|
dnObjectList.ColumnHeadersDefaultCellStyle.font = textFontBold
|
|
|
|
setupCols dnObjectList (RsSceneStats_objInfo.getColNames())
|
|
|
|
dnTextureList.SelectionMode = dnTextureList.SelectionMode.FullRowSelect
|
|
dnTextureList.AllowUserToAddRows = false
|
|
dnTextureList.AllowUserToDeleteRows = false
|
|
dnTextureList.AllowUserToOrderColumns = true
|
|
dnTextureList.AllowUserToResizeRows = false
|
|
dnTextureList.AllowUserToResizeColumns = true
|
|
dnTextureList.AllowDrop = false
|
|
dnTextureList.MultiSelect = true
|
|
dnTextureList.ReadOnly = true
|
|
|
|
dnTextureList.dock = dnTextureList.dock.fill
|
|
dnTextureList.DefaultCellStyle.backColor = backColour
|
|
dnTextureList.DefaultCellStyle.foreColor = textColour
|
|
|
|
dnTextureList.EnableHeadersVisualStyles = false
|
|
dnTextureList.ColumnHeadersDefaultCellStyle.backColor = backColour
|
|
dnTextureList.ColumnHeadersDefaultCellStyle.foreColor = textColour
|
|
dnTextureList.ColumnHeadersDefaultCellStyle.font = textFontBold
|
|
|
|
setupCols dnTextureList (RsSceneStats_objTexInfo.getColNames())
|
|
|
|
dnCountText.readOnly = true
|
|
dnCountText.multiline = true
|
|
dnCountText.scrollbars = dnCountText.ScrollBars.vertical
|
|
dnCountText.wordWrap = false
|
|
dnCountText.backColor = backColour
|
|
dnCountText.foreColor = textColour
|
|
|
|
startSize = GetIniRolloutSize()
|
|
arrangeCtrls size:startSize
|
|
)
|
|
|
|
on chkIncludeRefs changed state do (updateObjectList())
|
|
on chkIncludeStnd changed state do (updateObjectList())
|
|
on chkFragmentChildren changed state do (updateObjectList())
|
|
on chkFileData changed state do (updateObjectList())
|
|
on rdoSelAll changed state do (updateObjectList())
|
|
|
|
on RsSceneStatsRoll open do
|
|
(
|
|
init()
|
|
updateObjectList()
|
|
UpdateSelectedTextures()
|
|
)
|
|
|
|
on RsSceneStatsRoll resized pntSize do
|
|
(
|
|
-- Save the new rollout-size:
|
|
RsSettingWrite "RsStatsRoll" "size" pntSize
|
|
|
|
arrangeCtrls size:pntSize
|
|
)
|
|
|
|
fn create retry:false =
|
|
(
|
|
destroyDialog RsSceneStatsRoll
|
|
RS_CustomDataGrid forceRecompile:retry
|
|
|
|
local rollSize = RsSceneStatsRoll.GetIniRolloutSize()
|
|
CreateDialog RsSceneStatsRoll modal:false width:rollSize.x height:rollSize.y style:#(#style_resizing, #style_titlebar, #style_border, #style_sysmenu )
|
|
)
|
|
)
|
|
|
|
(
|
|
-- Try to catch if RsCustomDataGrid fails to work:
|
|
try (RsSceneStatsRoll.create())
|
|
catch (RsSceneStatsRoll.create retry:true)
|
|
)
|