Files
gtav-src/tools_ng/wildwest/script/3dsMax/Maps/Materials/multiSubMaterial_Updater.ms
T
2025-09-29 00:52:08 +02:00

427 lines
18 KiB
Plaintext
Executable File

---------------------------------------------------------
-- Script for updating multiSub materials using a preset
-- Stewart Wright - Rockstar North - August 2012
---------------------------------------------------------
filein (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms")
(
---------------------------------------------------------
-- LOCALS --------------------------------------------
local warnValue = 5
local updateCount = #(0,0,0,0) --(materials, rage materials, updated materials, updated to detail materials)
---------------------------------------------------------
-- GLOBALS -------------------------------------------
global rs_MulitSubUpdater
---------------------------------------------------------
-- FUNCTIONS --------------------------------------
---------------------------------------------------------
fn get_presetList =
(
returnArray = #()
presetFiles = getFiles (mapShaderPresetPath + "*.txt")
for i in presetFiles do
(
tempArray = filterString i @"\ /"
justName = tempArray[tempArray.count]
justName = substring justName 1 (justName.count-4)
append returnArray justName
)
return returnArray
)--end get_presetList
---------------------------------------------------------
--function for writing out the shader values to a text file
fn saveMaterialSettings toWrite saveAs =
-- toWrite = the array data we're going to write out
-- saveAs = what we're saving the file as
(
if (saveAs!= "") do
(
safeToSave = true
presetFile = (mapShaderPresetPath + saveAs + ".txt")
presetFile = rsMakeSafeSlashes presetFile
if (gRsPerforce.sync #(presetFile) silent:true) == true then --try and sync the file
(
if not(queryBox "Preset already exists, Do you want to continue?" title:"Warning!") then (safeToSave = false) --if it already exists
else
(
gRsPerforce.edit #(presetFile) silent:true
safeToSave = true
)
)
else --if it is new
(
gRsPerforce.add #(presetFile) silent:true
safeToSave = true
)
if (safeToSave) do
(
saveFile = createFile presetFile --now we make the text file using the above settings
print toWrite to: saveFile --write the contents of our array to the text file
close saveFile --housekeeping. this closes the txt file
rs_MulitSubUpdater.ddlPresets.items = (get_presetList())
)
)
)--end saveMaterialSettings
---------------------------------------------------------
--function for reading in the shader values from a text file
fn loadMaterialPreset =
(
readData = #() --housekeeping. empty the array before we start
presetName = rs_MulitSubUpdater.ddlPresets.selected
sourceFile = mapShaderPresetPath + presetName + ".txt"
shaderFile = openFile sourceFile
if shaderFile != undefined then
(
while not eof shaderFile do --as long as we haven't reached the end of the file keep going
(
checkLine = readValue shaderFile ignoreStringEscapes:true --readValue gets the info from the txt file
append readData checkLine --adds each line of the text file to our array
)
close shaderFile --housekeeping. this closes the txt file
readData --pass out the array
)
else
(
mText = "I couldn't find " + sourcefile + ".txt"
messagebox mText title:"Error"
)
)--end loadMaterialPreset
---------------------------------------------------------
--function to read the material and save it as a preset
fn readMaterial getMat =
--getMat = the material we're updating
(
multiSubArray = #()
if classOf getMat == Multimaterial then
(
for subID=1 to getMat.numSubs do
(
if classOf getMat[subID] == Rage_Shader do
(
shadertype = RstGetShaderName getMat[subID]
shaderArray = #() --lets make a 'disposable' array for storing the settings
for shaderVar=1 to RstGetVariableCount getMat[subID] do
(
varValue = (RstGetVariable getMat[subID] shaderVar) --get the variable value
append shaderArray varValue
)
append multiSubArray #(shaderType, shaderArray)
)
)
saveMaterialSettings multiSubArray rs_MulitSubUpdater.edtName.text
)
else
(
messageBox "This isn't a multi/sub material."
)
)--end readMaterial
---------------------------------------------------------
--a function to find all items in an array. similar to findItem but returns multiple values, not just the first hit
fn findAllItems arrayToSearch val =
(
local foundIndexes = #()
local arrayCopy = deepCopy arrayToSearch
while ((foundIndex = (findItem arrayCopy val)) != 0) do
(
append foundIndexes foundIndex
arrayCopy[foundIndex] = undefined
)
foundIndexes --pass out the results
)--end findAllItems
---------------------------------------------------------
--a function to apply the preset values to the active material
fn applyMaterial getMat presetArray=
--getMat = the material we're updating
--presetArray = the pesets we're loading
(
if classOf getMat == Multimaterial then
(
justTheShaderType = #()
justTheShaderValues = #()
for readShaderType=1 to presetArray.count do
(
append justTheShaderType presetArray[readShaderType][1]--make an array containing just the shader type
append justTheShaderValues presetArray[readShaderType][2]--make an array containing just the shader values
)
for subID=1 to getMat.numSubs do--for every sub material
(
updateCount[1] = (updateCount[1] + 1)--update our counter for the materials
shaderUpdateStatus = false
if classOf getMat[subID] == Rage_Shader do--if the shader is a rage material
(
updateCount[2] = (updateCount[2] + 1)--update our counter for the rage materials
shaderType = RstGetShaderName getMat[subID] --the selected shader type
filteredShaderType = filterString shaderType "."--attempt to create a detail version of this shader
detailShaderType = filteredShaderType[1] + "_detail." + filteredShaderType[2]
matchedShaders = findAllItems justTheShaderType shaderType --search our array for shader mathes
matchedDetailShaders = findAllItems justTheShaderType detailShaderType --search our array for shader mathes
join matchedShaders matchedDetailShaders
if matchedShaders.count > 0 do --if we've found shader matches
(
matchedShaderValues = #()
matchedShadersType = #()
for matched=1 to matchedShaders.count do
(
append matchedShadersType justTheShaderType[matchedShaders[matched]]--make a new array with just the matching shader data
append matchedShaderValues justTheShaderValues[matchedShaders[matched]]--make a new array with just the matching shader data
)
arrayToMatch = #() --lets make a 'disposable' array for storing the settings
for shaderVar=1 to RstGetVariableCount getMat[subID] do --for each variable in the shader
(
varValue = (RstGetVariable getMat[subID] shaderVar) --get the variable value
varName = (RstGetVariableName getMat[subID] shaderVar) --get the variable value
if (RstGetVariableType getMat[subID] shaderVar == "texmap") and (varName != "Detail Map") do --if it is a texture we'll note it, this is what we are checking for matches against
(
append arrayToMatch #(varValue, shaderVar)--we are storing the value and the position within the shader
)
)
for checkMe=1 to matchedShaderValues.count do --for every shader match
(
if shaderUpdateStatus != true do --if we haven't found a match yet
(
matchingPair = #()
for checkAgainst=1 to arrayToMatch.count do --for every texture in the current shader
(
source = upperCase (arrayToMatch[checkAgainst][1] as string) --the existing texture
target = upperCase (matchedShaderValues[checkMe][arrayToMatch[checkAgainst][2]] as string) --the new texture to check
append matchingPair (source == target) --store if we find a match. match == true
)
if (findItem matchingPair false) == 0 and (matchingPair.count != 0) then --if we don't find any false matches then we can update this shader
(
for shaderVar=1 to RstGetVariableCount getMat[subID] do --for every variable in the shader
(
RstSetVariable getMat[subID] shaderVar matchedShaderValues[checkMe][shaderVar] --update it with our preset values
if shaderVar==RstGetVariableCount getMat[subID] do
(
updateCount[3] = (updateCount[3] + 1)--update our counter for the updated rage materials
shaderUpdateStatus = true --set the status of the update so we can stop and move on to the next
)
)
)
else-- we'll do some checks to see if this shader matches a detail version
(
if shaderUpdateStatus != true do --if we haven't found a match yet
(
matchedDetailShaderValues = #()
for matched=1 to matchedShaders.count do
(
append matchedDetailShaderValues justTheShaderValues[matchedShaders[matched]]--make a new array with just the matching shader data
)
tempMatCopy = mlib[24]--copy slot24, we'll replace it once we're done
mlib[24] = copy getMat[subID]
RstSetShaderName mlib[24] detailShaderType
if RstGetVariableCount mlib[24] > 0 do --if it is a valid shader it will have a positive number
(
detailArrayToMatch = #() --lets make a 'disposable' array for storing the settings
for shaderVar=1 to RstGetVariableCount mlib[24] do --for each variable in the shader
(
varValue = (RstGetVariable mlib[24] shaderVar) --get the variable value
varName = (RstGetVariableName mlib[24] shaderVar) --get the variable value
if RstGetVariableType mlib[24] shaderVar == "texmap" do --if it is a texture we'll note it, this is what we are checking for matches against
(
if upperCase varName != "DETAIL MAP" do --we don't care about the detail map. if we are going from a non detail shader to a detail shader the original shader won't have this slot
(
append detailArrayToMatch #(varValue, shaderVar)--we are storing the value and the position within the shader
)
)
)
mlib[24] = tempMatCopy--return slot24 to what i used to be
if detailArrayToMatch.count != 0 do --if we find some matches
(
for checkMe=1 to matchedDetailShaderValues.count do --for every shader match
(
if shaderUpdateStatus != true do --if we haven't found a match yet
(
matchingPair = #()
for checkAgainst=1 to arrayToMatch.count do --for every texture in the current shader
(
source = upperCase (detailArrayToMatch[checkAgainst][1] as string) --the existing texture
target = upperCase (matchedDetailShaderValues[checkMe][detailArrayToMatch[checkAgainst][2]] as string) --the new texture to check
append matchingPair (source == target) --store if we find a match. match == true
)
if (findItem matchingPair false) == 0 and (matchingPair.count != 0) then --if we don't find any false matches then we can update this shader
(
RstSetShaderName getMat[subID] matchedShadersType[checkMe] --change the shader type
for shaderVar=1 to RstGetVariableCount getMat[subID] do --for every variable in the shader
(
RstSetVariable getMat[subID] shaderVar matchedDetailShaderValues[checkMe][shaderVar] --update it with our preset values
if shaderVar==RstGetVariableCount getMat[subID] do
(
updateCount[4] = (updateCount[4] + 1)--update our counter for the updated rage materials
shaderUpdateStatus = true --set the status of the update so we can stop and move on to the next
)
)
)
)
)
)
)
mlib[24] = tempMatCopy--return slot24 to what i used to be#
)
)--end of else loop. this loop is for detail shader upgrades
)
)
)--end shader match
)
)-- end for every sub material loop
)
)--end applyMaterial
---------------------------------------------------------
-- UI --------------------------------------------------
---------------------------------------------------------
(
try (destroyDialog rs_MulitSubUpdater) catch()
-- LOCALS -------------------------------------------
local theState = 1
---------------------------------------------------------
rollout rs_MulitSubUpdater "MultiSub Material Presets"
(
dotNetControl rsBannerPanel "Panel" pos:[0,0] height:32 width:rs_MulitSubUpdater.width
local banner = makeRsBanner dn_Panel:rsBannerPanel wiki:"" filename:(getThisScriptFilename())
group "Create New MultiSub Preset"
(
label presetName "Preset Name:" align:#left
editText edtName width:140 offset:[0,0]
button btnSave "Save MultiSub As Preset" width:130 tooltip:"Create preset from selecte material"
)
group "MultiSub Shader Preset"
(
button btnFetch "Sync Presets" width:130 toolTip:"Fetch the latest data from perforce"
dropdownlist ddlPresets width:140 items:(get_presetList())
label materialSource "Update Material On Selected:" align:#left
radiobuttons radObjectOrMaterial labels:#("Object", "Material") toolTip:"Select how you want to define the material to update"
button btnLoad "Apply MultiSub Preset" width:130 toolTip:"Load preset on to selected material."
)
---------------------------------------------------------
-- EVENTS ------------------------------------------
---------------------------------------------------------
on ddlPresets selected sel do (loadMaterialPreset())
-----------------------------------------------------------
on radObjectOrMaterial changed newState do (theState = newState)
-----------------------------------------------------------
on btnSave pressed do
(
materialWindowState = MatEditor.isOpen()--store the material editor window state
MatEditor.Close()--close the material editor if it is open
readMaterial (medit.getcurmtl())
if materialWindowState == true do MatEditor.Open()--open the material editor again if it was when we started
)
on btnLoad pressed do
(
if rs_MulitSubUpdater.ddlPresets.selected != undefined then
(
loadedData = loadMaterialPreset()
updateCount = #(0,0,0,0)--empty this, we'll fill it with report stats
case of
(
(theState == 1):--if the 'object' button is checked
(
if selection.count > 0 then--if we have at least 1 thing selected
(
continueState = true
whatMaterials = #()--an array to store the materials in
for selectedObject=1 to selection.count do
(
if superClassOf selection[selectedObject] == GeometryClass then
(
if classOf selection[selectedObject].material == Multimaterial then
(
whatMaterial = selection[selectedObject].material
appendIfUnique whatMaterials whatMaterial --add the material if it is unique
)
else (print "The material is not a multiSub.")
)
else (print "The selected object is not geometry.")
)
if selection.count > warnValue do--a warning about large selection counts
(
continueState = (queryBox "There are more than 5 objects selected.\r\nThis might take a while.\r\nDo you want to continue?" title:"Warning!")
)
if (whatMaterials.count > 0) and (continueState == true) do --if we have some materials to update
(
materialWindowState = MatEditor.isOpen()--store the material editor window state
MatEditor.Close()--close the material editor if it is open
for singleMaterial=1 to whatMaterials.count do --for every material
(
applyMaterial whatMaterials[singleMaterial] loadedData --update the material
)
if materialWindowState == true do MatEditor.Open()--open the material editor again if it was when we started
updateStatsText = updateCount[1] as string + " total materials checked. Of which " + updateCount[2] as string + " were Rage Shaders.\r\n" + updateCount[3] as string + " Rage Shaders updated.\r\n" + updateCount[4] as string + " Rage Shaders converted to details shaders.\r\n"
messageBox updateStatsText title:"Update Stats Report:"
)
)
else (messageBox "Please select a mesh.")
)
(theState == 2):--if the 'material' button is checked
(
if classOf (medit.getcurmtl()) == Multimaterial then--if the material is a rage shader we'll use it
(
materialWindowState = MatEditor.isOpen()--store the material editor window state
MatEditor.Close()--close the material editor if it is open
whatMaterial = medit.getcurmtl()
applyMaterial whatMaterial loadedData
if materialWindowState == true do MatEditor.Open()--open the material editor again if it was when we started
updateStatsText = updateCount[1] as string + " total materials checked. Of which " + updateCount[2] as string + " were Rage Shaders.\r\n" + updateCount[3] as string + " Rage Shaders updated.\r\n" + updateCount[4] as string + " Rage Shaders converted to details shaders.\r\n"
messageBox updateStatsText title:"Update Stats Report:"
)
else (messageBox "Please open the material editor and select a multiSub shader.")
)
)
)
else (messageBox "Please select a preset.")
)
-----------------------------------------------------------
on btnFetch pressed do
(
WWP4vSync mapShaderPresetFiles
rs_MulitSubUpdater.ddlPresets.items = (get_presetList())
)
---------------------------------------------------------
---------------------------------------------------------
on rs_MulitSubUpdater open do
(
rs_dialogPosition "get" rs_MulitSubUpdater
rs_MulitSubUpdater.banner.setup()
)
---------------------------------------------------------
on rs_MulitSubUpdater close do
(
rs_dialogPosition "set" rs_MulitSubUpdater
)
)
createDialog rs_MulitSubUpdater width:165 style:#(#style_border,#style_toolwindow,#style_sysmenu)
)--end UI
)