--- ROCKSTAR SUBSTANCE FUNCTIONS -- Useage : Converts substance information that is used on export to create RAGE shaders for game -- Allan Hayburn -- Nov 2012 ----------------------------------------------------------------- -- CALLS TO CONVERT A SUBSTANCE SCENE ----- ----------------------------------------------------------------- --rsMaterialHolder = rsSubstanceMaterialHolder() --- This generates the rsMaterialHolder -- struct that, on create, holds all the substance materials in scene --rsMaterialHolder.convertSceneToRage() -- Call to convert scene --rsMaterialHolder.returnSceneToSubstance() -- Call to return scene filein (rsConfigGetWildWestDir() + "script/3dsmax/_config_files/wildwest_header.ms") filein (RsConfigGetToolsDir()+"dcc/current/max2012/scripts/pipeline/util/p4.ms") rsSubstanceMappingFile = (RsConfigGetProjBinConfigDir()+"generic/SubstanceMappingTable.xml") struct rsSubstanceFunctions ( substanceMappingFile = rsSubstanceMappingFile, -------------------------------------------------------------------------------------------------------------------------------- -- fn checkRenderedMaps -- does perforce checks for rendered files. will add to new CL. -------------------------------------------------------------------------------------------------------------------------------- fn checkRenderedMaps files = ( checkFiles = gRsPerforce.getFileStats files filesInP4 = #() submitList = #() fileNames = #() checkingP4 = false for i in 1 to files.count do -- adds the files arg names to array to check in p4 ( append fileNames (getfilenamefile files[i]) ) checknames = #() for i in 1 to checkFiles.count do -- creates a struct and gets the name of the file for each file in p4 ( theRecordStruct = gRsPerforce.record2struct checkFiles[i] theFileProps = getpropnames theRecordStruct doesItHaveHeadRev = finditem theFileProps #headRev if doesItHaveHeadRev != 0 then ( append checknames (getfilenamefile theRecordStruct.depotFile) ) ) for i in 1 to filenames.count do ( inP4 = finditem checknames filenames[i] -- is our file in p4? if inP4 == 0 then ( append submitList files[i] -- if no add to submit list ) else ( append filesInP4 files[i] -- if yes then checkout dialog ) ) format "New Files: \n" for f in submitList do format "%\n" f format "In P4 Files:\n" filesInP4 for f in filesInP4 do format "%\n" f if submitList.count > 0 then ( gRsPerforce.addToChangelistByName "Substance Generated Maps" submitList checkingP4 = true ) if filesInP4.count > 0 then ( checkingP4 = gRsPerforce.readOnlyP4Check filesInP4 ) checkingP4 ), -------------------------------------------------------------------------------------------------------------------------------- -- fn makeRageFromSubstance -- converts to substance shader to rage shader -------------------------------------------------------------------------------------------------------------------------------- fn makeRageFromSubstance theMaterial theMaps = ( local theSubstanceSettings = theMaterial.ambientmap newRageMaterial = rage_shader() newRageMaterial.name = theMaterial.name RstSetShaderName newRageMaterial theSubstanceSettings.presetType theRsVariables = RstGetVariableCount newRageMaterial for i in 1 to theMaps.count do ( for ii in 1 to theRsVariables do ( if (tolower (RstGetVariableName newRageMaterial ii)) == theMaps[i].slot then ( RstSetVariable newRageMaterial ii theMaps[i].map ) ) ) newRageMaterial ), -------------------------------------------------------------------------------------------------------------------------------- -- fn renderSubstanceMaps -- renders the substance maps defined by the preset - if test: = true then it will only generate a list of maps, not render them. -------------------------------------------------------------------------------------------------------------------------------- fn renderSubstanceMaps theMaterial presetArray dataPairArray iterateVal test:false = ( format "%\n" theMaterial local theSubstanceSettings = theMaterial.ambientmap local rsMaps = #() local rsPreset = getfilenamefile theSubstanceSettings.presetType local notfound = true, res for i = 1 to presetArray.count while notfound do if presetArray[i].preset == rsPreset then (res = i; notfound = false;) res if notfound == false do ( local theMappings = presetArray[res].mappings for i in 1 to theMappings.count do ( local theMap = (tolower theMappings[i].mapping1) local theMapType = case theMap of ( "diffusemap":"texture" "specularmap":"texture" "bumpmap":"texture" "specular_levels_out":"float" "normal_strength":"float" ) if theMapType == "texture" then ( local rendertype = case theMap of ( "diffusemap":theMaterial.diffusemap "specularmap":theMaterial.SpecularMap "bumpmap":theMaterial.bumpMap ) local theSuffix = case theMap of ( "diffusemap":"_d" "specularmap":"_s" "bumpmap":"_n" ) local theBaseTex = ((substring (getfilenamefile theSubstanceSettings.baseTexture.filename) 1 8)+"_Subst"+iterateVal as string ) local theRsMapFile = (theSubstanceSettings.textureOutputDirectory+theBaseTex+theSuffix+".tif") if test == true then ( --theDataPair = datapair BaseTexture:theBaseTex MatArray:(datapair mat:theMaterial texture:theRsMapFile) --format "--------------------------\n\n%\n\n-------------------------\n\n" theDataPair ) else ( format "Rendering Substance Bitmap:%\n" theRsMapFile rm = renderMap rendertype size:[1024,1024] fileName:theRsMapFile rm.gamma = IDisplayGamma.gamma save rm ) append rsMaps (datapair Slot:(tolower theMappings[i].mapping2) Map:theRsMapFile) ) else if theMapType == "float" then ( local rendervalue = case theMap of ( "specular_levels_out":theMaterial.ambientmap.substance.Specular_Levels_Out.x "normal_strength":theMaterial.ambientmap.substance.Normal_Strength ) local valArray = (datapair Slot:(tolower theMappings[i].mapping2.mapping2) Map:rendervalue) append rsMaps valArray ) ) ) rsMaps ), -------------------------------------------------------------------------------------------------------------------------------- -- fn makeRagePresetArray -- Generates the list of rage presets that have been defined in the xml file -------------------------------------------------------------------------------------------------------------------------------- fn makeRagePresetArray = ( local presetArray = #() if presetArray == undefined then format "No Preset Array Defined" else ( if doesfileexist substanceMappingFile == true then ( xmlDoc = dotnetobject "System.XML.XMLDocument" xmlDoc.load substanceMappingFile theEle = xmlDoc.DocumentElement for i = 0 to theEle.childNodes.count-1 do ( theObject = theEle.ChildNodes.itemOf[i] if theObject.attributes.getnameditem "type" != undefined then ( if ((theObject.GetAttributeNode "type").value) == "preset" then ( mappingArray = #() for chld = 0 to theObject.childNodes.count-1 do ( theMapping = theObject.ChildNodes.itemOf[chld] if theMapping.name != "#comment" then ( if ((theMapping.GetAttributeNode "type").value) == "texture" then ( thePair = (datapair mapping1:(theMapping.GetAttributeNode "mapping1").value mapping2:(theMapping.GetAttributeNode "mapping2").value) append mappingArray thePair ) if ((theMapping.GetAttributeNode "type").value) == "float" then ( local theValues = datapair mapping2:(theMapping.GetAttributeNode "mapping2").value values:#((theMapping.GetAttributeNode "value1").value,(theMapping.GetAttributeNode "value2").value) thePair = datapair mapping1:(theMapping.GetAttributeNode "mapping1").value mapping2:theValues -- print thePair append mappingArray thePair ) ) ) append presetArray (datapair preset:(theObject.GetAttributeNode "name").value mappings:mappingArray) ) ) ) ) ) presetArray ), -------------------------------------------------------------------------------------------------------------------------------- -- fn makePresetList -- Generates the list of rage presets for ui -------------------------------------------------------------------------------------------------------------------------------- fn makePresetList = ( presetArray = makeRagePresetArray() presetlist = #() for i in 1 to presetArray.count do ( append presetlist (presetArray[i].preset + ".sps") ) sort presetlist presetlist ), -------------------------------------------------------------------------------------------------------------------------------- -- fn replaceShaderScenObjs -- looks for scene objects with the mat and converts their material -------------------------------------------------------------------------------------------------------------------------------- fn replaceShaderScenObjs theMat theNewMat= ( local theObjs = $objects as array local theMatObjs = #() for i in 1 to theObjs.count do ( if theObjs[i].material == theMat then theObjs[i].material = theNewMat ) theMatObjs ), -------------------------------------------------------------------------------------------------------------------------------- -- fn findTextures -- finds the given texture in a material -------------------------------------------------------------------------------------------------------------------------------- fn findTextures ThisTexture = ( local notfound = true, res = false if ThisTexture != undefined do ( if ThisTexture == medit.GetCurMtl() do (res = true; notfound = false;) texcount = (getNumSubTexmaps ThisTexture) for i in 1 to texcount do ( Texture = getSubTexMap ThisTexture i findTextures (Texture) ) ) res ), -------------------------------------------------------------------------------------------------------------------------------- -- fn findParentMaterials -- finds the parent material of a texture -------------------------------------------------------------------------------------------------------------------------------- fn findParentMaterials = ( if classof (medit.GetTopMtlSlot (medit.GetActiveMtlSlot() )) != multimaterial then ( theMaterial = ( medit.GetTopMtlSlot (medit.GetActiveMtlSlot() )) theParentMaterial = theMaterial theNum = 1 ) else ( theMulti = ( medit.GetTopMtlSlot (medit.GetActiveMtlSlot() )) submatcount = getNumSubMtls theMulti for m in 1 to submatcount do ( if theMulti[m] != undefined do ( --check the subtextures subtexcount = getNumSubTexmaps theMulti[m] for i in 1 to subtexcount do ( checkThis = findTextures (getSubTexMap theMulti[m] i) if checkThis == true then ( theNum = m theMaterial = theMulti[m] ) ) ) ) theParentMaterial = theMulti ) datapair pMaterial:theParentMaterial mMaterial:(datapair mat:theMaterial num:theNum) ) ) global grsSubstance = rsSubstanceFunctions() struct rsSubstanceMaterialHolder ( thePresetArray = undefined, sceneHasSubstanceMaterials = false, substanceObjects = #(), substanceMaterials = #(), tempMaterials = #(), textureDataPairs = #(), func1 = undefined, -- These functions are left undefined so that on create we can leverage functions from the rsSubstanceFunctions struct. func2 = undefined, func3 = undefined, func4 = undefined, iterateVal = 0, conversion = false, -------------------------------------------------------------------------------------------------------------------------------- -- fn checkForSubstanceTexture -- finds any substance texture that is used as an ambient map in the given material -------------------------------------------------------------------------------------------------------------------------------- fn checkForSubstanceTexture mat = ( retval = #() if classof mat != multimaterial then ( if mat != undefined and hasProperty mat #ambientmap and classof mat.ambientmap == rsSubstance do ( append retval 1 ) ) else ( for m in 1 to mat.count where mat[m] != undefined AND hasProperty mat[m] #ambientmap AND classof mat[m].ambientmap == rsSubstance do ( append retval m ) ) retval ), -------------------------------------------------------------------------------------------------------------------------------- -- fn findRsSubstanceInScene -- finds any object in the scene that is using a substance texture and adds that to material and object to the lists. -------------------------------------------------------------------------------------------------------------------------------- fn findRsSubstanceInScene = ( substanceObjects = #() substanceMaterials = #() tempMaterials = #() textureDataPairs = #() local theObjs = $objects as array for i in 1 to theObjs.count do ( local testMat = checkForSubstanceTexture theObjs[i].material if testMat.count > 0 then ( sceneHasSubstanceMaterials = true isUnique = appendifUnique substanceMaterials theObjs[i].material if isUnique == true then ( append tempMaterials (copy theObjs[i].material ) append substanceObjects (datapair Material:(theObjs[i].material as string) objects:#(theObjs[i])) ) else ( for o in 1 to substanceObjects.count do ( hasMat =substanceObjects[o].material == (theObjs[i].material as string) if hasMat == true then append substanceObjects[o].objects theObjs[i] ) ) ) ) sceneHasSubstanceMaterials ), -------------------------------------------------------------------------------------------------------------------------------- -- fn convertSceneToRage -- goes through the created lists of materials and objects and converts them to rage shaders. -------------------------------------------------------------------------------------------------------------------------------- fn convertSceneToRage = ( local submissionTextures = #() local theRageMat = undefined if sceneHasSubstanceMaterials == true then ( Format "Converting Substance Materials to Rage Shaders:\n" for m in SubstanceMaterials do ( if classof m != multimaterial then ( iterateVal = iterateVal+1 theMaps = (func2 m thePresetArray textureDataPairs iterateVal test:true ) for i in 1 to theMaps.count do ( if (classof theMaps[i].map) == string then append submissionTextures theMaps[i].map ) ) else ( theSubMats = (checkForSubstanceTexture m) for sm in 1 to theSubMats.count do ( iterateVal = iterateVal+1 theMaps = (func2 m[theSubMats[sm]] thePresetArray textureDataPairs iterateVal test:true ) for i in 1 to theMaps.count do ( if (classof theMaps[i].map) == string then append submissionTextures theMaps[i].map ) ) ) ) func3 submissionTextures iterateVal = 0 for m in 1 to SubstanceMaterials.count do ( mm = SubstanceMaterials[m] if classof mm != multimaterial then ( iterateVal = iterateVal+1 theMaps = (func2 mm thePresetArray textureDataPairs iterateVal test:false ) theRageMat = func4 mm theMaps for i in 1 to substanceObjects[m].objects.count do ( substanceObjects[m].objects[i].material = theRageMat ) ) else ( theSubMats = (checkForSubstanceTexture mm) for sm in 1 to theSubMats.count do ( iterateVal = iterateVal+1 theMaps = (func2 mm[theSubMats[sm]] thePresetArray textureDataPairs iterateVal test:false ) theRageMat = func4 mm[theSubMats[sm]] theMaps mm[theSubMats[sm]] = theRageMat for i in 1 to substanceObjects[m].objects.count do ( substanceObjects[m].objects[i].material = mm ) ) ) ) ) ), -------------------------------------------------------------------------------------------------------------------------------- -- fn returnSceneToSubstance -- reverts any object in list back to a substance shader -------------------------------------------------------------------------------------------------------------------------------- fn returnSceneToSubstance = ( if sceneHasSubstanceMaterials == true then ( for i in 1 to substanceObjects.count do ( for o in substanceObjects[i].objects do ( o.material = tempMaterials[i] ) ) ) ), on create do ( thePresetArray = (grsSubstance.makeRagePresetArray()) func2 = grsSubstance.renderSubstanceMaps func3 = grsSubstance.checkRenderedMaps func4 = grsSubstance.makeRageFromSubstance max select none ) ) global rsMaterialHolder = rsSubstanceMaterialHolder() global ConvertRsSubstance (try ConvertRsSubstance R1 catch()) Rollout ConvertRsSubstance "RS Substance Conversion" ( local substancesConverted = false Button B2 "Convert All Substance To Rage" width:230 Button B3 "Return All Rage to Substance" width:230 on B2 Pressed do ( rsMaterialHolder.convertSceneToRage() substancesConverted = true ) on B3 Pressed do ( rsMaterialHolder.returnSceneToSubstance() substancesConverted = false ) on R1 open do ( rsMaterialHolder = rsSubstanceMaterialHolder() rsMaterialHolder.findRsSubstanceInScene() ) on R1 oktoclose do ( if substancesConverted == true then querybox "You have unreverted Substances. Close?" ) )