User Guide Cancel

Substances modification | Substance 3D Automation ToolKit

  1. Substance 3D home
  2. Home
  3. Command Line Tools
    1. Command Line overview
    2. sbsbaker
      1. sbsbaker overview
      2. sbsbaker command line options
      3. sbsbaker Example Command Lines
    3. sbscooker
      1. sbscooker overview
      2. sbscooker command line options
      3. sbscooker pattern variables
    4. sbsmtools
      1. sbsmtools overview
      2. sbsmtools command line options
    5. sbsmutator
      1. sbsmutator overview
      2. sbsmutator command line options
      3. sbsmutator Example Command Lines
    6. sbsrender
      1. sbsrender overview
      2. sbsrender base parameters and pattern variables
      3. sbsrender command line options
      4. sbsrender example command lines
    7. sbsupdater
      1. sbsupdater overview
      2. sbsupdater command line options
  4. Pysbs - Python API
    1. Pysbs - Python API overview
    2. Getting started
    3. General topics
      1. Basic manipulation
      2. Substance creation
      3. Substances modification
      4. Dependencies management
      5. PySbs batchtools module
      6. metadata manipulation
      7. SAT demos
      8. Edit sbsar with SBSARManager
      9. Spot Colors
      10. Thumbnail creation with SAT
    4. Examples
      1. demohelloworld
      2. demos
      3. demos_batchtools
      4. script_update_with_sbsupdater
    5. API Content
      1. API Content overview
      2. Substance definitions
        1. Common interfaces
          1. basegraph
          2. package
          3. sbsarobject
          4. sbsobject
        2. compnode
          1. compnode overview
          2. common
          3. compimplementation
          4. paramgraph
        3. context projectmgr
        4. graph
          1. graph overview
          2. function
          3. inputparameters
          4. output
        5. mdl
          1. mdlannotation
          2. mdlcommon
          3. mdldictionaries
          4. mdlenum
          5. mdlgraph
          6. mdllibclasses
          7. mdlmanager
          8. mdlnode
          9. mdlnodeimpl
          10. mdloperand
          11. mdlsbsbridge
        6. modelgraphindex
          1. modelannotationnames
          2. modelgraph
          3. modelgraphgenerator
          4. modelgraphimplementation
          5. modelnodenames
          6. modeloperand
          7. modulegraphindex
          8. moduleannotation
          9. moduleconnection
          10. modulegraph
          11. modulegraphgenerator
          12. modulegraphimplementation
          13. modulegraphlibrary
          14. modulegraphregister
          15. modulenode
          16. modulenodeimplementation
          17. modulenodeinstance
          18. moduleoperand
          19. moduleoutputbridging
          20. moduleparaminput
        7. params
          1. params overview
          2. dynamicvalue
          3. paramnode
        8. projectmgrdoc
        9. sbsarchive
          1. sbsarchive overview
          2. sbsarenum
          3. sbsargraph
          4. sbsargui
          5. sbsarguiwidgets
          6. sbsarmanager
        10. sbscommon
          1. connections
          2. gui
          3. nodes
          4. values
        11. sbspreset
        12. sbsproject
        13. substance
          1. substance overview
          2. content
          3. resource
      3. Libraries
        1. sbsenum
        2. sbslibrary
          1. sbslibrary overview
          2. sbsdictionaries
          3. sbsfilters
          4. sbsfunctions
          5. sbsfxmapnodes
          6. sbslibclasses
          7. sbswidgets
        3. sbsbakerslibrary
          1. sbsbakerslibrary overview
          2. sbsbakersdef
          3. sbsbakersdefaultprops
          4. sbsbakersdictionaries
          5. sbsbakersenum
          6. sbsbakingconverter
          7. sbsbakingconverterparam
          8. sbsbakingparameters
          9. sbsdialogstate
          10. sbsscenedata
        4. Helpers
          1. sbscleaner
          2. sbsexporter
          3. sbsgenerator
          4. sbsparser
          5. sbswriter
          6. qtclasses
            1. qtclasses overview
            2. qtvariantreader
            3. qtvariantwriter
          7. psdparser
          8. sbsimpactmanager
          9. batchtools
          10. autograph
            1. ag_functions
            2. ag_layout
            3. ag_types
          11. info_mesh_parser
          12. sbsbaker_info_handlers
          13. sbsrender_render_handlers
          14. output_handlers
          15. spotcolorinfo_handler
          16. thumbnail
          17. batchtools overview
        5. Execution context
          1. context
          2. functions
        6. API Change log
  5. Samples
    1. Samples overview
    2. Texturing Template Demo
    3. Batch Tools Demo
    4. Variations
    5. Texture Mat
    6. Pixel Processor Ray tracer
  6. Setup and Getting Started
    1. Setup and Getting Started overview
    2. Compatibility
    3. Frequently asked Questions
    4. Known issues
    5. SAT Cookbook
    6. Use Pysbs in different python interpreter (maya, sd, blender...)
  7. Integrations
    1. Substance Maya toolset
      1. Substance Maya Toolset overview
      2. Installing
      3. Launching
      4. Baking
        1. Baking overview
        2. Export parameters
        3. Baker parameters
        4. Mesh setup
        5. Using a template
      5. Changelog
  8. Release notes
    1. Version 11.3.0
    2. Version 11.2.0
    3. Version 11.1.0

Substances modification

This API may also be used to modify existing substances, to automatize an update of a set of substances, or to apply the same modification on many nodes or graph.

Massive modification of Substances

Suppose you want to do the same modification on all nodes of a Substance, or on a set of Substances, this API will allows you to script the modifications you want to apply instead of doing them manually.

The sample function demos.demoMassiveModification() demonstrates the modification of the Output size of all Graphs included in the substance sample/blend_switch.sbs, and the Pixel format of all Input nodes included in each Graph.

Launch the script with this command line, from the folder demos/:

python demos.py -fct: demoMassiveModification -args: "../sample/argsDemoMassiveModification.xml"

On a package (SBSDocument), the following modifications can be done:

# Relocate a linked resource 
myResource = sbsDoc.getSBSResource('myResourceIdentifier')                            # Get the resource from its identifier 
myResource = sbsDoc.getSBSResourceFromPath('<myAbsolutePath>/myResourceFilename.png') # Get the resource from its absolute path 
myResource = sbsDoc.getObjectFromInternalPath('pkg:///myGroup/myResourceIdentifier')  # Get the resource from its internal path 
 
sbsDoc.relocateResource(myResource, aNewPath='<myNewAbsolutePath>/myNewFilename.png') # Relocate the resource 
 
# Move a graph/function/resource/group under a group 
# -> it updates all the references on this object inside the current package) 
sbsDoc.moveObjectUnderGroup(aObject=myGraph, aGroup='pkg:///Graphs')   # Move the graph under the group named 'Graphs' 
sbsDoc.moveObjectUnderGroup(aObject=myGraph, aGroup=None)              # Move the graph under the root package content 
 
# Set the identifier of a graph/function/resource/group 
# -> It updates all the references on this object inside the current package 
sbsDoc.setObjectIdentifier(aObject=myGraph, aIdentifier='myNewGraphName')

On a graph (SBSGraph), the following modifications can be done for instance:

# Create a new input parameter, named 'InputColor', with a RGBA color widget 
aParam = aSubGraph.addInputParameter(aIdentifier   = 'InputColor', 
                                     aWidget       = sbsenum.WidgetEnum.COLOR_FLOAT4, 
                                     aDefaultValue = [1,1,1,1], 
                                     aLabel        = 'Input Color') 
 
# Modify a base parameter: Set the output size for instance 
aGraph.setBaseParameterValue(aParameter  = sbsenum.CompNodeParamEnum.OUTPUT_SIZE, 
                             aParamValue = [sbsenum.OutputSizeEnum.SIZE_1024,sbsenum.OutputSizeEnum.SIZE_1024], 
                             aRelativeTo = sbsenum.ParamInheritanceEnum.ABSOLUTE) 
 
# Set attributes and icon 
aGraph.setAttribute(aAttributeIdentifier = sbsenum.AttributesEnum.Author, aAttributeValue='Substance Designer API') 
aGraph.setIcon(aIconAbsPath = myIconPath)) 
 
# Create parameter presets 
aPreset = aGraph.createPreset(aLabel='DefaultPreset', setCurrentDefaultValues=True) 
aPreset.setPresetInput(aInputParam=aParam, aPresetValue=[0.1, 0.2, 0.3, 1]) 
 
# Connect/Disconnect two nodes (the input and output can be specified or not, depending on the possible ambiguity) 
aGraph.connectNodes(aLeftNode = aFirstNode, aRightNode = aSecondNode) 
aGraph.connectNodes(aLeftNode = aFirstNode, aRightNode = aSecondNode, aLeftNodeOutput='myOutput', aRightNodeInput=sbsenum.InputEnum.OPACITY) 
aGraph.disconnectNodes(aLeftNode = aFirstNode, aRightNode = aSecondNode) 
 
# Move the connections on a pin input/output to another pin input/output 
aGraph.moveConnectionsOnPinOutput(aInitialNode = rgbaSplitNode, aInitialNodeOutput = 'R', 
                                  aTargetNode = rgbaSplitNode, aTargetNodeOutput = 'G') 
aGraph.moveConnectionOnPinInput(aInitialNode = blendNode, aInitialNodeInput = sbsenum.InputEnum.DESTINATION, 
                                aTargetNode = embossNode, aTargetNodeInput = sbsenum.InputEnum.INPUT_GRADIENT)

On a compositing node (SBSCompNode), several functions allow to modify a node:

# Modify a Parameter: Set the output pixel format for instance 
aCompNode.setParameterValue(aParameter  = sbsenum.CompNodeParamEnum.OUTPUT_FORMAT, 
                            aParamValue = sbsenum.OutputFormatEnum.FORMAT_16BITS, 
                            aRelativeTo = sbsenum.ParamInheritanceEnum.ABSOLUTE) 
 
# Define a parameter as dynamic, handled by the input parameter named 'InputColor' defined in the parent graph: 
aDynFunction = aCompNode.setDynamicParameter(sbsenum.CompNodeParamEnum.OUTPUT_COLOR) 
aDynFunction.setToInputParam(aParentGraph, aInputParamIdentifier='InputColor')

There are equivalent functions for Function graph (SBSFunction) and function node (SBSParamNode).

Baking Parameters modification

This API allows to create or modify the baking parameters of a Scene resource imported in the Substance.

The sample function demos.demoBakingParameters() gives a brief overview of what the API allows to do regarding the baking parameters.

The initial substance DemoBakingParameters.sbs provided in the folder sample/ is almost empty and contains no resource.

The objective of the script is:

  • to import a new linked Scene resource in the package

  • to create the Baking Parameters of this resource

  • to set a ‘Normal Map From Mesh baker’ with a High definition mesh

  • to set a ‘Ambient Occlusion’ baker which uses the result of the ‘Normal Map From Mesh’

# Add a new Scene resource to the document 
aRelPath = sbsDoc.buildAbsPathFromRelToMePath(aRelPathFromPackage='./Models/LowPolyModel.obj') 
aNewResource = sbsDoc.createExternalResource(aIdentifier='MyNewResource', 
                                             aResourcePath=aRelPath, 
                                             aResourceTypeEnum=sbsenum.ResourceTypeEnum.SCENE) 
 
# Create BakingParameters for this resource 
aBakingParams = aNewResource.createBakingParameters() 
 
# Add a high poly mesh from a file path 
aHighPolyFilePath = sbsDoc.buildAbsPathFromRelToMePath('./Models/HighPolyModel.obj') 
aBakingParams.addHighDefinitionMeshFromFile(aHighPolyFilePath) 
 
# Add a Normal Map From Mesh baker 
NM_baker = aBakingParams.addBaker(sbsbakers.BakerEnum.NORMAL_MAP_FROM_MESH) 
 
# Set Antialiasing value specifically to NormalMap baker 
NM_baker.setParameterValue(aParameter=sbsbakers.ConverterParamEnum.DEFAULT__SUB_SAMPLING, 
                           aParamValue=sbsbakers.BakerFromMeshSubSamplingEnum.SUBSAMPLING_2x2) 
 
# Add an Ambient Occlusion baker which uses the resulting map of the Normal Map From Mesh baker 
AO_baker = aBakingParams.addBaker(sbsbakers.BakerEnum.AMBIENT_OCCLUSION) 
AO_baker.setFileParameterValueFromPreviousBaker(aParameter=sbsbakers.ConverterParamEnum.NORMAL_MAP, 
                                                aPreviousBaker=NM_baker) 
 
# Set back the baking parameters into the options of the resource 
aNewResource.setBakingParameters(aBakingParams)

Launch the script with this command line, from the folder demos/:

python demos.py -fct: demoBakingParameters -args: "../sample/argsDemoBakingParameters.xml"

Here is the result of the script execution:

The generated maps:

On a SBSResource on kind SCENE, the baking parameters are saved as a base64 encoded string in the options of the resource. The following functions allow to get the baking parameters as an object BakingParameters with the ability to do modifications on the converters already defined, and to add new converters.

# Get the Scene resource named 'myResourceName' 
aSceneResource = sbsDoc.getSBSResource(aResourceIdentifier='myResourceName') 
 
# Get the baking parameters as an object 
aBakingParams = aSceneResource.getBakingParameters() 
 
# Get the 'Position' baker 
Position_baker = aBakingParams.getConverter(aConverter=sbsbakers.BakerEnum.POSITION) 
 
# Override the 'Common to all bakers parameters' 
Position_baker.setOverrideParamsCommonToAllBakers(aOverride=True) 
 
# Set parameters values 
Position_baker.setParameterValue(aParameter=sbsbakers.ConverterParamEnum.COMMON__APPLY_DIFFUSION, 
                                 aParamValue=False) 
Position_baker.setParameterValue(aParameter=sbsbakers.ConverterParamEnum.MODE, 
                                 aParamValue=sbsbakers.BakerPositionModeEnum.ONE_AXIS) 
Position_baker.setParameterValue(aParameter=sbsbakers.ConverterParamEnum.AXIS, 
                                 aParamValue=sbsbakers.BakerPositionAxisEnum.AXIS_Y) 
 
# Add a Curvature converter 
Curvature_baker = aBakingParams.addConverter(aIdentifier=sbsbakers.BakerEnum.CURVATURE) 
 
# Set back the baking parameters into the options of the resource 
aSceneResource.setBakingParameters(aBakingParams)

Iteration generation

This API provides a set of useful function to generate iterations on compositing or function graphs, allowing to duplicate n times a single node or a pattern of nodes.

Several objects implement the two methods to generate iteration: createIterationOnNode and createIterationOnPattern:

A set of sample of iteration creation are available in the folder sample/.

Sample 1: Compositing node iteration

The sample function demos.demoIteration() shows how to generate different kind of iterations.

The initial substance contains four graphs, and in each graph an iteration will be created, showing the ability of the provided methods to deduce automatically the connection between pattern where there is no ambiguity, or the ability to specify the connection between two successive patterns.

Launch the script with this command line, from the folder demos/:

python demos.py -fct: demoIteration -args: "../sample/argsDemoIteration.xml"

The first example is very simple and shows the creation of an iteration of a single node, with only one input and one output:

# Duplicate 5 times a single node 
aGraph = sbsDoc.getSBSGraph(aGraphIdentifier = 'DemoIterationSubGraph') 
aGraph.createIterationOnNode(aNbIteration = 5, aCompNodeUID = '1255032103')

The second example shows the creation of an iteration of a single node, with two inputs and outputs, grayscale and color, whose types are inverted between the input and the output:

# Duplicate 3 times the node (test the automatic detection of compatible inputs / outputs) 
aGraph = sbsDoc.getSBSGraph(aGraphIdentifier = 'DemoIterationSubGraphDouble') 
aGraph.createIterationOnNode(aNbIteration = 3, aCompNodeUID = '1255523774')

The third example shows the creation of an iteration of a pattern, the detection of connections coming from the outside of the pattern, and the detection of the connection between two successive patterns.

# Duplicate 3 times the pattern of nodes (test the automatic detection of compatible inputs / outputs) 
aGraph = sbsDoc.getSBSGraph(aGraphIdentifier = 'DemoIterationPattern') 
aGraph.createIterationOnPattern(aNbIteration = 3, 
                                aCompNodeUIDs = ['1255034408', '1255026224', '1255029181', '1255029884', 
                                                 '1255029987', '1255029994', '1255029049'])

The fourth example shows the creation of an iteration of a pattern with the way to specify the connection between two successive patterns.

# Duplicate 3 times the pattern of nodes, specifying way to connect two successive patterns 
aGraph = sbsDoc.getSBSGraph(aGraphIdentifier = 'DemoIterationVerticalPattern') 
aGraph.createIterationOnPattern(aNbIteration = 3, 
                                aCompNodeUIDs = ['1262168894', '1262168896'], 
                                aCompNodeUIDs_NextPatternInput = ['1262169024', '1262168960'], 
                                aGUIOffset = [0, 100])

Sample 2: Iteration inside a Pixel Processor

The sample function demos.demoIterationPixProc() demonstrates the way to generate an iteration inside the pixel processor function.

The purpose of the substance used in this sample is to generate a terrain heightmap using the MultiFractal algorithm:

Launch the script with this command line, from the folder demos/:

python demos.py -fct: demoIterationPixProc -args: "../sample/argsDemoIterationPixProc.xml"

The function will modify the pixel processor node framed with ‘Fractal Sum’ in yellow, in the substance demoIteration.sbs provided in sample/:

In the initial state of the pixel processor function, there are only two iterations of what we want to duplicate: The Add node and the instance of function FractamSumFct node.

The first one will be given as the pattern to duplicate, and the second one will be given as the next iteration pattern, in order to specify the connections between two successive patterns:

The function demos.demoIterationPixProc() calls the method createIterationOnPattern() on the pixel processor function, with these lines:

# Get the pixel processor node where the iteration will be created, and its dynamic function 
aPixProcNode = aGraph.getCompNode('1260898088') 
aDynFct = aPixProcNode.getPixProcFunction() 
 
# Duplicate 10 times the pattern, indicating the way to reconnect to the next pattern 
createdNodes = aDynFct.createIterationOnPattern(aNbIteration = 10, 
                                aGUIOffset = [0, 200], 
                                aParamNodeUIDs = ['1263052178', '1263052114' ], 
                                aParamNodeUIDs_NextPattern = ['1263052279', '1263052278'])

Note that the nodes are identified by their UID, which is their unique identifier in the context of their parent.

The UIDs can be retrieved by parsing the .sbs file or directly in Substance Designer, by right-clicking on a node and selecting ‘Copy Info to Clipboard’

Here is the result:

Sample 3: Iteration inside a function, and use of variables

The sample function demos.demoIterationFlame() demonstrates the use of iteration inside a function, with the modification of variables inside this function, using the Get and Set nodes.

In the substance Flame.sbs provided in the folder sample/, the function RayMarch needs 64 iterations of the function called IterationStep to implement the RayMarching algorithm.

The function IteraionStep uses the variables called glow and glowed and eventually modify their value depending on certain conditions.

Same thing for variable ‘i’, which counts the number of executed iterations, and allows the user to obtain different results when tweacking the input parameter NbIterations (if i>NbIterations, the IterationStep function does nothing).

The purpose of the substance used in this sample is to generate a flame animation, fully inspired from this ShaderToy by XT95: ShaderToy Flame

Launch the script with this command line, from the folder demos/:

python demos.py -fct: demoIterationFlame -args: "../sample/argsDemoIterationFlame.xml"

Function IterationStep (click on it to view it correctly…):

Function RayMarch in its initial state:

# Duplicate 63 times the pattern inside function RayMarch 
aFunction = sbsDoc.getSBSFunction(aFunctionIdentifier= 'RayMarch') 
createdNodes = aFunction.createIterationOnPattern(aNbIteration = 63, 
                                    aGUIOffset                 = [0, 200], 
                                    aNodeUIDs                  = ['1262101431', '1262101516'], 
                                    aNodeUIDs_NextPatternInput = ['1262101533', '1262101527']) 
 
 
# Connect the last created node with the end of the function 
aEndNode = aFunction.getFunctionNode('1262095290') 
aFunction.connectNodes(aLeftNode = createdNodes[-1], 
                       aRightNode = aEndNode, 
                       aRightNodeInput = sbsenum.FunctionInputEnum.SEQUENCE_IN)

And here is the result:

 Adobe

Get help faster and easier

New user?

Adobe MAX 2024

Adobe MAX
The Creativity Conference

Oct 14–16 Miami Beach and online

Adobe MAX

The Creativity Conference

Oct 14–16 Miami Beach and online

Adobe MAX 2024

Adobe MAX
The Creativity Conference

Oct 14–16 Miami Beach and online

Adobe MAX

The Creativity Conference

Oct 14–16 Miami Beach and online