Files
2025-09-29 00:52:08 +02:00

994 lines
31 KiB
Plaintext
Executable File

-- Scene shader-optimiser:
-- Looks for materials in scene that can be auto-optimised, and offers to apply optimisations.
-- Neal D Corbett 13/07/2012
try (destroyDialog RsShaderOptimiserRoll) catch ()
rollout RsShaderOptimiserRoll "Shader Optimiser [v1.19:DidacticWire]" width:320 height:100
(
-- Phrase generated via: (filein (RsConfigGetWildWestDir() + "script/3dsmax/_config_files/wildwest_header.ms"); print (RsRandomPhrase count:100))
local shadersPath = RsConfigGetCommonDir() + "shaders/db/*.sps"
local shaderNames = for filename in (getFiles shadersPath) collect (toLower (getFilenameFile filename))
local bannerHeight = 32
dotNetControl RsBannerPanel "Panel" pos:[0,0] height:bannerHeight width:RsShaderOptimiserRoll.width
local banner = makeRsBanner dn_Panel:RsBannerPanel wiki:"ShaderOptimiser" filename:(getThisScriptFilename())
button btnCheckForNonoptimal "Find Non-Optimal Shaders" pos:(RsBannerPanel.pos + [5, bannerHeight + 5]) width:(RsShaderOptimiserRoll.width - 10) height:50
button btnOpenCSV "Edit Alternative-Shaders List" tooltip:"Opens CSV-file containing alternative shadernames used by tool" pos:(btnCheckForNonoptimal.pos + [0, 55]) width:(RsShaderOptimiserRoll.width - 10) height:30
-- This CSV-file contains a list of alternative shader-names:
local listFilename = RsConfigGetProjRootDir() + "/assets/maps/shader_optimiser.csv"
-- Suggested changes are added to this array:
local suggestionList = #()
local shaderLookupList, shaderLookupListNames
fn loadShaderLookupList =
(
local readFile = openFile listFilename
if (readFile == undefined) do return false
struct shaderLookupItem
(
shaderName,
-- Data loaded from csv:
noBump, noSpec, noAlpha, noReflect,
noBumpSpec, noBumpAlpha, noBumpReflect,
noSpecAlpha, noSpecReflect, noAlphaReflect,
noBumpSpecAlpha, noBumpSpecReflect, noSpecAlphaReflect, noBumpAlphaReflect,
noBumpSpecAlphaReflect,
-- Used to say what features this shader supports:
processed = False,
isAlphaShader = False,
isCutOut = False, isAlpha = False, isBump = False, isSpec = False, isReflect = False,
fn initFeatures mat =
(
-- Only needs to be run once per shadername:
if processed do return True
-- Is this is a Cutout Alpha shader? Check name:
isCutout = (matchPattern shaderName pattern:"*cutout*")
isAlpha = (isCutout)
-- Set features to True if CSV included alternates for shaders missing particular features:
for propName in (getPropNames This) where (matchPattern propName pattern:"no*") do
(
if ((getProperty This propName) != undefined) do
(
if (not isAlpha) and (matchPattern propName pattern:"*alpha*") do
(
isAlpha = True
)
if (not isSpec) and (matchPattern propName pattern:"*spec*") do
(
isSpec = True
)
if (not isBump) and (matchPattern propName pattern:"*bump*") do
(
isBump = True
)
if (not isReflect) and (matchPattern propName pattern:"*reflect*") do
(
isReflect = True
)
)
)
if isAlpha do
(
isAlphaShader = RstGetIsAlphaShader mat
)
processed = True
)
)
shaderLookupList = #()
local headerNames
local validHeaders = #{}
while not eof readFile do
(
local thisLine = (toLower (trimRight (RsRemoveCharacterFromString (readLine readFile) "\"")))
case of
(
(thisLine == ""):() -- Ignore empty/whitespace lines
(thisLine[1] == "#"):() -- Ignore hashed comments
(headerNames == undefined): -- First line is header
(
headerNames = for token in (filterString thisLine ",") collect ((trimLeft (trimRight token)) as name)
-- Only use header-names that are in struct:
local structPropNames = (getPropNames shaderLookupItem)
for n = 1 to headerNames.count do
(
validHeaders[n] = (findItem structPropNames headerNames[n] != 0)
)
)
Default:
(
-- Load the actual data-lines:
local lineTokens = for token in (filterString thisLine "," splitEmptyTokens:True) collect (trimLeft (trimRight token))
if (lineTokens.count != 0) do
(
local newItem = shaderLookupItem()
for n = validHeaders do
(
local thisToken = lineTokens[n]
if (thisToken != undefined) and (thisToken != "") do
(
setProperty newItem headerNames[n] thisToken
)
)
append shaderLookupList newItem
)
)
)
)
close readFile
shaderLookupListNames = for item in shaderLookupList collect item.shaderName
return true
)
-- Used to find the maximum value, or range of values, used on a bitmap:
fn getBmpRange filename bmpResize:64 maxValue:false getAlphaType:false rgbaChans:#{1..3} slotNum:1 =
(
if getAlphaType do
(
rgbaChans = #{4}
)
local fileBmp = openBitmap filename
local fileBmpHasAlpha = fileBmp.hasAlpha
-- Abort check if doing alpha-check on non-alpha bitmap in first slot:
if (getAlphaType) and (slotNum == 1) and (not fileBmpHasAlpha) do
(
close fileBmp
return #none
)
local doResize = (isKindOf bmpResize number)
local readBmp
-- Scale down bitmap, to speed up processing:
if doResize then
(
local bmpSize = [bmpResize,bmpResize]
local RGBOutputIdx = if (getAlphaType and (slotNum == 1)) then 1 else 0
readBmp = renderMap (bitmapTexture bitmap:fileBmp RGBOutput:RGBOutputIdx) size:bmpSize filter:false
close fileBmp
)
else
(
readBmp = fileBmp
)
local bmpWidth = readBmp.width
local usedValsR = #{}
local usedValsG = #{}
local usedValsB = #{}
local usedValsA = #{}
local usedVals = #(usedValsR, usedValsG, usedValsB, usedValsA)
for item in usedVals do (item.count = 256)
local doR = rgbaChans[1]
local doG = rgbaChans[2]
local doB = rgbaChans[3]
local doA = getAlphaType or rgbaChans[4]
local alphaLineA = #{}
local alphaLineB = #{}
local alphaLineC = #{}
local isCutout = true
for rowNum = 0 to (readBmp.height - 1) do
(
local readRow = getPixels readBmp [0, rowNum] bmpWidth
if doR do
(
for clr in readRow do (usedValsR[clr.R + 1] = true)
)
if doG do
(
for clr in readRow do (usedValsG[clr.G + 1] = true)
)
if doB do
(
for clr in readRow do (usedValsB[clr.B + 1] = true)
)
if doA do
(
local alphaVals = if (fileBmpHasAlpha and (not doResize)) then
(
for clr in readRow collect clr.a
)
else
(
-- Assuming rgb is monochrome, so just need to read one colour-channel:
for clr in readRow collect clr.r
)
for val in alphaVals do (usedValsA[val + 1] = true)
-- Set as non-cutout if bitmap contains non-monochrome pixels with no monochrome neighbours:
if getAlphaType and isCutout do
(
alphaLineA = alphaLineB
alphaLineB = alphaLineC
-- Set alphaLineC as bitarray of monochrome/non-monochrome pixels:
for n = 1 to alphaVals.count do
(
alphaLineC[n] = ((alphaVals[n] <= 10) or (alphaVals[n] >= 245))
)
-- Only start checking once all alphaLine arrays are filled:
if (rowNum > 1) do
(
-- Check non-monochrome pixels:
for n = 2 to (alphaLineB.count - 1) where (not alphaLineB[n]) while isCutout do
(
-- True if any neighbour is monochrome:
isCutout = alphaLineA[n - 1] or alphaLineA[n] or alphaLineA[n + 1] or alphaLineB[n - 1] or alphaLineB[n + 1] or alphaLineC[n - 1] or alphaLineC[n] or alphaLineC[n + 1]
)
)
)
)
)
if doResize then
(
close readBmp
)
else
(
close fileBmp
)
-- Return a point4 if showing alpha, else return a point3:
local valRange = if (rgbaChans.count == 4) then [0,0,0,0] else [0,0,0]
local retAlphaType
-- Find value-range for requested channels:
for n = rgbaChans do
(
-- Get bitarray of values, as array:
local chanVals = usedVals[n] as array
-- Get max value:
valRange[n] = chanVals[chanVals.count] - 1
-- Check for alpha/cutout:
if getAlphaType and (n == 4) do
(
-- Bno has suitable alpha-channel if lowest value is noticably less than white, or it has a reasonable difference between its dark/bright values:
local hasAlpha = ((chanVals[1] <= 245) or ((valRange[n] - chanVals[1] - 1) > 10))
-- Has Cutout if bmp only contains very dark/bright values:
local hasCutout = hasAlpha
for val in chanVals while hasCutout do
(
hasCutout = ((val <= 10) or (val >= 245))
)
retAlphaType = case of
(
hasCutout:#cutout
hasAlpha:#alpha
default:#none
)
)
-- Get max/min range:
if not maxValue do
(
valRange[n] -= (chanVals[1] - 1)
)
)
-- Return alpha-type or 0->1 ranged values:
if getAlphaType then (return retAlphaType) else (return (valRange / 255.0))
)
-- Function to replace instances of mat is scene with a Rage version:
fn replaceStdWithRage mat unused =
(
local newMat = RstCreateRstMtlFromStdMtl mat
for obj in geometry do
(
if isKindOf obj.material multiMaterial then
(
local matList = obj.material.materialList
for n = 1 to matList.count where (matList[n] == mat) do
(
matList[n] = newMat
)
)
else
(
if obj.material == mat do
(
obj.material = newMat
)
)
)
)
-- Called when list is double-clicked:
fn listDoubleClick Sender Args =
(
local rowNum = args.RowIndex + 1
if (rowNum <= 0) do return false
-- Open new material-editor (closing first, as existing one can't be focused directly)
-- (also sets to non-Slate mode, as I don't think Slate can be controlled from script)
MatEditor.Close()
MatEditor.mode = #basic
MatEditor.Open()
local clickedMat = suggestionList[rowNum].mat
if (clickedMat != undefined) do
(
local matEditSlot = medit.GetActiveMtlSlot()
medit.PutMtlToMtlEditor clickedMat matEditSlot
)
Sender.Parent.Dispose()
)
fn checkForNonOptimal =
(
if (loadShaderLookupList() == False) do
(
messageBox ("Aborting: Failed to load list:\n\n" + listFilename) title:"Error: List-file not loaded"
return false
)
local timeStart = timeStamp()
local notCancelled = true
local objs = for obj in geometry where (isEditPolyMesh obj) collect obj
local objsCount = objs.count
progressStart ("Collecting mats from " + (objsCount as string) + " objs:")
local matList = #()
local objFaceMats = for objNum = 1 to objsCount while (notCancelled = progressUpdate (100.0 * objNum / objsCount)) collect
(
local obj = objs[objNum]
local matFaces = (for item in matList collect #{})
RsGetMaterialsOnObjFaces obj materials:matList faceLists:matFaces
dataPair obj:obj matFaces:matFaces
)
progressEnd()
pushPrompt "Filtering down to unique materials..."
(
local uniqueMats = makeUniqueArray matList
if (matList.count != uniqueMats.count) do
(
-- Generate lookup-list:
local newIdxList = for matNum = 1 to matList.count collect (findItem uniqueMats matList[matNum])
local nonUniques = #{}
nonUniques.count = matList.count
for n = 1 to newIdxList.count where (newIdxList[n] != n) do
(
nonUniques[n] = True
)
-- Change matFaces for each object to use new indexes:
for item in objFaceMats do
(
local matFaces = item.matFaces
for matNum = 1 to matFaces.count where nonUniques[matNum] do
(
local theseMatFaces = matFaces[matNum]
if (theseMatFaces.numberSet != 0) do
(
local newIdx = newIdxList[matNum]
-- Move these faces to the face-list for equivalent unique material:
matFaces[newIdx] += theseMatFaces
theseMatFaces.count = 0
)
)
)
matList = uniqueMats
)
)
popPrompt()
format "Data-aquisition time: %s\n" ((timeStamp() - timeStart) / 1000.0)
if (not notCancelled) do (return false)
struct matSuggest (mat, action = "", reason = "", func, arg, ticked = true)
suggestionList = #()
-- Threshold for low-value multipliers:
local varThreshold = 0.02 -- Multiplier-value
local texThreshold = 0.03 -- Bitmap-channel value
local varTexThreshold = 0.01 -- Combined value
local matCount = matList.count
progressStart ("Examining " + (matCount as string) + " materials:")
local timeStart = timeStamp()
listedMats = #()
for matNum = 1 to matCount while (notCancelled = progressUpdate (100.0 * matNum / matCount)) do
(
local mat = matList[matNum]
case classOf mat of
(
StandardMaterial:
(
append suggestionList (matSuggest mat:mat action:"Replace with Rage material" reason:"Uses StandardMaterial" func:replaceStdWithRage)
)
Rage_Shader:
(
local matShaderName = toLower (getFilenameFile (RstGetShaderName mat))
local lookupNum = findItem shaderLookupListNames matShaderName
-- Check spec/bump data for listed shaders:
if (lookupNum != 0) do
(
local shaderItem = shaderLookupList[lookupNum]
-- Make sure shader's fingerprint has been taken, to see what features need to be tested for:
shaderItem.initFeatures mat
local missingFiles = #()
local noDiffTex = true
local hasDiffSlot = false
local noAlphaTex = true
local alphaType = #none
local noSpecTex = true
local noSpecVal = false
local hasSpecSlot = false
local noBumpTex = true
local noBumpVal = false
local hasBumpSlot = false
local noReflectTex = true
local noReflectVal = false
local hasReflectSlot = false
local numMaps = getNumSubTexmaps mat
local numVars = RstGetVariableCount mat
local texMapNum = 0
local bumpVal = 1.0
local specVal = 1.0
local reflectVal = 1.0
local specChanMult = [1.0, 1.0, 1.0]
local alphaBmpVals, specBmpVals, bumpBmpRanges
local alphaTexNums = #{}
local alphaTexOffset = (numMaps / 2)
-- Check variables:
for varNum = 1 to numVars do
(
local varType = RstGetVariableType mat varNum
local varName = RstGetVariableName mat varNum
case varType of
(
"texmap":
(
-- No-Specular/Bump flags are turned OFF if matching bitmaps are found:
texMapNum += 1
local texMap = getSubTexmap mat texMapNum
local filename = ""
local fileExists = False
-- Does this slot set a valid filename?
if (isKindOf texMap Bitmaptexture) and (texMap.filename != "") do
(
filename = texMap.filename
fileExists = (doesFileExist filename)
if not fileExists do
(
append missingFiles filename
)
)
case of
(
(RsIsDiffuseMap varName):
(
hasDiffSlot = True
if fileExists do
(
if (shaderItem.isAlphaShader) do
(
local alphaIdx = (texMapNum + alphaTexOffset)
alphaTexNums[alphaIdx] = True
)
noDiffTex = False
)
)
((noSpecTex) and (shaderItem.isSpec) and (RsIsSpecMap varName)):
(
hasSpecSlot = True
if fileExists do
(
specBmpVals = getBmpRange texMap.filename maxValue:true
noSpecTex = false
)
)
((noBumpTex) and (shaderItem.isBump) and (RsIsBumpMap varName)):
(
hasBumpSlot = True
if fileExists do
(
bumpBmpRanges = getBmpRange texMap.filename maxValue:false rgbaChans:#{1,2}
noBumpTex = false
)
)
((noReflectTex) and (shaderItem.isReflect) and (matchPattern varName pattern:"Environment*")):
(
hasReflectSlot = True
if fileExists do
(
noReflectTex = false
)
)
)
)
"vector3":
(
case varName of
(
"specular map intensity mask color":
(
specChanMult = RstGetVariable mat varNum
)
)
)
"float":
(
-- No-Specular/Bump-value flags are turned ON if matching values are zero:
case varName of
(
"Bumpiness":
(
bumpVal = RstGetVariable mat varNum
if (bumpVal <= varThreshold) do (noBumpVal = true)
)
"Specular Intensity":
(
specVal = RstGetVariable mat varNum
if (specVal <= varThreshold) do (noSpecVal = true)
)
"Reflectivity":
(
reflectVal = RstGetVariable mat varNum
if (reflectVal <= varThreshold) do (noReflectVal = true)
)
)
)
)
)
-- Check diffuse-alphas, if found:
if shaderItem.isAlpha do
(
for alphaIdx in alphaTexNums while noAlphaTex do
(
local texMapNums = #((alphaIdx - alphaTexOffset), (alphaIdx))
for slotNum in #(2,1) while noAlphaTex do
(
local texMapNum = texMapNums[slotNum]
local texMap = getSubTexmap mat texMapNum
if (isKindOf texMap Bitmaptexture) and (texMap.filename != "") do
(
local filename = texMap.filename
if doesFileExist filename then
(
alphaType = getBmpRange filename getAlphaType:True slotNum:slotNum
-- Will be set to False if texture was found in slot 2, or if slot 1 provides alpha:
noAlphaTex = ((slotNum == 1) and (alphaType == #None))
)
else
(
append missingFiles filename
)
)
)
-- If Alpha-texture wasn't found, check vert-alphas:
local uvChannel_vertAlpha = -2
for item in objFaceMats while noAlphaTex do
(
local thisMatFaces = item.matFaces[matNum]
-- If this object uses this material, test its vert-alpha:
if (thisMatFaces != undefined) and (thisMatFaces.numberSet != 0) do
(
local thisObj = item.obj
local objOp = RsMeshPolyOp thisObj
if (objOp.getMapSupport thisObj uvChannel_vertAlpha) do
(
local objGetMapFace = RsGetMapFaceFunc thisObj
local objGetMapVert = objOp.getMapVert
for faceNum = thisMatFaces while noAlphaTex do
(
local alphaVerts = objGetMapFace thisObj uvChannel_vertAlpha faceNum
for vertNum in alphaVerts while noAlphaTex do
(
local vertVal = objGetMapVert thisObj uvChannel_vertAlpha vertNum
-- If vert-alpha is non-white, we no longer want to warn about lack of alpha-texture:
if (vertVal[1] != 1) do
(
noAlphaTex = False
alphaType = #vertex
)
)
)
)
)
)
)
)
if (missingFiles.count == 0) then
(
local badAlpha = false
local badSpec = false
local badBump = false
local badReflect = false
-- Reasons are given a line each:
local reasons = #()
if shaderItem.isAlpha do
(
-- Does material have a suitable alpha-channel?
if ((not noAlphaTex) and (alphaType == #none)) do (badAlpha = true; append reasons "Alpha-channel all is all white (or close to it)")
if (noAlphaTex and shaderItem.isAlphaShader) do (badAlpha = true; append reasons "Unused diffuse-alpha texture slot.")
)
if shaderItem.isSpec do
(
-- Does bitmap have all specular-channel multipliers turned down too low?
local validSpecMults = false
for n = 1 to 3 while not validSpecMults do
(
validSpecMults = (specChanMult[n] > varThreshold)
)
if not validSpecMults do (badSpec = true; append reasons "Specular-channel multipliers are set to negligible values.")
if noSpecVal do (badSpec = true; append reasons "Specular Intensity value is negligible.")
-- Check bitmap values, if multipliers are valid:
if (not badSpec) and (specBmpVals != undefined) do
(
local ineffectiveTex = true
local ineffectiveMultTex = true
-- Check rgb values:
for n = 1 to 3 while (ineffectiveTex or ineffectiveMultTex) do
(
local bmpChanVal = specBmpVals[n]
local bmpChanMult = specChanMult[n]
-- Does this specular-texture have a channel above the threshold?
if ineffectiveTex and (bmpChanVal > texThreshold) do
(
ineffectiveTex = false
)
-- See if the bitmap is still effective once the multipliers are applied, if they were accepted:
if ineffectiveMultTex and ((bmpChanVal * bmpChanMult * specVal) > varTexThreshold) do
(
ineffectiveMultTex = false
)
)
case of
(
ineffectiveTex:(badSpec = true; append reasons "Specular Texture is too dark to be effective.")
ineffectiveMultTex:(badSpec = true; append reasons "Specular Texture's values are ineffective once multiplier-values are applied.")
)
)
if (hasSpecSlot and noSpecTex) do
(
badSpec = true; append reasons "Unused Specular Texture slot."
)
)
if shaderItem.isBump do
(
if noBumpTex do (badBump = true; append reasons "Unused Bump Texture slot.")
if noBumpVal do (badBump = true; append reasons "Bumpiness value is negligible.")
-- Check bitmap values, if multipliers are valid:
if (not badBump) and (bumpBmpRanges != undefined) do
(
local ineffectiveTex = true
local ineffectiveMultTex = true
-- Check red/green ranges:
for n = 1 to 2 while (ineffectiveTex or ineffectiveMultTex) do
(
local bmpChanVal = bumpBmpRanges[n]
-- Does this bump-texture have a channel-range above the threshold?
if ineffectiveTex and (bmpChanVal > texThreshold) do
(
ineffectiveTex = false
)
-- See if the bitmap is still effective once the multipliers are applied, if they were accepted:
if ineffectiveMultTex and ((bmpChanVal * bumpVal) > varTexThreshold) do
(
ineffectiveMultTex = false
)
)
case of
(
ineffectiveTex:(badBump = true; append reasons "Bump Texture's value-ranges are too small to be effective.")
ineffectiveMultTex:(badBump = true; append reasons "Bump Texture's value-ranges are too small to be effective once Bumpiness multiplier is applied.")
)
)
)
if shaderItem.isReflect do
(
if noReflectVal then (badReflect = true; append reasons "Reflectivity value is negligible.") else
(
if (not noReflectTex) do
(
append suggestionList (matSuggest action:"Check suitability?\nThis is mainly used for interiors" mat:mat reason:"Uses Environment Texture" ticked:false)
)
)
)
--format "%: % (%) badSpec:% badBump:% badAlpha:% badReflect:%\n" mat.name matShaderName lookupNum badSpec badBump badAlpha badReflect
-- See if shader has a suggested alternative name, if it's missing texturemaps:
local suggestedName
local testList =
#(
datapair test:(badSpec and badBump and badAlpha and badReflect) val:(shaderItem.noBumpSpecAlphaReflect),
datapair test:(badSpec and badBump and badReflect) val:(shaderItem.noBumpSpecReflect),
datapair test:(badSpec and badAlpha and badReflect) val:(shaderItem.noSpecAlphaReflect),
datapair test:(badBump and badAlpha and badReflect) val:(shaderItem.noBumpAlphaReflect),
datapair test:(badSpec and badReflect) val:(shaderItem.noSpecReflect),
datapair test:(badBump and badReflect) val:(shaderItem.noBumpReflect),
datapair test:(badAlpha and badReflect) val:(shaderItem.noAlphaReflect),
datapair test:(badReflect) val:(shaderItem.noReflect),
datapair test:(badSpec and badBump and badAlpha) val:(shaderItem.noBumpSpecAlpha),
datapair test:(badSpec and badBump) val:(shaderItem.noBumpSpec),
datapair test:(badSpec and badAlpha) val:(shaderItem.noSpecAlpha),
datapair test:(badBump and badAlpha) val:(shaderItem.noBumpAlpha),
datapair test:(badSpec) val:(shaderItem.noSpec),
datapair test:(badBump) val:(shaderItem.noBump),
datapair test:(badAlpha) val:(shaderItem.noAlpha)
)
for item in testList where item.test while (suggestedName == undefined) do
(
suggestedName = item.val
)
-- If possible, swap alpha out for cutout shader:
if shaderItem.isAlpha and (not shaderItem.isCutout) and (alphaType == #Cutout) do
(
local checkName = if (suggestedName == undefined) then matShaderName else suggestedName
local alphaIdx = findString checkName "alpha"
-- Generate alternative shader-name with "cutout" replacing "alpha":
local newNameStart = (substring checkName 1 (alphaIdx - 1))
local newNameEnd = (substring checkName (alphaIdx + 5) -1)
local newSuggestion = (newNameStart + "cutout" + newNameEnd)
-- Only swap for this name if it's on our list:
if (findItem shaderLookupListNames newSuggestion) != 0 do
(
suggestedName = newSuggestion
append reasons "Diffuse-alpha contains only black/white (or near enough) values"
)
)
-- Generate change-reason string:
local reason = stringStream ""
for n = 1 to reasons.count do
(
format "%" reasons[n] to:reason
if (n != reasons.count) do
(
format "\n" to:reason
)
)
if (suggestedName != undefined) do
(
local action = ("Change shader: \n'" + matShaderName + "' -> '" + suggestedName + "'")
append suggestionList (matSuggest mat:mat action:action reason:(reason as string) func:RstSetShaderName arg:suggestedName)
)
)
else
(
for filename in missingFiles do
(
local msg = "Missing file: " + filename
append suggestionList (matSuggest action:"Sync/find textures" mat:mat reason:msg ticked:false)
)
)
)
)
)
)
format "Material-processing time: %s\n" ((timeStamp() - timeStart) / 1000.0)
progressEnd()
if (not notCancelled) do (return false)
if (suggestionList.count == 0) then
(
messageBox "No auto-optimisable materials were found.\n\nHooray!" title:"No optimisations found" beep:false
)
else
(
local queryListLabels = #(" ", "Material", "Warning", "Recommended Action")
local queryList = for item in suggestionList collect #(item.mat.name, item.reason, item.action)
local queryListTicks = #{}
local queryVal = RsQueryBoxMultiBtn "Possible material-optimisations were found.\n\nDo you want to perform these changes?\n\n(double-click to open in Material Editor)" title:"Change materials?" \
timeout:-1 width:800 labels:#("Perform ticked actions", "Cancel") tooltips:#("Perform the suggested material optimisations ticked on the list") \
listItems:queryList listTitles:queryListLabels listCheckStates:queryListTicks listDoubleClickFunc:listDoubleClick
if (queryVal == 1) do
(
-- Perform ticked actions:
for itemNum = queryListTicks do
(
local actionItem = suggestionList[itemNum]
if (actionItem.func != undefined) do
(
actionItem.func actionItem.mat actionItem.arg
)
)
)
)
-- Clear suggestion-list:
suggestionList.count = 0
return OK
)
on btnCheckForNonoptimal pressed do
(
checkForNonOptimal()
setFocus RsShaderOptimiserRoll
)
on btnOpenCSV pressed do
(
local isCheckedOut = (gRsPerforce.checkedOutForEdit #(listFilename))[1]
if (not isCheckedOut) and (querybox "Check out file before editing?" title:"Perforce checkout") do
(
gRsPerforce.edit listFilename silent:true
)
-- Create a new Excel instance, and use it to open the CSV:
local excel = CreateOLEObject "Excel.Application"
excel.application.Workbooks.Open listFilename
excel.ActiveSheet.Name = "Shader Alternatives"
local worksheet = excel.ActiveSheet
-- Set up column-filters:
workSheet.EnableAutoFilter = True
workSheet.Cells.AutoFilter 1
-- Freeze header-rows/columns, and set to bold:
(workSheet.Range "B2" "B2").Select()
excel.ActiveWindow.FreezePanes = True
(workSheet.Range "A1" "A1").EntireRow.Font.Bold = True
(workSheet.Range "A1" "A1").EntireColumn.Font.Bold = True
-- Adjust all columns:
workSheet.Columns.AutoFit();
-- Show Excel, then give it focus:
excel.visible = True
SC_MINIMIZE = 0xF020
SC_RESTORE = 0xF120
WM_SYSCOMMAND = 0x112
windows.SendMessage excel.Hwnd WM_SYSCOMMAND SC_MINIMIZE 0
windows.SendMessage excel.Hwnd WM_SYSCOMMAND SC_RESTORE 0
releaseOLEObject excel
)
on RsShaderOptimiserRoll open do
(
RsShaderOptimiserRoll.height = btnOpenCSV.pos.Y + 35
banner.setup()
-- Make sure we have latest list-file (if it's not writable)
if (not (doesFileExist listFilename)) or (getfileattribute listFilename #readOnly) do
(
pushPrompt ("Syncing list-file: "+ (RsMakeBackSlashes listFilename))
gRsPerforce.sync listFilename silent:true
popPrompt()
)
)
on RsShaderOptimiserRoll close do
(
--print "CLOSING OPTIMISER"
)
)
createDialog RsShaderOptimiserRoll