# Shading networks

Shading Networks is a fundamental part of Simplygon's material pipeline and is used to describe materials in a flexible node-based manner. Most shading nodes supported by the Simplygon API are also supported by the plug-in, the input(s) / output(s) is a 1-to-1 mapping to the API.

Shading Networks are generated automatically when sending an asset using standard materials, for DirectX shaders a Shading Network template is required for Simplygon to understand how to map certain parameters to shading nodes, for example material name, mapping channel/UV-set and textures. This documentation will focus on DirectX shaders and how to map them to and from Simplygon.

# Fundamentals

A material can have several material channels, for example Ambient_Color, Diffuse_Color, Specular_Color, Opacity and Bump which can be found in standard materials in Max, or Ambient, Diffuse, Specular and Normals which will be used in HLSL examples later on. There are also possibility to add custom channels, if needed. Each of these material channels has its own shading network which is Simplygon’s way of describing a material.

Shading networks will be responsible for the visual representation of a material, as well as a description of what will be baked during processing. A shading network starts with one node, an exit node which can be of any (shading) node type, for example a texture-, add- or multiply node.

# Supported shading nodes

Max Name API Name No of Inputs Description
ShadingTextureNode spShadingTextureNode 0 Points at a texture
ShadingColorNode spShadingColorNode 1 RGBA color
ShadingAddNode spShadingAddNode 2 Adds two input nodes together
ShadingSubtractNode spShadingSubtractNode 2 Subtracts input 1 from input 0
ShadingMultiplyNode spShadingMuliplyNode 2 Multiplies input 1 with input 0
ShadingDivideNode spShadingDivideNode 2 Divides input 0 with input 1
ShadingInterpolateNode spShadingInterpolateNode 3 Interpolates input 0 and 1 based on 2
ShadingClampNode spShadingClampNode 3 Clamps input 0 based on 1 and 2
ShadingSwizzlingNode spShadingSwizzlingNode 4 Swizzles the channels
ShadingVertexColorNode spShadingVertexColorNode 0 Points at a vertex color set
ShadingLayeredBlendNode spShadingLayeredBlendNode (internal) unlimited Blends textures together from start to end, based on blend mode
ShadingPowNode spShadingPowNode 2 The power (input 1) of input 0
ShadingStepNode spShadingStepNode 2 Steps up/down the values from input 0 based on input 1
ShadingNormalize3Node spShadingNormalize3Node 1 Normalizes input 0
ShadingSqrtNode spShadingSqrtNode 1 Sqrt of input 0
ShadingDot3Node spShadingDot3Node 2 Dot of input 0 and input 1
ShadingCross3Node spShadingCross3Node 2 Cross of input 0 and input 1
ShadingCosNode spShadingCosNode 1 Cos of input 0
ShadingSinNode spShadingSinNode 1 Sin of input 0
ShadingMaxNode spShadingMaxNode 2 Max of input 0 and input 1
ShadingMinNode spShadingMinNode 2 Min of input 0 and input 1
ShadingEqualNode spShadingEqualNode 2 1.0f if value in input 0 equals the value in input 1, otherwise 0.0f
ShadingNotEqualNode spShadingNotEqualNode 2 1.0f if value in input 0 does not equal the value in input 1, otherwise 0.0f
ShadingGreaterThanNode spShadingGreaterThanNode 2 1.0f if value in input 0 is greater than the value in input 1, otherwise 0.0f
ShadingLessThanNode spShadingLessThanNode 2 1.0f if value in input 0 is smaller than the value in input 1, otherwise 0.0f

# Relevant script functions

All relevant script functions related to shading networks are listed in the table below. These functions can be executed from both MaxScript and Python. This documentation is focusing mainly on MaxScript but there are some Python examples as well.

Function Parameter(s) Description
sgsdk_UseShadingNetwork bool isEnabled Enables shading network pipeline.
sgsdk_CreateMaterialMetadata string materialName Creates an override material for the named material.
sgsdk_CreateShadingTextureNode string textureName Creates a texture node that connects to the given texture name in the shader.
sgsdk_CreateShading<nodeType>
Ex: sgsdk_CreateShadingColorNode
string nodeName Creates a node of the given type.
sgsdk_SetDefaultParameter int nodeIndex
uint inputSlot
double a
double b
double c
double d
Sets default values for the given node input.
sgsdk_AddAttributeToNode int nodeIndex
uint inputSlot
Sets an attribute to the specified node.
sgsdk_ConnectNodes int nodeIndex
uint inputSlot
int nodeIndexToConnect
Connects a node to another node’s input slot.
sgsdk_ConnectNodeToChannel int nodeIndex
int materialIndex
string channelName
Sets the exit node for the given material channel.
sgsdk_ConnectOutputToMaterialMetadata string materialName
string channelName
string samplerName
Specifies to which material and sampler the result will be connected to.
sgsdk_VertexColorNodeSetVertexChannel int nodeIndex
int mappingChannel
Specifies which color channel to read from.
sgsdk_SwizzlingNodeSetChannelSwizzle int nodeIndex
uint inputSlot
uint outputSlot
Sets output slot for the given input slot for the node, (RGBA -> 0, 1, 2, 3).
sgsdk_SetMappingChannel int nodeIndex
int mappingChannel
Sets the mapping channel/UV-set for the given texture node.
sgsdk_SetSRGB int nodeIndex
bool isSRGB
Sets the sRGB flag for the given texture node.
sgsdk_SetUVTiling int nodeIndex
float uTiling
float vTiling
Sets the UV-tiling for the given texture node.
sgsdk_SetUTiling int nodeIndex
float uTilingSets
Sets the U-tiling for the given texture node.
sgsdk_SetVTiling int nodeIndex
float vTiling
Sets the V-tiling for the given texture node.
sgsdk_SetUVOffset int nodeIndex
float uOffset
float vOffset
Sets the UV-offset for the given texture node
sgsdk_SetUOffset int nodeIndex
float uOffset
Sets the U-offset for the given texture node.
sgsdk_SetVOffset int nodeIndex
float vOffset
Sets the V-offset for the given texture node.
sgsdk_GetProcessedMeshes <none> Returns a list of processed mesh names from last run.
sgsdk_GetMaterialForMesh string meshName Return material name for the specified mesh.
sgsdk_GetMeshReusesMaterial string meshName Returns material name if the material is shared, otherwise empty string.
sgsdk_GetChannelsForMaterial string materialName Returns a list of material channel names for the specified material.
sgsdk_GetTexturePathForChannel string materialName
string channelName
Returns the texture path for the specified material channel.
sgsdk_GetMaterials <none> Returns a list of material names from the last run.
sgsdk_SetGenerateMaterial bool generateMaterial Specifies whether the plug-in should generate a standard material for baked LODs.
sgsdk_ProcessSelectedGeometries <none> Sends selected assets for processing.
sgsdk_SetTextureOutputDirectory string filePath Sets the output texture directory for baked textures.

# Special node attributes

Special node attributes can be set using AddAttributeToNode, see the list of attributes below.

Shading Node Attribute Type
ShadingTextureNode 1 – TileU, 2 – TileV
3 – MappingChannel
4 – TileUV
5 – OffsetU, 6 – OffsetV
7 – OffsetUV

# Examples

This section contains various Pipeline examples written in MaxScript and Python. We've left out some parts of the scripts to keep things as simple as possible.

  • from pymxs import runtime as rt must be declared at the top of each Python script.
  • reductionPipeline settings-path must be declared for both MaxScript and Python scripts where it makes sense.
  • a scene-selection is made before each sgsdk_RunPipelineOnSelection.

# Create material

Creates a shading network material override for the specified material.

MaxScript

DirectXShader = sgsdk_CreateMaterialMetadata "MyMaterial"

Python

from pymxs import runtime as rt

DirectXShader = rt.sgsdk_CreateMaterialMetadata('MyMaterial')

# Create node

Creates a texture node that is linked to a shader's texture slot named DiffuseTexture.

MaxScript

DiffuseTexture = sgsdk_CreateShadingTextureNode "DiffuseTexture"

Python

from pymxs import runtime as rt

DiffuseTexture = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture')

# Set default values

Creates a pow node, connects a texture to input 0 and sets a value for input 1.

MaxScript

powNode1 = sgsdk_CreateShadingPowNode "powNode1"
sgsdk_ConnectNodes powNode1 0 DiffuseTexture
sgsdk_SetDefaultParameter powNode1 1 0.5 0.5 0.5 0.5

Python

from pymxs import runtime as rt

powNode1 = rt.sgsdk_CreateShadingPowNode('powNode1')
rt.sgsdk_ConnectNodes(powNode1, 0, DiffuseTexture)
rt.sgsdk_SetDefaultParameter(powNode1, 1, 0.5, 0.5, 0.5, 0.5)

# Set exit node

Assigns a shading node to a material channel, in this case we connect as texture node (which points to DiffuseTexture in shader) to the Diffuse channel.

MaxScript

sgsdk_ConnectNodeToChannel DiffuseTexture DirectXShader "Diffuse"

Python

from pymxs import runtime as rt

rt.sgsdk_ConnectNodeToChannel(DiffuseTexture, DirectXShader, 'Diffuse')

# Set swizzle

Creates a swizzle node with the same texture input on all input channels and swizzles the output channels (in reverse). (R->A, G->B, B->G, A->R)

MaxScript

swizzNode = sgsdk_CreateShadingSwizzlingNode "swizzlingNode"
sgsdk_ConnectNodes swizzNode 0 DiffuseTexture
sgsdk_ConnectNodes swizzNode 1 DiffuseTexture
sgsdk_ConnectNodes swizzNode 2 DiffuseTexture
sgsdk_ConnectNodes swizzNode 3 DiffuseTexture
sgsdk_SwizzlingNodeSetChannelSwizzle swizzNode 0 3
sgsdk_SwizzlingNodeSetChannelSwizzle swizzNode 1 2
sgsdk_SwizzlingNodeSetChannelSwizzle swizzNode 2 1
sgsdk_SwizzlingNodeSetChannelSwizzle swizzNode 3 0

Python

from pymxs import runtime as rt

swizzNode = rt.sgsdk_CreateShadingSwizzlingNode('swizzlingNode')
rt.sgsdk_ConnectNodes(swizzNode, 0, DiffuseTexture)
rt.sgsdk_ConnectNodes(swizzNode, 1, DiffuseTexture)
rt.sgsdk_ConnectNodes(swizzNode, 2, DiffuseTexture)
rt.sgsdk_ConnectNodes(swizzNode, 3, DiffuseTexture)
rt.sgsdk_SwizzlingNodeSetChannelSwizzle(swizzNode, 0, 3)
rt.sgsdk_SwizzlingNodeSetChannelSwizzle(swizzNode, 1, 2)
rt.sgsdk_SwizzlingNodeSetChannelSwizzle(swizzNode, 2, 1)
rt.sgsdk_SwizzlingNodeSetChannelSwizzle(swizzNode, 3, 0)

# Set vertex color mapping channel

Creates a vertex color node and sets the node to read from mapping channel 0 (default vertex color channel in Max).

MaxScript

vertexColorNode = sgsdk_CreateShadingVertexColorNode "vertexColorNode"
sgsdk_VertexColorNodeSetVertexChannel vertexColorNode 0

Python

from pymxs import runtime as rt

vertexColorNode = rt.sgsdk_CreateShadingVertexColorNode('vertexColorNode')
rt.sgsdk_VertexColorNodeSetVertexChannel(vertexColorNode, 0)

# Override mapping channel / UV-set

Creates a texture and overrides the mapping channel / UV-set.

MaxScript

DiffuseTexture = sgsdk_CreateShadingTextureNode "DiffuseTexture"
sgsdk_SetMappingChannel DiffuseTexture 3

Python

from pymxs import runtime as rt

DiffuseTexture = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture')
rt.sgsdk_SetMappingChannel(DiffuseTexture, 3)

If we have the mapping channel exposed in the shader we can create a direct link between the texture node and mapping channel shader parameter using attributes. The Texture2D definition in the HLSL shader below contains a variable named MapChannel.

HLSL

Texture2D <float4> DiffuseTexture : DiffuseMap<
	string UIName = "Diffuse";
	string ResourceType = "2D";
	int Texcoord = 0;
	int MapChannel = 1;
>;

Note that the mapChannel in the line below starts with lowercase.

MaxScript

sgsdk_AddAttributeToNode DirectXShader "DiffuseTexturemapChannel" 3

Python

from pymxs import runtime as rt

rt.sgsdk_AddAttributeToNode(DirectXShader, "DiffuseTexturemapChannel", 3)

# Override sRGB

Overrides the sRGB flag for a texture node.

MaxScript

sgsdk_SetSRGB DiffuseTexture false

Python

from pymxs import runtime as rt

rt.sgsdk_SetSRGB(DiffuseTexture, false)

# Override tiling / offset

Creates a texture node and overrides the U/V-tiling and U/V-offset.

MaxScript

DiffuseTexture = sgsdk_CreateShadingTextureNode "DiffuseTexture"
sgsdk_SetUVTiling DiffuseTexture 2.0 2.0
sgsdk_SetUVOffset DiffuseTexture 0.25 0.25

Python

from pymxs import runtime as rt

DiffuseTexture = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture')
rt.sgsdk_SetUVTiling(DiffuseTexture, 2.0, 2.0)
rt.sgsdk_SetUVOffset(DiffuseTexture, 0.25, 0.25)

If tiling and / or offset parameters are defined in the HLSL shader it might be preferred to create a link between the texture node and the shader parameter(s) using attributes.

HLSL

float g_UTiling <
	string UIName = "UTiling";
	string UIWidget = "slider";
	float UIMin = -10.0f;
	float UIMax = 10.0f;
	float UIStep = 0.01f;
>   = 1.0f;
float g_VTiling <
	string UIName = "VTiling";
	string UIWidget = "slider";
	float UIMin = -10.0f;
	float UIMax = 10.0f;
	float UIStep = 0.01f;
>   = 1.0f;
float g_UOffset <
	string UIName = "UOffset";
	string UIWidget = "slider";
	float UIMin = -10.0f;
	float UIMax = 10.0f;
	float UIStep = 0.01f;
>   = 0.0f;
float g_VOffset <
	string UIName = "VOffset";
	string UIWidget = "slider";
	float UIMin = -10.0f;
	float UIMax = 10.0f;
	float UIStep = 0.01f;
>   = 0.0f;

Link the texture node to the parameters.

MaxScript

sgsdk_AddAttributeToNode DiffuseTexture "g_UTiling" 1
sgsdk_AddAttributeToNode DiffuseTexture "g_VTiling" 2
sgsdk_AddAttributeToNode DiffuseTexture "g_UOffset" 5
sgsdk_AddAttributeToNode DiffuseTexture "g_VOffset" 6

Python

from pymxs import runtime as rt

rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'g_UTiling', 1)
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'g_VTiling', 2)
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'g_UOffset', 5)
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'g_VOffset', 6)

# Back-mapping to custom shader

Specifies to which texture in the shader the baked texture will get connected to after the processing has been completed.

MaxScript

sgsdk_ConnectOutputToMaterialMetadata MaterialName "Diffuse" "DiffuseTexture"

Python

from pymxs import runtime as rt

rt.sgsdk_ConnectOutputToMaterialMetadata(MaterialName, 'Diffuse', 'DiffuseTexture')

# Setting up a DirectX material

This section goes through how to set up a DirectX (HLSL) as well as how to export material properties to Simplygon through scripting and shading networks. Simplicity before complexity - let’s create a cube as in figure 1. This cube will be the target for our soon to be created DirectX material. Cube

Figure 1: A cube.

To create a new DirectX material, go to the Material Editor (Rendering -> Material Editor), select an empty slot and click the Standard button (figure 2). The Material/Map Browser should now list all available materials. Select DirectX Shader in the list under standard materials and click OK.

Create DirectX material

Figure 2: Create DirectX material.

The Material Editor should now show the new (default) DirectX material including it’s HLSL properties. We will name it MyMaterial to make it consistent throughout this example (figure 3).

Change HLSL shader

Figure 3: Change HLSL shader.

While the standard HLSL shader might be suitable in some cases we will go for our own simple HLSL shader in this example, it will make more sense when doing the material mapping. Now, click on the button containing the path to the HLSL and navigate to the shader you would like to use (figure 3, figure 4).

Select effect file

Figure 4: Select effect file.

The Material Editor is now listing various material properties defined in the HLSL shader (figure 5). In this example we use a shader with texture slots representing a simple PBR material, the rendering has been somewhat simplified to make this example easier to follow.

Shader in material editor

Figure 5: Shader in material editor.

It is now time to assign the material to our asset: Select the asset, then go to the Material Editor, select the recently created shader material (MyMaterial) and click the button with a tiny globe and green box. Another way to do this is to simply drag the material using left mouse button from the Material Editor and drop it onto the asset.

The asset should now render with the new material. We’ve already prepared some textures for this specific material to make it somewhat more describing (figure 6).

Cube in viewport

Figure 6: Cube in viewport.

That is it, we now have an asset with a HLSL shader attached to it! The next section will go through the steps necessary to translate the shader to shading networks.

# Scripting

To translate a DirectX material to a Simplygon material you would probably want to extract certain properties from your shader, for example a texture. Below is a snippet of a DirectX shader defining a texture.

HLSL

Texture2D <float4> DiffuseTexture : DiffuseMap<
	string UIName = "Diffuse";
	string ResourceType = "2D";
	int Texcoord = 0;
	int MapChannel = 1;
>;

SamplerState DiffuseSampler
{
	Texture = DiffuseTexture;
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

Representation of the texture definition in the material editor.

Texture and mapping channel visible in material editor

Figure 7: Texture and mapping channel in material editor.

# Export

The first thing we need to do is to map the shader properties to shading networks to make the material compatible with Simplygon. Let’s start with defining which material to be overridden.

MaxScript

DirectXShader = sgsdk_CreateMaterialMetadata MaterialName

Then we create a texture node that points at a texture in the shader.

MaxScript

DiffuseTexture = sgsdk_CreateShadingTextureNode "DiffuseTexture"

To connect the shading network exit node to a material channel, in this case to a channel named Diffuse, we will add another row to our script.

MaxScript

sgsdk_ConnectNodeToChannel DiffuseTexture DirectXShader "Diffuse"

Before calling the Simplygon function there are some optional settings that can be set, for example texture output directory which can make life easier when importing/handling textures. Remember to select the asset before running the Simplygon command, otherwise the command will be ignored.

MaxScript

sgsdk_SetTextureOutputDirectory "d:\\CubeExample\\OutputTextures\\"
sgsdk_ProcessSelectedGeometries()

Let us add the NormalsSampler texture to the Normals channel, this should be fairly easy from what we have learned. Let us take a look at the definition in the HLSL shader.

HLSL

Texture2D <float4> NormalTexture : NORMALMAP<
	string UIName = "Normal";
	string ResourceType = "2D";
	int Texcoord = 0;
	int MapChannel = 1;
>;

SamplerState NormalsSampler
{
	Texture = NormalTexture;
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

We add similar rows for normals as for diffuse.

MaxScript

NormalsTexture = sgsdk_CreateShadingTextureNode "NormalTexture"
sgsdk_AddAttributeToNode DirectXShader "NormalsTexturemapChannel" 3
sgsdk_SetSRGB NormalsTexture false

This is a short example on how to use shading networks for translating a custom HLSL shader. Please take a look at the tables above including shading nodes and script functions to see what is available. There are also a couple of examples at the end of this document which focusing more on shading nodes and hierarchy. This is the end of the export part and how to get the asset to Simplygon. What happens next is more dependent on the settings that is used for processing and what kind of LODs that come back.

# Import

The Simplygon Max plug-in stores some material information from the last run which can be accessed via script. We will start by asking the plug-in for the material name of a mesh, material channels followed by textures.

MaxScript

...
sgsdk_RunPipelineOnSelection(pipeline)

processedMeshed = sgsdk_GetProcessedMeshes()
for mesh in processedMeshed do
(
	material = sgsdk_GetMaterialForMesh mesh
	print ("\tMesh: " + mesh)
	
	channels = sgsdk_GetChannelsForMaterial material
	for channel in channels do
	(
		print ("\t\t\tChannel: " + channel)
		texture = sgsdk_GetTexturePathForChannel material channel
		print ("\t\t\t\tTexture: " + texture)
	)
)

What remains is to create a new DirectX material for each processed asset and map back the information that was fetched from the plug-in. However, we will spare you from that right now as it includes quite many lines and special conditions that does not belong in this section. Please take a look at the examples for a more complete script.

# Example scripts

# Simplygon PBR (DirectX)

This script loops through all Simplygon PBR materials (HLSL connected to DirectX) and sets up shading networks accordingly. Then it sends the selected assets to Simplygon for processing. When the processing is done the LODs are returned to Max where the script takes over and maps the LOD materials back to a HLSL shader.

MaxScript

clear
sgsdk_Reset()
sgsdk_UseShadingNetwork true
sgsdk_SetTextureOutputDirectory "D:\\CubeExample\\OutputTextures\\"

-- path to cubemap
cubeMapPath = "D:\\CubeExample\\cube_map.dds"

-- list that will be populated with DirectX Shaders
SimplygonShaderMaterials = #()

-- loop all scene materials
for mat in scenematerials do
(
	print ("Material name: " + mat.name)

	-- get all material properties
	props = getPropNames  mat
	for i = 1 to props.count do
	(
		prop = getProperty  mat props[i]
		print ("\t\t\t." + props[i] + ": " + prop as string + "")

		-- if contains "technique"
		if (props[i] as string) == "technique" do
		(
			techniqueName =  mat.getCurrentTechniqueName()
			print ("\t\t\t." + props[i] + "Name: " + techniqueName as string + "")
			
			-- see if it is the shader we want by looking at technique name
			if techniqueName == "dx11|PBR_11" do
			(
				-- shader found, store in list
				appendIfUnique  SimplygonShaderMaterials  mat
			)
		)
	)
	print ("\n")

	-- loop through all sub-materials
	subMatCount = getNumSubMtls mat
	print ("\tNum sub-materials: " + subMatCount as string)
	for i = 1  to subMatCount do
	(
		subMat = getSubMtl mat i
		print ("\t\tSub-material name: " + subMat.name)

		-- get all sub-material properties
		props = getPropNames subMat
		for i = 1 to props.count do
		(
			prop = getProperty subMat props[i]
			print ("\t\t\t." + props[i] + ": " + prop as string + "")
			
			-- if contains "technique"
			if (props[i] as string) == "technique" do
			(
				techniqueName = subMat.getCurrentTechniqueName()
				print ("\t\t\t." + props[i] + "Name: " + techniqueName as string + "")

				-- see if it is the shader we want by looking at technique name
				if techniqueName == "dx11|PBR_11" do
				(
					-- shader found, store in list
					appendIfUnique  SimplygonShaderMaterials subMat
				)
			)
		)
		print ("\n")
	)
)

-- debug print of shader list
for mat in SimplygonShaderMaterials do
(
	print ("Found Simplygon DirectX Shader: " + mat.name)
)

-- setup shading network for each material/shader
for mat in SimplygonShaderMaterials do
(
	MaterialName = mat.name
	print ("Setting up shading network for: " + MaterialName)
		
	-- material
	DirectXShader = sgsdk_CreateMaterialMetadata MaterialName

	-- pre-setup
	sgsdk_MaterialColor MaterialName "Ambient" 0 0 0 1
	sgsdk_MaterialColor MaterialName "Diffuse" 1 1 1 1
	sgsdk_MaterialColor MaterialName "Specular" 1 1 1 1

	-- diffuse texture
	DiffuseTexture = sgsdk_CreateShadingTextureNode("DiffuseTexture");
	sgsdk_AddAttributeToNode DirectXShader "DiffuseTexturemapChannel" 3

	-- specular texture
	SpecularTexture = sgsdk_CreateShadingTextureNode("SpecularTexture");
	sgsdk_AddAttributeToNode DirectXShader "SpecularTexturemapChannel" 3

	-- roughness texture
	RoughnessTexture = sgsdk_CreateShadingTextureNode("RoughnessTexture");
	sgsdk_AddAttributeToNode DirectXShader "RoughnessTexturemapChannel" 3

	-- metallic texture
	MetallicTexture = sgsdk_CreateShadingTextureNode("MetallnessTexture");
	sgsdk_AddAttributeToNode DirectXShader "MetallicTexturemapChannel" 3

	-- normals texture
	NormalsTexture = sgsdk_CreateShadingTextureNode("NormalTexture");
	sgsdk_AddAttributeToNode DirectXShader "NormalsTexturemapChannel" 3
	sgsdk_SetSRGB NormalsTexture false

	-- connect shading network to Simplygon channel
	sgsdk_ConnectNodeToChannel DiffuseTexture DirectXShader "Diffuse"
	sgsdk_ConnectNodeToChannel SpecularTexture DirectXShader "Specular"
	sgsdk_ConnectNodeToChannel RoughnessTexture DirectXShader "Roughness"
	sgsdk_ConnectNodeToChannel MetallicTexture DirectXShader "Metalness"
	sgsdk_ConnectNodeToChannel NormalsTexture DirectXShader "Normals"

	-- map back texture on diffuse channel to Diffuse1 in HLSL shader
	sgsdk_ConnectOutputToMaterialMetadata MaterialName "Diffuse" "DiffuseTexture"
	sgsdk_ConnectOutputToMaterialMetadata MaterialName "Specular" "SpecularTexture"
	sgsdk_ConnectOutputToMaterialMetadata MaterialName "Roughness" "RoughnessTexture"
	sgsdk_ConnectOutputToMaterialMetadata MaterialName "Metalness" "MetallnessTexture"
	sgsdk_ConnectOutputToMaterialMetadata MaterialName "Normals" "NormalTexture"
)
-- start Simplygon (can be done in batch mode, no gui)
sgsdk_RunPipelineOnSelection(pipeline)

-- get list with processed meshes
processedMeshes = sgsdk_GetProcessedMeshes() -- not necessarily in order!!!

-- use mapping information to map cube map to the shaders cube map slot
print ("Simplygon material mapping information:")

-- for all processed meshes
for mesh in processedMeshes do
(
	-- get material by querying Simplygon plug-in
	material = sgsdk_GetMaterialForMesh mesh
	print ("\tMesh: " + mesh)

	-- get reuse material (if any)
	reuseMaterial = sgsdk_GetMeshReusesMaterial mesh
	if reuseMaterial != "" then
	(
		print ("\t\tReusing material: " + reuseMaterial)
	)
	else
	(
		print ("\t\tMaterial: " + material)
	)
	
	-- fetch all channels for the material
	channels = sgsdk_GetChannelsForMaterial material
	for channel in channels do
	(
		print ("\t\t\tChannel: " + channel)

		-- fetch texture path for each channel
		texture = sgsdk_GetTexturePathForChannel material channel
		print ("\t\t\t\tTexture: " + texture)
	)

	-- look for a matching material in the scene
	print ("\n\t\tLooking for matching material in scene...")
	for mat in scenematerials do
	(
		-- if material was found
		if material == mat.name do
		(
			-- get the material properties
			props = getPropNames  mat
			for i = 1 to props.count do
			(
				prop = getProperty  mat props[i]
				--print ("\t\t\t." + props[i] + ": " + prop as string + "")

				-- if it contains a "technique"
				if (props[i] as string) == "technique" do
				(
					techniqueName =  mat.getCurrentTechniqueName()
					--print ("\t\t\t." + props[i] + "Name: " + techniqueName as string + "")

					-- see if it is our shader by looking at the string
					if techniqueName == "dx11|PBR_11" do
					(
						print ("\t\tSimplygon shader found!")
						print ("\t\t\tLinking cube map...")

						-- if no cube map is set, set it
						if mat.CubemapTexture == undefined then
						(
							mat.CubemapTexture = openBitMap cubeMapPath
							print ("\t\t\tDone!")
						)
						else
						(
							print ("\t\t\tAlready set, ignoring...")
						)
					)
				)
			)
		)
	)
)

# Texture blending

This script sets up two texture nodes that are blended using the InterpolateNode.

MaxScript

DirectXShader = sgsdk_CreateMaterialMetadata MaterialName

DiffuseTexture1 = sgsdk_CreateShadingTextureNode "DiffuseTexture1"
sgsdk_AddAttributeToNode DiffuseTexture "DiffuseTexture1mapChannel" 3

DiffuseTexture2 = sgsdk_CreateShadingTextureNode "DiffuseTexture2"
sgsdk_AddAttributeToNode DiffuseTexture "DiffuseTexture2mapChannel" 3

BlendColor = sgsdk_CreateShadingColorNode "BlendColor"
sgsdk_SetDefaultParameter BlendColor 0 0.5 0.5 0.5 1.0

InterpolateNode = sgsdk_CreateShadingInterpolateNode "InterpolateNode"
sgsdk_ConnectNodes InterpolateNode 0 DiffuseTexture1
sgsdk_ConnectNodes InterpolateNode 1 DiffuseTexture2
sgsdk_ConnectNodes InterpolateNode 2 DefaultBlendColor

sgsdk_ConnectNodeToChannel InterpolateNode DirectXShader "Diffuse"
sgsdk_ConnectOutputToMaterialMetadata MaterialName "Diffuse" "DiffuseTexture"

Python

from pymxs import runtime as rt

DirectXShader = rt.sgsdk_CreateMaterialMetadata(MaterialName)

DiffuseTexture1 = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture1')
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'DiffuseTexture1mapChannel', 3)

DiffuseTexture2 = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture2')
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'DiffuseTexture2mapChannel', 3)

BlendColor = rt.sgsdk_CreateShadingColorNode('BlendColor')
sgsdk_SetDefaultParameter(BlendColor, 0, 0.5, 0.5, 0.5, 1.0)

InterpolateNode = rt.sgsdk_CreateShadingInterpolateNode('InterpolateNode')
rt.sgsdk_ConnectNodes(InterpolateNode, 0, DiffuseTexture1)
rt.sgsdk_ConnectNodes(InterpolateNode, 1, DiffuseTexture2)
rt.sgsdk_ConnectNodes(InterpolateNode, 2, DefaultBlendColor)

rt.sgsdk_ConnectNodeToChannel(InterpolateNode, DirectXShader, 'Diffuse')
rt.sgsdk_ConnectOutputToMaterialMetadata(MaterialName, 'Diffuse', 'DiffuseTexture')

# Blending with vertex colors

This script sets up three vertex color nodes where the last one is used as blend weight. This example is based on the InterpolateNode.

MaxScript

DirectXShader = sgsdk_CreateMaterialMetadata MaterialName

DiffuseTexture1 = sgsdk_CreateShadingTextureNode "DiffuseTexture1"
sgsdk_AddAttributeToNode DiffuseTexture "DiffuseTexture1mapChannel" 3

DiffuseTexture2 = sgsdk_CreateShadingTextureNode "DiffuseTexture2"
sgsdk_AddAttributeToNode DiffuseTexture "DiffuseTexture2mapChannel" 3

BlendColor = sgsdk_CreateShadingVertexColorNode "BlendColor"
sgsdk_VertexColorNodeSetVertexChannel BlendColor 0

InterpolateNode = sgsdk_CreateShadingInterpolateNode "InterpolateNode"
sgsdk_ConnectNodes InterpolateNode 0 DiffuseTexture1
sgsdk_ConnectNodes InterpolateNode 1 DiffuseTexture2
sgsdk_ConnectNodes InterpolateNode 2 DefaultBlendColor

sgsdk_ConnectNodeToChannel InterpolateNode DirectXShader "Diffuse"
sgsdk_ConnectOutputToMaterialMetadata MaterialName "Diffuse" "DiffuseTexture"

Python

from pymxs import runtime as rt

DirectXShader = rt.sgsdk_CreateMaterialMetadata(MaterialName)

DiffuseTexture1 = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture1')
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'DiffuseTexture1mapChannel', 3)

DiffuseTexture2 = rt.sgsdk_CreateShadingTextureNode('DiffuseTexture2')
rt.sgsdk_AddAttributeToNode(DiffuseTexture, 'DiffuseTexture2mapChannel', 3)

BlendColor = rt.sgsdk_CreateShadingVertexColorNode('BlendColor')
rt.sgsdk_VertexColorNodeSetVertexChannel(BlendColor, 0)

InterpolateNode = rt.sgsdk_CreateShadingInterpolateNode('InterpolateNode')
rt.sgsdk_ConnectNodes(InterpolateNode, 0, DiffuseTexture1)
rt.sgsdk_ConnectNodes(InterpolateNode, 1, DiffuseTexture2)
rt.sgsdk_ConnectNodes(InterpolateNode, 2, DefaultBlendColor)

rt.sgsdk_ConnectNodeToChannel(InterpolateNode, DirectXShader, 'Diffuse')
rt.sgsdk_ConnectOutputToMaterialMetadata(MaterialName, 'Diffuse', 'DiffuseTexture')

# Common issues

# Brightness differences, blending issues and/or discontinuous normals

Brightness differences between the original asset's textures and the LOD’s textures are usually caused by mismatching color spaces.
For example, if using sRGB color space (or gamma around 2.2) on a texture on the original asset and create a texture node with linear color space, then Simplygon will read the texture incorrectly and make the incorrect conversions, thus affecting the resulting baked textures.

The texture node’s sRGB setting is the input sRGB, while the sRGB flag in Simplygon Settings is the output sRGB. The input and output sRGB need to be set to the same value if the texture shall come out in the same space it came in. If a color space conversion is intended they can be set differently. These issues also apply for blending - if we set different color spaces on texture nodes the result of the blending will get different compared to only sRGB or linear textures.

Max tends to apply sRGB to imported textures by default regardless of whether it is a normal, specular or diffuse map. This happens especially from FBX, so it can sometimes be beneficial to write a script that takes care of this issue before exporting an asset. If a normal map comes back from Simplygon with the sRGB flag enabled the rendering will show up with visible edges/islands or rather discontinuous normals - turning of sRGB normally solves this issue.