-- -- File:: rockstar/helpers/TextureStats.ms -- Description:: 3dsMax Rage Shader Texture Map Statistics -- -- Author:: David Muir -- Date:: 22 June 2007 -- ----------------------------------------------------------------------------- ----------------------------------------------------------------------------- -- Rollout ----------------------------------------------------------------------------- try (destroyDialog RsTextureMapStatsRoll) catch () filein "rockstar/util/mapobj.ms" rollout RsTextureMapStatsRoll "Texture Map Statistics" ( ----------------------------------------------------------------------------------------- -- Script-scope Variables ----------------------------------------------------------------------------------------- local headers = #( "Texture Map", "Objects", "Object\nCount", "TXDs", "TXD\nCount") local widths = #( 800, 3200, 2600, 3200, 2600, 3200, 2600 ) local idxTXD = GetAttrIndex "Gta Object" "TXD" ----------------------------------------------------------------------------------------- -- UI Widgets and Layout ----------------------------------------------------------------------------------------- button btnUpdate "Update" align:#left width:100 height:28 offset:[-8,0] across:6 checkbox chkSelection "Selection Only" pos:(btnUpdate.pos + [104,0]) checkbox chkIncRefs "Include Refs" pos:(chkSelection.pos + [0,14]) editText txtFilter "Filter:" text:"*" pos:(chkSelection.pos + [100,1]) dotNetControl barProgress "Windows.Forms.Progressbar" height:10 pos:(txtFilter.pos + [txtFilter.width + 10,3]) hyperlink lnkHelp "Help?" offset:[0,3] address:"https://devstar.rockstargames.com/wiki/index.php/Texture_Stats" align:#right color:(color 0 0 255) hoverColor:(color 0 0 255) visitedColor:(color 0 0 255) dotNetControl lstTextures "RsCustomDataGridView" align:#center bitmap bmpPreview width:256 height:256 pos:(lstTextures.pos + [0,1]) local notFoundText = "FILE NOT FOUND" local notFoundOffset = [128, 128] - (getTextExtent notFoundText) / 2 label lblNotFound notFoundText pos:(bmpPreview.pos + notFoundOffset) visible:false dotNetControl txtBmpInfo "MaxCustomControls.MaxTextBox" width:256 pos:(bmpPreview.pos + [0,bmpPreview.height]) ------------------------------------------------------------------------------------ -- 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] ----------------------------------------------------------------------------------------- -- Functions ----------------------------------------------------------------------------------------- fn GetIniRolloutSize = ( RsSettingsReadValue "RsTextureMapStatsRoll" "size" [1015, 520] ) fn arrangeCtrls size:[RsTextureMapStatsRoll.width, RsTextureMapStatsRoll.height] = ( bmpPreview.pos.x = size.x - bmpPreview.width lblNotFound.pos.x = bmpPreview.pos.x + notFoundOffset.x txtBmpInfo.pos.x = bmpPreview.pos.x txtBmpInfo.height = size.y - txtBmpInfo.pos.y lstTextures.width = size.x - bmpPreview.width lstTextures.height = size.y - lstTextures.pos.y barProgress.width = size.x - barProgress.pos.x - 54 lnkHelp.pos.x = size.x - 40 ) -- Fill out texture-list: fn update doSelected:chkSelection.checked doRefs:chkIncRefs.checked = ( lstTextures.Rows.Clear() barProgress.value = 0.0 struct textureInfoStruct (name, path, showPath, texMap, objs=#(), txds=#()) local texInfoList = #() local texPathList = #() local objs = if doSelected then (selection as array) else (objects as array) objs = for obj in objs where (GetAttrClass obj == "Gta Object") collect obj -- Mark objects as being refs, and ignore if not used. local refNamesUsed = #() local refsDefsUsed = #() local objItems = #() for obj in objs do ( local isRef = isRSref obj includeDelegates:true if doRefs or not isRef do ( local objNum = 0 if isRef do ( objNum = findItem refNamesUsed obj.objectName ) if (objNum == 0) then ( append objItems (dataPair objs:#(obj) isRef:isRef) if isRef do ( append refNamesUsed obj.objectName append refsDefsUsed obj.refDef ) ) else ( if isRef do ( append objItems[objNum].objs obj ) ) ) ) -- Load RsRef txd data: if (refsDefsUsed.count != 0) do ( RsRefFuncs.loadTxds refsDefsUsed ) local objCount = objItems.count local objNum = 0 local filterPattern = "*" + txtFilter.text + "*" local showPathFilter = RsConfigGetTextureSourceDir() + "*" --local timestart = timeStamp() -- Holding down Escape will stop processing: for item in objItems while not keyboard.escPressed do ( barProgress.value = 100.0 * (objNum += 1) / objCount local obj = item.objs[1] local isRef = item.isRef -- Get texturemap paths from object: local objMatIds = RsGetMatIdsUsedByObj (if (isRef or (isRsInternalRef obj)) then (copy obj.mesh) else obj) local objTexMapPaths = RsGetTexPathsFromMat obj.mat matIds:objMatIds local objTexMapNames = for filename in objTexMapPaths collect (RsRemovePathAndExtension filename) local txdname = "" if (objTexMapNames.count != 0) do ( if isRef then ( case of ( (isRsInternalRef obj): ( local sourceObj = obj.getSourceObj() txdname = GetAttr sourceObj idxTXD ) (obj.refDef != undefined): ( txdname = obj.refDef.txd ) ) ) else ( txdname = GetAttr obj idxTXD ) ) -- Process texture list for n = 1 to objTexMapNames.count where (isKindOf objTexMapNames[n] string) and (matchPattern objTexMapNames[n] pattern:filterPattern) do ( local texName = objTexMapNames[n] local texPath = objTexMapPaths[n] local listId = findItem texPathList texPath if (listId == 0) do ( local showPath = if matchPattern texPath pattern:showPathFilter then ( showPath = subString texPath showPathFilter.count -1 ) else texPath local newTexInfo = textureInfoStruct name:texName path:texPath showPath:showPath append texInfoList newTexInfo append texPathList texPath listId = texInfoList.count ) local texInfo = texInfoList[listId] join texInfo.objs item.objs appendIfUnique texInfo.txds txdname ) ) --format "Time taken: %s\n" ((timeStamp() - timestart) / 1000.0) for texInfo in texInfoList do ( texInfo.objs = makeUniqueArray texInfo.objs ) -- Function to generate comma-separated list-string: fn makeListString list = ( local outStream = stringStream "" for i = 1 to list.count do ( format "%" list[i] to:outStream if (i != list.count) do ( format ", " to:outStream ) ) return (outStream as string) ) -- Populate listview for texInfo in texInfoList do ( local objsString = makeListString (for obj in texInfo.objs collect obj.name) local txdsString = makeListString texInfo.txds local rowData = #(texInfo.name, objsString, texInfo.objs.count, txdsString, texInfo.txds.count) local rowNum = lstTextures.Rows.add rowData -- Add texInfo struct as row-tag: lstTextures.Rows.item[rowNum].tag = dotNetMXSValue texInfo ) lstTextures.AutoResizeColumns() local maxAutoWidth = 220 for colNum = 0 to (lstTextures.columns.count - 1) do ( local col = lstTextures.columns.item[colNum] if (col.width > maxAutoWidth) do ( col.width = maxAutoWidth ) ) lstTextures.ClearSelection() barProgress.value = 0.0 ) fn updateText = ( if lstTextures.selectedRows.count == 0 do ( txtBmpInfo.text = "" return false ) local selRowsInfo = for n = 0 to (lstTextures.selectedRows.count - 1) collect lstTextures.selectedRows.item[n].tag.value local textStream = stringstream "" local texPaths = #() local texObjs = #() local texTxds = #() for texInfo in selRowsInfo do ( append texPaths texInfo.showPath join texObjs texInfo.objs join texTxds texInfo.txds ) texObjs = makeUniqueArray texObjs texTxds = makeUniqueArray texTxds format "Textures:\n" to:textStream for texPath in texPaths do ( format "%\n" texPath to:textStream ) format "\nTXDs:\n" to:textStream for texTxd in texTxds do ( format "%\n" texTxd to:textStream ) format "\nObjects:\n" to:textStream for obj in texObjs do ( format "%\n" obj.name to:textStream ) txtBmpInfo.text = replace_LF_with_CRLF (textStream as string) OK ) fn init = ( lstTextures.pos.x = 0 lstTextures.SelectionMode = lstTextures.SelectionMode.FullRowSelect lstTextures.AllowUserToAddRows = false lstTextures.AllowUserToDeleteRows = false lstTextures.AllowUserToOrderColumns = true lstTextures.AllowUserToResizeRows = false lstTextures.AllowUserToResizeColumns = true lstTextures.AllowDrop = false lstTextures.MultiSelect = true lstTextures.ReadOnly = true lstTextures.dock = lstTextures.dock.fill lstTextures.DefaultCellStyle.backColor = backColour lstTextures.DefaultCellStyle.foreColor = textColour textFont = lstTextures.font dingFont = dotNetObject "System.Drawing.Font" "Webdings" textFont.size textFontBold = dotnetobject "system.drawing.font" textFont (dotnetclass "system.drawing.fontstyle").bold lstTextures.EnableHeadersVisualStyles = false lstTextures.ColumnHeadersDefaultCellStyle.backColor = backColour lstTextures.ColumnHeadersDefaultCellStyle.foreColor = textColour lstTextures.ColumnHeadersDefaultCellStyle.font = textFontBold lstTextures.ColumnHeadersHeight = 34 txtBmpInfo.readOnly = true txtBmpInfo.multiline = true txtBmpInfo.scrollbars = txtBmpInfo.ScrollBars.vertical txtBmpInfo.wordWrap = true txtBmpInfo.backColor = backColour txtBmpInfo.foreColor = textColour startSize = GetIniRolloutSize() arrangeCtrls size:startSize local colNum append headers "" for item in headers do ( colNum = lstTextures.Columns.add item item ) local firstCol = lstTextures.Columns.item[0] local lastCol = lstTextures.Columns.item[colNum] firstCol.width = 160 lastCol.AutoSizeMode = lastCol.AutoSizeMode.Fill ) ----------------------------------------------------------------------------------------- -- UI Event Handlers ----------------------------------------------------------------------------------------- on chkSelection changed state do ( update() ) on chkIncRefs changed state do ( update() ) on btnUpdate pressed do ( update() ) on lstTextures mouseDown args do ( local clickPoint = dotNetObject "System.Drawing.Point" args.x args.y local clickedItem = lstTextures.hitTest args.x args.y if (clickedItem != undefined) do ( local rowNum = clickedItem.rowIndex if (rowNum != -1) and (lstTextures.selectedRows.count != 0) do ( local rightClick = args.button.equals args.button.right if rightClick then ( -- Rightclick menu: rcmenu RSmenu_textureMapStats ( local selRows menuItem itmSelObjs "Select objects using textures" menuItem itmPhotoshop "Open textures in Photoshop" menuItem itmPerforce "Perforce Checkout/Add textures" -- Select selected textures' objects: on itmSelObjs picked do ( local selObjs = #() for n = 0 to (selRows.count - 1) do ( join selObjs (selRows.item[n].tag.value.objs) ) selObjs = for obj in selObjs where (isValidNode obj) collect obj clearSelection() select selObjs ) fn getSelTexFiles = ( for n = 0 to (selRows.count - 1) collect ( local selRow = selRows.Item[n] local selTexStruct = selRow.tag.value RsMakeBackSlashes selTexStruct.path ) ) -- Open selected textures in Photoshop: on itmPhotoshop picked do ( for selTexFilename in getSelTexFiles() do ( format "Opening in Photoshop: %\n" selTexFilename shelllaunch ("Photoshop.exe") selTexFilename ) ) -- Perforce checkout/add selected textures: on itmPerforce picked do ( local selTexFiles = getSelTexFiles() gRsPerforce.add_or_edit selTexFiles ) on RSmenu_textureMapStats open do ( selRows = lstTextures.selectedRows local plural = if (selRows.count != 1) then "s" else "" itmSelObjs.text = "Select objects using texture" + plural itmPhotoshop.text = "Open texture" + plural + " in Photoshop" itmPerforce.text = "Perforce Checkout/Add texture" + plural ) ) popUpMenu RSmenu_textureMapStats ) else ( ) ) ) ) on lstTextures selectionChanged ev args do ( local selRowNum = lstTextures.currentCell.RowIndex local img = bitmap bmpPreview.width bmpPreview.height color:windowCol if (lstTextures.selectedRows.count == 0) do ( bmpPreview.bitmap = img return false ) local rowTag = lstTextures.rows.item[selRowNum].tag if (rowTag == undefined) do (return false) local rowInfo = rowTag.value if (doesFileExist rowInfo.path) then ( renderMap (bitmapTexture bitmap:(openBitmap rowInfo.path)) filter:true into:img lblNotFound.visible = false ) else ( lblNotFound.visible = true ) bmpPreview.bitmap = img updateText() OK ) on txtFilter entered newText do ( update() ) on RsTextureMapStatsRoll resized pntSize do ( -- Save the new rollout-size: RsSettingWrite "RsTextureMapStatsRoll" "size" pntSize arrangeCtrls size:pntSize ) on RsTextureMapStatsRoll open do ( init() update() ) fn create retry:false = ( destroyDialog RsTextureMapStatsRoll RS_CustomDataGrid forceRecompile:retry local rollSize = RsTextureMapStatsRoll.GetIniRolloutSize() CreateDialog RsTextureMapStatsRoll modal:false width:rollSize.x height:rollSize.y style:#(#style_resizing, #style_titlebar, #style_border, #style_sysmenu ) ) ) -- End of rollout definition ( -- Try to catch if RsCustomDataGrid fails to work: try (RsTextureMapStatsRoll.create()) catch (RsTextureMapStatsRoll.create retry:true) )