filein "rockstar/export/settings.ms" filein ( RsConfigGetWildWestDir() + "script/max/Rockstar_London/utils/RSL_dotNetClasses.ms" ) filein ( RsConfigGetWildWestDir() + "script/max/Rockstar_London/utils/RSL_dotNetObjects.ms" ) filein ( RsConfigGetWildWestDir() + "script/max/Rockstar_London/utils/RSL_dotNetPresets.ms" ) filein ( RsConfigGetWildWestDir() + "script/max/Rockstar_London/utils/RSL_dotNetUIOps.ms" ) filein ( RsConfigGetWildWestDir() + "script/max/Rockstar_London/pipeline/RSL_LevelConfig.ms" ) global textureStatsInstance struct RSL_Texture ( name = "", size = [0,0], textures = #(), materials = #(), objectsUsedOn = #(), TXDs = #(), faceCount = 0, totalFaces = 0 ) struct RSL_TextureStatsStruct ( ------------------------------------------------------------------------------------------------------------------ --VARIABLES ------------------------------------------------------------------------------------------------------------------ --UI elements textureStatsForm, getKeyObjectForm, getKeyObjectListView, currentObject, currentMaterial, keyObject, keyObjectID, treeListPanel, buttonSize = 32, infoPanel, textureList, textureListFaceCount, textureCounts, txdList, objectList, uniqueTXDs, sceneObjects, selectionOnlyCB, zoomOnClickCB, topPanelHeight = 40, progressBarHeight = 20, nodeList, selectionOnlyState = false, zoomOnClickState = false, iniFilePath = "X:\\tools\\dcc\\current\\max2009\\plugcfg\\TextureStats.ini", reportList = #(), --preset definitions CBS_None = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").None, CBS_Single = (dotNetClass "System.Windows.Forms.TableLayoutPanelCellBorderStyle").Single, DS_Fill = (dotNetClass "System.Windows.Forms.DockStyle").Fill, FBS_FixedToolWindow = (dotNetClass "System.Windows.Forms.FormBorderStyle").FixedToolWindow, FontStyle_Bold = (dotNetClass "System.Drawing.FontStyle").bold, Font_Calibri = dotNetObject "System.Drawing.Font" "Calibri" 8, Font_ArialBold = dotNetObject "System.Drawing.Font" "Arial" 10 FontStyle_Bold, FS_Flat = (dotNetClass "System.Windows.Forms.FlatStyle").Flat, FS_Popup = (dotNetClass "System.Windows.Forms.FlatStyle").Popup, SP_Manual = (dotNetClass "System.Windows.Forms.FormStartPosition").Manual, TA_Center = (dotNetClass "System.Drawing.ContentAlignment").MiddleCenter, TA_Left = (dotNetClass "System.Drawing.ContentAlignment").MiddleLeft, Margin_Zero = dotNetObject "System.Windows.Forms.Padding" 0, --treelist columns attrList = #( "Texture Map", "Object Count", "Objects", "TXD Count", "TXDs", "Faces Mapped", "Total Faces", "Map size" ), --colours RGB_LightGrey = (dotNetClass "System.Drawing.Color").fromARGB 229 229 229, RGB_Steel = (dotNetClass "System.Drawing.Color").FromARGB 200 210 220, ------------------------------------------------------------------------------------------------------------------------------------------ --GENERAL FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn InitButton = ( newButton = dotNetObject "Button" newButton.Size = dotNetObject "System.Drawing.Size" 50 (topPanelHeight - 3) newButton.FlatStyle = FS_Popup newButton.Margin = dotNetObject "System.Windows.Forms.Padding" 2 return newButton ), ------------------------------------------------------------------------------------------------------------------------------------------ fn formatArrayToString inArray token = ( local outString = "" if (inArray.Count > 1) then ( for i = 1 to (inArray.Count - 1) do ( outString += (inArray[i] as string + token as string) ) ) outString += inArray[inArray.Count] outString ), ------------------------------------------------------------------------------------------------------------------------------------------ fn CountFacesUsedOn object ID = ( local result = 0 for face in object.faces do ( case (classOf Object) of ( editable_mesh: ( if (getFaceMatID object face.index) == ID then ( result += 1 ) ) editable_poly: ( if (polyOp.getFaceMatID object face.index) == ID then ( result += 1 ) ) ) ) result ), ------------------------------------------------------------------------------------------------------------------ fn IsUniqueTexture texture array = ( local result = true for entry in array do ( if entry.name == texture.name then ( result = false ) ) result ), ------------------------------------------------------------------------------------------------------------------------------------------ fn GetMaterialIDs objectname = ( local IDList = #() for x = 1 to (getPolygonCount objectname)[1] do ( if classof objectname == Editable_Poly then ( local id = polyop.getFaceMatID objectname x) else ( local id = getFaceMatID objectname x) appendifunique IDList id ) IDList ), ------------------------------------------------------------------------------------------------------------------------------------------ fn GetFaceCount inObject inMaterial = ( faceCount = 0 case (classOf inObject.material) of ( Rage_Shader: ( faceCount = (getPolygonCount inObject)[1] ) multimaterial: ( --find out the material id for inMaterial currentMaterial = inObject.material local keyID materialIDs = GetMaterialIDs inObject for thisID in materialIDs do ( local matIndex = findItem currentMaterial.materialIDList thisID if (matIndex != 0) then --we have a valid matID ( if (currentMaterial.materialList[matIndex] == inMaterial) then keyID = thisID ) ) --get number of faces using the CountFacesUsedOn function if (keyID != undefined) then ( faceCount = CountFacesUsedOn inObject keyID ) ) ) faceCount ), ------------------------------------------------------------------------------------------------------------------------------------------ --LOAD/SAVE FUNCTIONS ------------------------------------------------------------------------------------------------------------------------------------------ fn UpdateLocalConfigFileSave = ( setINISetting iniFilePath "TextureStats" "selectionOnlyState" (textureStatsInstance.selectionOnlyCB.checked as string) setINISetting iniFilePath "TextureStats" "zoomOnClickState" (textureStatsInstance.zoomOnClickCB.checked as string) ), fn UpdateLocalConfigFileLoad = ( try ( textureStatsInstance.selectionOnlyCB.checked = getINISetting iniFilePath "TextureStats" "selectionOnlyState" as BooleanClass textureStatsInstance.zoomOnClickCB.checked = getINISetting iniFilePath "TextureStats" "zoomOnClickState" as BooleanClass ) catch() ), fn DispatchLoadIniFile s e = ( textureStatsInstance.UpdateLocalConfigFileLoad() ), fn DispatchSaveIniFile s e = ( textureStatsInstance.UpdateLocalConfigFileSave() ), ------------------------------------------------------------------------------------------------------------------------------------------ --THE DADDY FUNCTION ------------------------------------------------------------------------------------------------------------------------------------------ fn recurseMaterialForTextures material object &textureArray = ( case (classOf material) of ( default: ( -- append reportList (dataPair error:("-- Data error: Material is not a Rage Shader: " + material.name + " Class: " + (classOf material) as string) data:material) -- ) -- RexBoundMtl: -- ( append reportList ("Material data ignored for " + (classOf material as string) + ":" + material.name) ) Rage_Shader: ( local textureCount = getNumSubTexmaps material local isAlpha = false if (RstGetIsAlphaShader material) then ( textureCount /= 2 isAlpha = true ) for i = 1 to textureCount do ( local subTextureMap = getSubTexmap material i local texture = RSL_Texture() case (classof subTextureMap) of ( Bitmaptexture: ( try ( if subTextureMap.filename != "" then ( local facecount = GetFaceCount object material texture.name = toLower (getFilenameFile subTextureMap.filename) texture.size = [subTextureMap.bitmap.width, subTextureMap.bitmap.height] append texture.objectsUsedOn object --AD line append texture.txds (GetAttr object (GetAttrIndex "Gta Object" "TXD")) --AD line append texture.textures subTextureMap.filename append texture.materials material --AD line texture.faceCount = faceCount --AD line ) if isAlpha then ( local alphaTextureMap = getSubTexmap material (i + textureCount) if (classof alphaTextureMap) == Bitmaptexture and alphaTextureMap.filename != "" then ( texture.name += "+" + (toLower (getFilenameFile alphaTextureMap.filename)) append texture.textures alphaTextureMap.filename ) ) ) catch ( texture = RSL_Texture() append reportList (dataPair error:(getCurrentException()) data:subTextureMap) ) ) CompositeTexturemap: ( local compositeCount = getNumSubTexmaps subTextureMap for c = 1 to compositeCount do ( local compTextureMap = getSubTexmap subTextureMap c try ( if (classof compTextureMap) == Bitmaptexture and compTextureMap.filename != "" then ( if c > 1 then ( texture.name += ">" ) texture.name += toLower (getFilenameFile compTextureMap.filename) append texture.textures compTextureMap.filename local compSize = [compTextureMap.bitmap.width, compTextureMap.bitmap.height] if (length texture.size) < (length compSize) then ( texture.size = compSize ) ) ) catch ( texture = RSL_Texture() append reportList (dataPair error:(getCurrentException()) data:compTextureMap) ) ) ) ) if texture.name != "" and (IsUniqueTexture texture textureArray) then ( append textureArray texture ) ) ) multimaterial: ( for i = 1 to material.materialList.count do ( local subMaterial = material.materialList[i] if subMaterial != undefined then ( if ((CountFacesUsedOn object material.materialIDList[i]) > 0) then ( recurseMaterialForTextures subMaterial object &textureArray ) ) ) ) ) ), ------------------------------------------------------------------------------------------------------------------------------------------ fn GetRSLTexturesFromObject object = ( local result = #() local material = object.material if material != undefined then ( recurseMaterialForTextures material object &result ) result ), ------------------------------------------------------------------------------------------------------------------------------------------ fn GetBitmapByFilename filename = ( local result local bitmapArray = getClassInstances bitmapTexture for entry in bitmapArray do ( if (toLower entry.filename) == (toLower filename) then ( result = entry ) ) result ), ------------------------------------------------------------------------------------------------------------------------------------------ fn UpdateData = ( progressStart "processing textures..." textureList = #() if (selectionOnlyCB.Checked == true) then ( sceneObjects = for this in selection where (getAttrClass this == "Gta Object") and (classOf this != XrefObject) and (classOf this != PolyMeshObject) collect this ) else ( sceneObjects = for this in objects where (getAttrClass this == "Gta Object") and (classOf this != XrefObject) and (classOf this != PolyMeshObject) collect this ) for i = 1 to sceneObjects.count do ( local object = sceneObjects[i] local RSLTextureArray = getRSLTexturesFromObject object if (RSLTextureArray.count > 0) then ( for thisRSLTexture in RSLTextureArray do ( local textureListID = 0 for i = 1 to textureList.count do ( if (textureList[i].name == thisRSLTexture.name) then textureListID = i ) if (textureListID == 0) then --we have a new RSL_Texture ( append textureList thisRSLTexture ) else --need to append and iterate values in the existing item ( appendIfUnique textureList[textureListID].objectsUsedOn thisRSLTexture.objectsUsedOn[1] appendIfUnique textureList[textureListID].txds thisRSLTexture.txds[1] appendIfUnique textureList[textureListID].materials thisRSLTexture.materials[1] textureList[textureListID].faceCount += thisRSLTexture.faceCount ) ) ) progressUpdate = 100 * ((i as float) / (sceneObjects.count as float)) ) for i = 1 to textureList.count do ( local totalFaces = 0 for thisObject in textureList[i].objectsUsedOn do ( totalFaces += (getPolygonCount thisObject)[1] ) textureList[i].totalFaces = totalFaces ) progressEnd() ), ------------------------------------------------------------------------------------------------------------------------------------------ fn UpdateList = ( UpdateData() nodeList = #() treeListPanel.ClearNodes() treeListPanel.BeginUnboundLoad() for i = 1 to textureList.Count do --run through all the attributes appending the value to nodeAttrValues ( local nodeAttrValues = #(textureList[i].name) --texture map append nodeAttrValues (textureList[i].objectsUsedOn.count) --object count local objectsUsedOn = #() for this in textureList[i].objectsUsedOn do --objects ( append objectsUsedOn this.name ) append nodeAttrValues (formatArrayToString objectsUsedOn ", ") append nodeAttrValues (textureList[i].txds.count) --txd count append nodeAttrValues (formatArrayToString textureList[i].txds ", ") --txds append nodeAttrValues (textureList[i].faceCount) --faces mapped append nodeAttrValues (textureList[i].totalFaces) --total faces append nodeAttrValues (textureList[i].size as string) --map size newNode = treeListPanel.AppendNode nodeAttrValues -1 -- value of -1 puts newNode at the root -- newNode.Tag = dotNetMXSValue textureList[i] -- tag new node with the texture list object newNode.Tag = i --tag new node with the textureList id append nodeList newNode ) treeListPanel.EndUnboundLoad() infoPanel.Text = "Listing " + textureList.Count as string + " textures on " + sceneObjects.count as string + " object" if sceneObjects.count > 1 then infoPanel.Text += "s" local polyMeshObjects = for this in objects where (classOf this == PolyMeshObject) collect this if polyMeshObjects.count > 0 then ( infoPanel.Text += "\nWarning: scene contains uncollapsed geometry. These objects have not been listed." ) textureStatsForm.Height = topPanelHeight + 64 + (textureList.Count * 19) --selection clear for this in nodeList do ( this.Selected = false ) ), ------------------------------------------------------------------------------------------------------------------------------------------ --EVENT HANDLERS ------------------------------------------------------------------------------------------------------------------------------------------ fn DispatchToggleSelectionOnlyMode s e = textureStatsInstance.UpdateList(), fn DispatchUpdateList s e = textureStatsInstance.UpdateList(), fn CloseForm s e = textureStatsInstance.CloseForm s e, fn DispatchDoubleClick s e = textureStatsInstance.DoubleClick s e, fn HelpWiki s e = ( shellLaunch "https://mp.rockstargames.com/index.php/Texture_Stats" "" ), fn SelectFacesWithMaterial inObject materialList = ( local facesToSelect = #() setCommandPanelTaskMode #modify select inObject subobjectlevel = 4 case (classOf inObject.material) of ( Rage_Shader: ( for thisMaterial in materialList do ( if (thisMaterial == inObject.material) then ( select inObject.faces ) ) ) multimaterial: ( local facesToSelect = #() for thisMaterial in materialList do ( local numFaces = (getPolygonCount inObject)[1] for thisFace = 1 to numFaces do ( local matid case (classOf inObject) of ( Editable_Poly: ( matid = polyop.getFaceMatID inObject thisFace ) Editable_Mesh: ( matid = getFaceMatID inObject thisFace ) ) local matIndex = finditem inObject.material.materialIDList matid if (matIndex != 0) then ( if (inObject.material.materialList[matIndex] == thisMaterial) then append facesToSelect thisFace ) ) ) if (facesToSelect.count > 0) then ( select inObject.faces[facesToSelect] ) -- else print "oops" ) ) ), fn DispatchObjectPicked s e = textureStatsInstance.ObjectPicked s e, fn ObjectPicked s e = ( SelectFacesWithMaterial e.item.Tag.Value currentMaterial getKeyObjectForm.Close() ), -- Will select all the object instances fn RickClick s e = ( local nodeClicked = treeListPanel.FocusedNode local textureListItem = textureList[nodeClicked.Tag] ), fn DoubleClick s e = ( local columnClicked = treeListPanel.FocusedColumn.Caption local nodeClicked = treeListPanel.FocusedNode local textureListItem = textureList[nodeClicked.Tag] if (columnClicked == "Objects") then ( local objectList = textureListItem.objectsUsedOn if (objectList.count > 1) then ( local keyID if (keyObject != undefined) then ( keyID = findItem objectList keyObject if (keyID == 0 or keyID == objectList.count) then ( keyObject = objectList[1] ) else ( keyObject = objectList[keyID + 1] ) ) else ( keyObject = objectList[1] ) keyID = findItem objectList keyObject infoPanel.Text = "Object (" + keyID as string + " / " + objectList.count as string infoPanel.Text += "): " + keyObject.Name + "\nTexture: " + nodeClicked.Item[0] SelectFacesWithMaterial keyObject textureListItem.materials ) else ( infoPanel.Text = "Object: " + objectList[1].Name + "\nTexture: " + nodeClicked.Item[0] SelectFacesWithMaterial objectList[1] textureListItem.materials ) if (zoomOnClickCB.Checked == true) then ( max zoomext sel all ) ) if (columnClicked == "Texture Map") then ( local bitmapname = getBitmapByFilename textureListItem.textures[1] display bitmapname.bitmap infoPanel.Text = "Displaying texture: " + nodeClicked.Item[0] ) ), fn DispatchMouseClick s e = textureStatsInstance.MouseClick s e, fn MouseClick s e = ( -- if e.button == e.button.middle then print "middle" -- if e.button == e.button.left then print "left" if e.button == e.button.right then ( clearSelection() local nodeClicked = treeListPanel.FocusedNode local textureListItem = textureList[nodeClicked.Tag] local objectList = textureListItem.objectsUsedOn if (objectList.count > 1) then ( for i in objectList do ( selectmore i ) ) ) ), fn CloseForm s e = textureStatsInstance.textureStatsForm.close(), fn DispatchResizeInfoPanel s e = textureStatsInstance.ResizeInfoPanel s e, fn ResizeInfoPanel s e = infoPanel.Width = (textureStatsForm.Width - selectionOnlyCB.Width - zoomOnClickCB.Width - 190), ------------------------------------------------------------------------------------------------------------------------------------------ --UI ------------------------------------------------------------------------------------------------------------------------------------------ fn CreateUI = ( -- form setup textureStatsForm = dotNetObject "maxCustomControls.maxForm" textureStatsForm.Text = "Texture Map Statistics" textureStatsForm.Size = dotNetObject "System.Drawing.Size" 1280 480 textureStatsForm.MaximumSize = dotNetObject "System.Drawing.Size" 1600 1100 textureStatsForm.MinimumSize = dotNetObject "System.Drawing.Size" 640 180 textureStatsForm.SizeGripStyle = (dotNetClass "SizeGripStyle").show textureStatsForm.StartPosition = SP_Manual textureStatsForm.Location = dotNetObject "system.drawing.point" 0 0 textureStatsForm.FormBorderStyle = (dotNetClass "System.Windows.Forms.FormBorderStyle").SizableToolWindow dotNet.AddEventHandler textureStatsForm "Load" DispatchLoadIniFile dotNet.AddEventHandler textureStatsForm "Closing" DispatchSaveIniFile textureStatsToolTip = dotnetobject "ToolTip" --table setup textureStatsTable = dotNetObject "TableLayoutPanel" textureStatsTable.backColor = RGB_Steel textureStatsTable.dock = DS_fill textureStatsTable.cellBorderStyle = CBS_None textureStatsTable.columnCount = 1 textureStatsTable.columnStyles.add (RS_dotNetObject.columnStyleObject "percent" 100) textureStatsTable.rowCount = 2 textureStatsTable.rowStyles.add (RS_dotNetObject.rowStyleObject "absolute" topPanelHeight) textureStatsTable.rowStyles.add (RS_dotNetObject.rowStyleObject "percent" 100) -- textureStatsTable.rowStyles.add (RS_dotNetObject.rowStyleObject "absolute" progressBarHeight) textureStatsForm.Controls.Add textureStatsTable textureStatsTopPanel = dotNetObject "FlowLayoutPanel" textureStatsTopPanel.Dock = DS_Fill textureStatsTopPanel.BackColor = RGB_Steel textureStatsTopPanel.Margin = dotNetObject "System.Windows.Forms.Padding" 0 textureStatsTable.Controls.Add textureStatsTopPanel 0 0 selectionOnlyCB = dotNetObject "Checkbox" selectionOnlyCB.Text = "Selection Only" selectionOnlyCB.Font = Font_Calibri selectionOnlyCB.Width = 90 selectionOnlyCB.Height = topPanelHeight selectionOnlyCB.TextAlign = (dotNetClass "System.Drawing.ContentAlignment").MiddleLeft selectionOnlyCB.Margin = dotNetObject "System.Windows.Forms.Padding"1 selectionOnlyCB.Checked = selectionOnlyState textureStatsToolTip.SetToolTip selectionOnlyCB "List data for selected objects only" textureStatsTopPanel.Controls.Add selectionOnlyCB zoomOnClickCB = dotNetObject "Checkbox" zoomOnClickCB.Text = "Zoom On Double-Click" zoomOnClickCB.Font = Font_Calibri zoomOnClickCB.Width = 130 zoomOnClickCB.Height = topPanelHeight zoomOnClickCB.TextAlign = (dotNetClass "System.Drawing.ContentAlignment").MiddleLeft zoomOnClickCB.Margin = dotNetObject "System.Windows.Forms.Padding"1 zoomOnClickCB.Checked = zoomOnClickState textureStatsToolTip.SetToolTip zoomOnClickCB "Zoom in on geometry when a texture in the list is double-clicked" textureStatsTopPanel.Controls.Add zoomOnClickCB infoPanel = dotNetObject "Label" infoPanel.Text = "Texture Map Statistics" infoPanel.Font = Font_ArialBold infoPanel.Width = (textureStatsForm.Width - selectionOnlyCB.Width - zoomOnClickCB.Width - 190) infoPanel.Height = topPanelHeight - 3 infoPanel.TextAlign = TA_Left infoPanel.BackColor = RGB_LightGrey infoPanel.BorderStyle = (dotNetClass "System.Windows.Forms.BorderStyle").Fixed3D infoPanel.Margin = dotNetObject "System.Windows.Forms.Padding" 2 textureStatsTopPanel.Controls.Add infoPanel dotNet.AddEventHandler textureStatsForm "Resize" DispatchResizeInfoPanel updateButton = InitButton() updateButton.Text = "Update" textureStatsToolTip.SetToolTip updateButton "Update table to reflect latest changes" textureStatsTopPanel.Controls.Add updateButton dotNet.AddEventHandler updateButton "Click" DispatchUpdateList helpButton = InitButton() helpButton.Text = "Help" textureStatsToolTip.SetToolTip helpButton "Open the help wiki in a browser window" textureStatsTopPanel.Controls.Add helpButton dotNet.AddEventHandler helpButton "Click" HelpWiki closeButton = InitButton() closeButton.Text = "Close" textureStatsToolTip.SetToolTip closeButton "Close the tool" textureStatsTopPanel.Controls.Add closeButton dotNet.AddEventHandler closeButton "Click" CloseForm ------------------------------------------------------------------------------------------------------------------------------------------ --xtra tree list treeListPanel = dotNetObject "DevExpress.XtraTreeList.TreeList" treeListPanel.Dock = DS_Fill treeListPanel.OptionsSelection.MultiSelect = false treeListPanel.OptionsView.ShowRoot = false treeListPanel.OptionsView.ShowIndicator = false treeListPanel.OptionsBehavior.Editable = false treeListPanel.OptionsView.EnableAppearanceEvenRow = true --colours the rows alternately treeListPanel.OptionsView.EnableAppearanceOddRow = true --colours the rows alternately treeListPanel.BeginUpdate() for i = 0 to (attrList.Count - 1) do ( treeListPanel.Columns.Add() treeListPanel.Columns.Item[i].Caption = attrList[i + 1] treeListPanel.Columns.Item[i].VisibleIndex = i treeListPanel.Columns.Item[i].Width = 80 treeListPanel.Columns.Item[i].OptionsColumn.AllowEdit = false treeListPanel.Columns.Item[i].OptionsColumn.AllowSize = true treeListPanel.Columns.Item[i].OptionsColumn.AllowMove = false ) treeListPanel.Columns.Item[0].Width = 320 treeListPanel.Columns.Item[2].Width = 320 treeListPanel.Columns.Item[4].Width = 200 treeListPanel.EndUpdate() textureStatsTable.Controls.Add treeListPanel 0 1 dotNet.AddEventHandler treeListPanel "DoubleClick" DispatchDoubleClick dotNet.AddEventHandler treeListPanel "MouseClick" DispatchMouseClick ------------------------------------------------------------------------------------------------------------------------------------------ --draw form UpdateList() textureStatsForm.showModeless() if (reportList != #()) then ( clearListener() print "Report Data" print reportList ) textureStatsForm ) ) ------------------------------------------------------------------------------------------------------------------ --CREATE WINDOW ------------------------------------------------------------------------------------------------------------------ if textureStatsInstance != undefined then ( try ( textureStatsInstance.textureStatsForm.close() ) catch() ) textureStatsInstance = RSL_TextureStatsStruct() textureStatsInstance.CreateUI()