Shading networks
Introduction
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 supported by the plug-in and the input for the nodes matches the ones in 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 in Simplygon can have several material channels, for example Ambient, Diffuse and Normals as well as custom channels. 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
Maya Name | API Name | No of Inputs | Description |
---|---|---|---|
TextureNode | spShadingTextureNode |
0 | Points at a texture |
ColorNode | spShadingColorNode |
1 | RGBA color |
AddNode | spShadingAddNode |
2 | Adds two input nodes together |
SubtractNode | spShadingSubtractNode |
2 | Subtracts input 1 from input 0 |
MultiplyNode | spShadingMuliplyNode |
2 | Multiplies input 1 with input 0 |
DivideNode | spShadingDivideNode |
2 | Divides input 0 with input 1 |
InterpolateNode | spShadingInterpolateNode |
3 | Interpolates input 0 and 1 based on 2 |
ClampNode | spShadingClampNode |
3 | Clamps input 0 based on 1 and 2 |
SwizzlingNode | spShadingSwizzlingNode |
4 | Swizzles the channels |
VertexColorNode | spShadingVertexColorNode |
0 | Points at a vertex color set |
LayeredBlendNode | spShadingLayeredBlendNode (internal) |
unlimited | Blends textures together from start to end, based on blend mode |
PowNode | spShadingPowNode |
2 | The power (input 1) of input 0 |
StepNode | spShadingStepNode |
2 | Steps up/down the values from input 0 based on input 1 |
Normalize3Node | spShadingNormalize3Node |
1 | Normalizes input 0 |
SqrtNode | spShadingSqrtNode |
1 | Sqrt of input 0 |
Dot3Node | spShadingDot3Node |
2 | Dot of input 0 and input 1 |
Cross3Node | spShadingCross3Node |
2 | Cross of input 0 and input 1 |
CosNode | spShadingCosNode |
1 | Cos of input 0 |
SinNode | spShadingSinNode |
1 | Sin of input 0 |
MaxNode | spShadingMaxNode |
2 | Max of input 0 and input 1 |
MinNode | spShadingMinNode |
2 | Min of input 0 and input 1 |
EqualNode | spShadingEqualNode |
2 | 1.0f if value in input 0 equals the value in input 1, otherwise 0.0f |
NotEqualNode | spShadingNotEqualNode |
2 | 1.0f if value in input 0 does not equal the value in input 1, otherwise 0.0f |
GreaterThanNode | spShadingGreaterThanNode |
2 | 1.0f if value in input 0 is greater than the value in input 1, otherwise 0.0f |
LessThanNode | spShadingLessThanNode |
2 | 1.0f if value in input 0 is smaller than the value in input 1, otherwise 0.0f |
Relevant commands and flags
All relevant commands and flags are listed below. The commands can be executed from both MEL and Python, this example is focusing on MEL.
Command |
---|
SimplygonShadingNetwork |
Short | Long | Argument(s) | Description |
---|---|---|---|
-cn |
-CreateNode |
string materialName string nodeType string nodeName |
Creates a node of the given type. |
-si |
-SetInput |
string materialName string nodeName uint inputSlot string nodeNameToConnect |
Connects a node to another node’s input slot. |
-sd |
-SetDefault |
string materialName string nodeName uint inputSlot double A double B double C |
Sets default values for the given node input. |
-sd1 |
-SetDefault1f |
string materialName string nodeName uint inputSlot uint component double A |
Sets default value for the given node input on the given component [RGBA, 0-3]. |
-sce |
-SetExitNode |
string materialName string channelName string nodeName |
Sets the exit node for the given material channel. |
-exf |
-ExportXML |
string materialName string channelName string filePath |
Exports shading network for the given material channel to file. |
-swz |
-Swizzle |
string materialName string nodeName uint inputSlot uint outputSlot |
Sets output slot for the given input slot for the specified swizzle node (RGBA -> 0, 1, 2, 3). |
-svn |
-SetVertexColorName |
string materialName string nodeName string colorSetName |
Sets which color set to sample from for the given vertex color node. |
-svc |
-SetVertCol |
string materialName string nodeName uint colorSet |
Sets which color set to sample from for the given vertex color node. |
-uva |
-SetUVAll |
string nodeName string uvSetName |
Sets the UV-set for the texture nodes matching the specified node name. |
-uvm |
-SetUVMaterial |
string materialName string nodeName string uvSetName |
Sets the UV-set for the texture nodes that matches the specified node name for the given material. |
-uvc |
-SetUVMaterialChannel |
string materialName string channelName string nodeName string uvSetName |
Sets the UV-set for the texture nodes that matches the specified node name for the given material channel. |
-sa |
-SetSRGBAll |
string nodeName bool isSRGB |
Sets the sRGB flag for the texture nodes matching the specified node name. |
-sm |
-SetSRGBMaterial |
string materialName string nodeName bool isSRGB |
Sets the sRGB flag for the texture nodes that matches the specified node name for the given material. |
-sc |
-SetSRGBMaterialChannel |
string materialName string channelName string nodeName bool isSRGB |
Sets the sRGB flag for the texture nodes that matches the specified node name for the given material channel. |
-tmc |
-SetUVTilingMaterialChannel |
string materialName string channelName string nodeName double uTiling double vTiling |
Sets u- and v-tiling for the given texture node. |
-omc |
-SetUVOffsetMaterialChannel |
string materialName string channelName string nodeName double uOffset double vOffset |
Sets u- and v-offset for the given texture node. |
Command |
---|
SimplygonQuery |
Short | Long | Argument(s) | Description |
---|---|---|---|
-gpm |
-GetProcessedMeshes |
<none> | Returns a list of processed mesh names from the last run. |
-mrm |
-MeshReusesMaterial |
string meshName |
Returns material name if the material is shared, otherwise empty string. |
-gmm |
-GetMaterialForMesh |
string meshName |
Return material name for the specified mesh. |
-gm |
-GetMaterials |
<none> | Returns a list of material names from the last run. |
-gcm |
-GetChannelsForMaterial |
string materialName |
Returns a list of material channel names for the specified material. |
-gtc |
-GetTexturePathForChannel |
string materialName string channelName |
Returns the texture path for the specified material channel. |
Command |
---|
Simplygon |
Short | Long | Description | Argument(s) |
---|---|---|---|
-asm |
-AddShaderMaterial |
string materialNames |
Specifies which materials to override at the extraction stage. Material names are separated by "|". Ex: "redMaterial | blueMaterial". |
-ixf |
-ImportShaderXML |
string materialName string channelName string filePath |
Imports shading network for the given material channel. |
Examples
This section contains some examples on how to use shading networks through MEL. The variable $materialName
will be used as a place holder for the name of the material we are currently working on.
Create node
-cn TextureNode "DiffuseSampler"
-cn TextureNode "NormalsSampler";
Set default values
-sd $materialName multiplyNode 0 1.0 1.0 1.0
-sd $materialName multiplyNode 1 0.25 0.25 0.25
-sd1 $materialName multiplyNode 1 3 1.0;
Set exit node
-sce $materialName Diffuse "DiffuseSampler";
Set swizzle
-si $materialName swizzleNode 0 "DiffuseSampler"
-si $materialName swizzleNode 1 "DiffuseSampler"
-si $materialName swizzleNode 2 "DiffuseSampler"
-si $materialName swizzleNode 3 "DiffuseSampler"
-swz $materialName swizzleNode 0 3
-swz $materialName swizzleNode 1 2
-swz $materialName swizzleNode 2 1
-swz $materialName swizzleNode 3 0;
Set vertex color set
-svn $materialName vertexColorNode "colorSet1";
Override UV-set
-uva DiffuseSampler "map1";
Override sRGB
-sc $materialName Diffuse "DiffuseSampler" true
-sc $materialName Normals "NormalsSampler" false;
Override tiling/offset
-tmc $materialName Diffuse "DiffuseSampler" 2.0 2.0
-omc $materialName Diffuse "DiffuseSampler" 0.5 0.5;
Export Shading Network XML
-exf cgfx_leather Diffuse "c:\\ShadingNetworksTemp\\cgfx_leather_diffuse.xml";
Import Shading Network XML
Simplygon -asm cgfx_leather|cgfx_wood
-ixf cgfx_leather Diffuse "c:\\ShadingNetworksTemp\\cgfx_leather_diffuse.xml"
-ixf cgfx_wood Diffuse "c:\\ShadingNetworksTemp\\cgfx_wood_diffuse.xml";
Setting up CGFX/DirectX material
This section goes through how to set up a CGFX/DirectX (HLSL) material, how to export and import materials to/from Simplygon through scripting and shading networks. First, make sure the DLL for the specific material type is loaded in the plug-in manager (Windows -> Settings/Preferences -> Plug-in Manager) as in figure 1.
Figure 1: CGFX/DirectX plug-in loaded in the plug-in manager.
Let’s create a cube as in figure 2. This cube will be the target for our soon to be created DirectX material.
Figure 2: A cube.
To create a new material, go to Hypershade (Windows -> Rendering Editors -> Hypershade) and click either Cgfx or DirectX shader (figure 3).
The following sections will focus more on the CGFX shader, and as most things are identical for DirectX shaders it would be redundant. Please see the examples for complete CGFX/DirectX import/export scripts.
Figure 3: How to create CGFX material in Hypershade.
In the panel (figure 3) there is a material name which we name MyMaterial in this example. There is an empty field labeled CgFX File below the name - click Open and navigate to the HLSL file you want to use. The Property Editor should now list various material properties depending on how the HLSL is written as well as the technique (figure 4).
In this example we use a HLSL shader with slots matching a simple PBR material, the rendering is somewhat simplified to make this example easier to follow.
Figure 4: CGFX material with properties.
It is now time to assign the material to our asset: Select the asset, go to Hypershade, click and hold the right mouse button on the recently created shader followed by releasing the button when hovering assign to selection
.
Another way to do this is to simply drag the material from Hypershade (using the middle mouse button) and drop it onto the asset.
If we close Hypershade and bring forth the attribute editor (ctrl + A), then the asset should render with the new material. We have already prepared some textures for this specific material to make it somewhat more describing (figure 5).
Figure 5: Cube with CGFX material successfully applied.
That is it, we now have an asset with a HLSL shader attached to it!
Scripting
To translate a CGFX/DirectX material to a Simplygon material you might want to extract certain properties from your shader, for example a texture. Below is a snippet of a DirectX shader defining a texture.
texture DiffuseTexture : Diffuse
<
string ResourceType = "2D";
string UIName = "Diffuse Texture";
>;
sampler2D DiffuseSampler = sampler_state
{
Texture = <DiffuseTexture>;
MinFilter = LinearMipMapLinear;
MagFilter = Linear;
};
The sampler will show up in the CGFX material as in figure 6.
Figure 6: Diffuse texture slot.
Export
The first thing we need to do is to create a (shading) texture node using the SimplygonShadingNetwork
command along with the -cn
flag, the material name as well as the name of the texture sampler.
SimplygonShadingNetwork
-cn $materialName TextureNode "DiffuseSampler";
To connect the shading network exit node to a material channel, in this case to a channel named Diffuse, we add another row to our script.
SimplygonShadingNetwork
-cn $materialName TextureNode "DiffuseSampler"
-sce $materialName Diffuse "DiffuseSampler";
Commands in Maya are not state based and does not share any information, so to be able to pass the shading network templates from SimplygonShadingNetwork
to Simplygon we will store the information to disk (using the exf
flag). Let’s add another flag to the SimplygonShadingNetwork
command.
SimplygonShadingNetwork
-cn $materialName TextureNode "DiffuseSampler"
-sce $materialName Diffuse "DiffuseSampler"
-exf $materialName Diffuse ($xmlExportPath + $materialName + "_Diffuse.xml");
Now, after running this script there should be a xml file in the specified output folder containing the shading network template for the specified material channel. We will now use the generated template as input when calling the Simplygon
command.
First we need the material input arguments for the Simplygon
command, which in this case is the path to the xml we exported earlier. We import it using import xml file (ixf).
string $arguments = " -ixf \"" + $materialName + "\" Diffuse \"" + ($xmlExportPath + $materialName + "_Diffuse.xml\"");
When calling the Simplygon
command we can specify some optional parameters which in this case are:-dgm
to not generate standard blinn phong material when importing LOD) and-tod
to specify a texture output directory where the resulting textures shall end up after LOD import.
Finally we add the required flag, add shader material (-asm
) followed by material name and the previous xml import path. Remember to select the asset before running the Simplygon
command - otherwise the command will be ignored.
string $processedMeshes[] = eval("Simplygon -dgm -tod $textureOutputDirectory -asm $materialName " + $arguments);
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.
C#
texture NormalTexture : Normal
<
string ResourceType = "2D";
string UIName = "Normal Texture";
>;
sampler2D NormalsSampler = sampler_state
{
Texture = <NormalTexture>;
MinFilter = LinearMipMapLinear;
MagFilter = Linear;
};
We add similar rows for normals as for the diffuse texture.
SimplygonShadingNetwork
-cn $materialName TextureNode "DiffuseSampler"
-cn $materialName TextureNode "NormalsSampler"
-sce $materialName Diffuse "DiffuseSampler"
-sce $materialName Normals "NormalsSampler"
-exf $materialName Diffuse ($xmlExportPath + $materialName + "_Diffuse.xml")
-exf $materialName Normals ($xmlExportPath + $materialName + "_Normals.xml");
string $arguments =
" -ixf \"" + $materialName + "\" Diffuse \"" + ($xmlExportPath + $materialName + "_Diffuse.xml\"") +
" -ixf \"" + $materialName + "\" Normals \"" + ($xmlExportPath + $materialName + "_Normals.xml\"");
string $processedMeshes[] = eval("Simplygon -dgm -tod $textureOutputDirectory -asm $materialName " + $arguments);
This is a short example of how to use shading networks to translate a custom HLSL shader. Please take a look at the tables above including shading nodes, commands and flags 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
If we continue where we left off at the export step the return value of the Simplygon
command will contain the names of the processed meshes.
string $processedMeshes[] = eval("Simplygon -dgm -tod $textureOutputDirectory -asm $materialName " + $arguments);
The Simplygon Maya plug-in stores some material information from the last run to give the user a chance to manually setup materials via script. To access such information we will use the SimplygonQuery
command with the appropriate flags. We will start by asking the plug-in for the material name of a mesh, material channels as well as textures.
for( $m in $processedMeshes )
{
$m = substituteAllString($m, "|", "");
print ("\nMesh: " + $m + "\n");
string $material;
$material = `SimplygonQuery -gmm $m`;
print (" Material: " + $material + "\n");
$channels = `SimplygonQuery -gcm $material`;
for( $c in $channels )
{
print (" Channel: " + $c + "\n");
$filepath = `SimplygonQuery -gtc $material $c`;
print (" Texture: " + $filepath + "\n");
}
}
What remains is to create a new CGFX/DirectX material and map back the information that was fetched from SimplygonQuery
command. However, we will spare you from that right now as it includes quite many lines and special conditions that may not belong in this section. Please take a look at the examples for a more complete script.
Example scripts
Simplygon PBR (CGFX)
This script loops through all Simplygon PBR materials (HLSL connected to CGFX) 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 Maya where the script takes over and maps the LOD materials back to a HLSL shader.
string $simplygonDocumentsPath[] = `SimplygonPreset -gsp`;
string $rootPath = $simplygonDocumentsPath[0] + "\\Samples\\Maya\\SimplygonMan";
// settings for batch mode
string $simplygonSettingsFile = $rootPath + "\\Presets\\LODChainPBR.preset";
// path to Simplygon PBR shader
string $pbgShaderFile = $rootPath + "\\Shader\\SimplygonPBRShader.fx";
// "" = default, "d:\\TextureOutputFolder\\" = custom
string $textureOutputDirectory = $rootPath + "\\OutputTextures\\";
// cube map path for all meshes with material baking
string $cubeMapFilePath = $rootPath + "\\CubeMap\\cube_map.dds";
// drive to store temporary xml files (c might be protected)
string $xmlExportPath = $rootPath + "\\XMLTempFolder\\";
// set to 1 to enable batch mode
int $batchMode = 0;
// populate material list
string $cgfxMaterials[] = `ls -type cgfxShader`;
for( $cgfx_mat in $cgfxMaterials )
{
string $technique = `getAttr ($cgfx_mat + ".technique")`;
if($technique != "PBR")
{
$cgfx_mat = "";
}
}
// create shading networks for each cgfx material
// and export template to xml (for later use)
string $inputMaterialString = "";
for( $cgfx_mat in $cgfxMaterials )
{
if(size ($cgfx_mat) == 0)
continue;
print ("\nSimplygon PBR Shader:\n Name: " + $cgfx_mat + "\n");
string $normalMaps[] = `listConnections -type "file" ($cgfx_mat + ".NormalsSampler")`;
for ( $nMap in $normalMaps )
{
string $filename = `getAttr($nMap + ".fileTextureName")`;
print (" Normal map (" + $nMap + "): " + $filename + "\n");
// check color space for all textures connected to the normal map of this material
string $currentColorSpace = `getAttr ($nMap + ".colorSpace")`;
print (" Current color space: " + $currentColorSpace + "\n");
if($currentColorSpace != "Raw")
{
string $availableColorSpaces[] = `colorManagementPrefs -q -inputSpaceNames`;
if ( stringArrayContains("Raw", $availableColorSpaces) )
{
setAttr ($nMap+".ignoreColorSpaceFileRules") 1;
setAttr ($nMap+".colorSpace") -type "string" "Raw";
print (" New color space: Raw\n");
}
else
{
print ("Normal map with incorrect color space found but could not find a suitable color space, please check the color space setting for this texture!\n");
}
}
int $NameBasedSearchWarning = false;
string $result = `match "_n." $filename`;
if($result == "_n.")
{
$NameBasedSearchWarning = true;
}
$result = `match "normals" $filename`;
if($result == "normals")
{
$NameBasedSearchWarning = true;
}
if($NameBasedSearchWarning == true)
{
print (" Prediction (name based): most likely a normal map)\n");
}
}
SimplygonShadingNetwork
// create texture nodes
-cn $cgfx_mat TextureNode DiffuseSampler
-cn $cgfx_mat TextureNode SpecularSampler
-cn $cgfx_mat TextureNode RoughnessSampler
-cn $cgfx_mat TextureNode MetalnessSampler
-cn $cgfx_mat TextureNode NormalsSampler
// override uv channels
-uva DiffuseSampler map1
-uva SpecularSampler map1
-uva RoughnessSampler map1
-uva MetalnessSampler map1
-uva NormalsSampler map1
// override sRGB
-sa DiffuseSampler true
-sa SpecularSampler true
-sa RoughnessSampler true
-sa MetalnessSampler true
-sa NormalsSampler false
// connect textures to simplygon channels
-sce $cgfx_mat Diffuse DiffuseSampler
-sce $cgfx_mat Specular SpecularSampler
-sce $cgfx_mat Roughness RoughnessSampler
-sce $cgfx_mat Metalness MetalnessSampler
-sce $cgfx_mat Normals NormalsSampler
-tmc $cgfx_mat Diffuse DiffuseSampler 1.0 1.0
-omc $cgfx_mat Diffuse DiffuseSampler 0.0 0.0
// export material channel templates to xml for later use
-exf $cgfx_mat Diffuse ($xmlExportPath + $cgfx_mat + "_Diffuse.xml")
-exf $cgfx_mat Specular ($xmlExportPath + $cgfx_mat + "_Specular.xml")
-exf $cgfx_mat Roughness ($xmlExportPath + $cgfx_mat + "_Roughness.xml")
-exf $cgfx_mat Metalness ($xmlExportPath + $cgfx_mat + "_Metalness.xml")
-exf $cgfx_mat Normals ($xmlExportPath + $cgfx_mat + "_Normals.xml");
// append material name to string (for later use)
$inputMaterialString += $cgfx_mat + "|";
}
// parse arguments
string $arguments = "";
for( $cgfx_material in $cgfxMaterials )
{
if(size ($cgfx_material) == 0)
continue;
$arguments += " -ixf \"" + $cgfx_material + "\" Diffuse \"" + ($xmlExportPath + $cgfx_material + "_Diffuse.xml\"");
$arguments += " -ixf \"" + $cgfx_material + "\" Specular \"" + ($xmlExportPath + $cgfx_material + "_Specular.xml\"");
$arguments += " -ixf \"" + $cgfx_material + "\" Roughness \"" + ($xmlExportPath + $cgfx_material + "_Roughness.xml\"");
$arguments += " -ixf \"" + $cgfx_material + "\" Metalness \"" + ($xmlExportPath + $cgfx_material + "_Metalness.xml\"");
$arguments += " -ixf \"" + $cgfx_material + "\" Normals \"" + ($xmlExportPath + $cgfx_material + "_Normals.xml\"");
}
// correct double slashes
$arguments = substituteAllString($arguments, "\\", "\\\\");
// launch Simplygon and fetch the result
string $processedMeshes[];
if($batchMode)
{
$processedMeshes = eval("Simplygon -b -dgm -tod $textureOutputDirectory -sf $simplygonSettingsFile -asm $inputMaterialString" + $arguments);
}
else
{
$processedMeshes = eval("Simplygon -ssh \"PBR\" -dgm -tod $textureOutputDirectory -sf $simplygonSettingsFile -asm $inputMaterialString" + $arguments);
}
// link cube map
string $cubeMapNode = `shadingNode -isColorManaged -asTexture file`;
setAttr -type "string" ( $cubeMapNode + ".fileTextureName" ) $cubeMapFilePath;
// fetch lod names
int $lodIndex = 0;
string $materialNameArray[];
string $SGNameArray[];
//$processedMeshes = `SimplygonQuery -gpm`;
for( $m in $processedMeshes )
{
// correct mesh names from Simplygon call
$m = substituteAllString($m, "|", "");
print ("\nMesh: " + $m + "\n");
// offset new mesh along x-axis
select -r $m;
$transAttr = $m+".translateX";
float $bbox[] = `exactWorldBoundingBox`;
float $moveOffset = ($lodIndex+1)*($bbox[3]-$bbox[0])*1.1;
setAttr -lock off $transAttr;
move -r $moveOffset 0 0;
string $material;
// fetch material name for mesh,
// start with checking for reused materials
$reuseMaterial = `SimplygonQuery -mrm $m`;
if(size ($reuseMaterial) == 0)
{
$material = `SimplygonQuery -gmm $m`;
}
else
{
print (" Reusing material: " + $reuseMaterial + "\n");
int $i = stringArrayFind( $reuseMaterial, 0, $materialNameArray );
if($i < 0)
{
print (" This mesh does not have material baking, ignoring!\n");
$lodIndex++;
continue;
}
// material was found in lod chain, continuing
string $nameOfMaterialNode = $SGNameArray[$i];
// assign material to selected mesh
select -r $m;
if(size ($nameOfMaterialNode) != 0)
{
sets -e -forceElement $nameOfMaterialNode;
}
// offset new mesh along x-axis
$lodIndex++;
continue;
}
print (" Material: " + $material + "\n");
// create cgfx shader and matching SG node
string $shader = `shadingNode -asShader cgfxShader`;
string $shaderSG = `sets -renderable true -noSurfaceShader true -empty -name ($shader + "SG")`;
stringArrayInsertAtIndex($lodIndex, $SGNameArray, $shaderSG);
stringArrayInsertAtIndex($lodIndex, $materialNameArray, $material);
// connect shader to shaderSG
connectAttr -f ( $shader + ".outColor" ) ( $shaderSG + ".surfaceShader" );
// user Simplygon PBR shader with default PBR teqhnique
cgfxShader -e -fx $pbgShaderFile $shader;
setAttr -type "string" ($shader + ".technique") "PBR";
// connect cube map
connectAttr -force ($cubeMapNode + ".outColor") ($shader + ".CubemapSampler");
// fetch channel names for mesh material
$channels = `SimplygonQuery -gcm $material`;
for( $c in $channels )
{
// fetch texture path for channel
print (" Channel: " + $c + "\n");
$filepath = `SimplygonQuery -gtc $material $c`;
print (" Texture: " + $filepath + "\n");
// create texture nodes and link textures
string $fileNode = `shadingNode -isColorManaged -asTexture file`;
setAttr -type "string" ( $fileNode + ".fileTextureName" ) $filepath;
if($c == "Diffuse")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".DiffuseSampler");
}
else if($c == "Specular")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".SpecularSampler");
}
else if($c == "Roughness")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".RoughnessSampler");
}
else if($c == "Metalness")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".MetalnessSampler");
}
else if($c == "Normals")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".NormalsSampler");
// force colorspace, if possible
string $currentColorSpace = `getAttr ($fileNode + ".colorSpace")`;
if($currentColorSpace != "Raw")
{
string $availableColorSpaces[] = `colorManagementPrefs -q -inputSpaceNames`;
if ( stringArrayContains("Raw", $availableColorSpaces) )
{
setAttr ($fileNode+".ignoreColorSpaceFileRules") 1;
setAttr ($fileNode+".colorSpace") -type "string" "Raw";
}
}
}
}
// assign material to selected mesh
select -r $m;
sets -e -forceElement $shaderSG;
// offset new mesh along x-axis
$lodIndex++;
}
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 Maya where the script takes over and maps the LOD materials back to a HLSL shader.
string $simplygonDocumentsPath[] = `SimplygonPreset -gsp`;
string $rootPath = $simplygonDocumentsPath[0] + "\\Samples\\Maya\\SimplygonMan";
// settings for batch mode
string $simplygonSettingsFile = $rootPath + "\\Presets\\LODChainPBR.preset";
// path to Simplygon PBR shader
string $pbrShaderFile = $rootPath + "\\Shader\\SimplygonPBRShader.fx";
// "" = default, "d:\\TextureOutputFolder\\" = custom
string $textureOutputDirectory = $rootPath + "\\OutputTextures\\";
// cube map path for all meshes with material baking
string $cubeMapFilePath = $rootPath + "\\CubeMap\\cube_map.dds";
// drive to store temporary xml files (c might be protected)
string $xmlExportPath = $rootPath + "\\XMLTempFolder\\";
// set to 1 to enable batch mode
int $batchMode = 0;
// populate material list
string $directxMaterials[] = `ls -type dx11Shader`;
for( $directx_mat in $directxMaterials )
{
string $technique = `getAttr ($directx_mat + ".technique")`;
if($technique != "PBR")
{
$directx_mat = "";
}
}
// create shading networks for each DirectX11 material
// and export template to xml (for later use)
string $inputMaterialString = "";
for( $directx_mat in $directxMaterials )
{
if(size ($directx_mat) == 0)
continue;
print ("\nDirectX11 Shader:\n Name: " + $directx_mat + "\n");
string $normalMaps[] = `listConnections -type "file" ($directx_mat + ".NormalsSampler")`;
for ( $nMap in $normalMaps )
{
string $filename = `getAttr($nMap + ".fileTextureName")`;
print (" Normal map (" + $nMap + "): " + $filename + "\n");
// check color space for all textures connected to the normal map of this material
string $currentColorSpace = `getAttr ($nMap + ".colorSpace")`;
print (" Current color space: " + $currentColorSpace + "\n");
if($currentColorSpace != "Raw")
{
string $availableColorSpaces[] = `colorManagementPrefs -q -inputSpaceNames`;
if ( stringArrayContains("Raw", $availableColorSpaces) )
{
setAttr ($nMap+".ignoreColorSpaceFileRules") 1;
setAttr ($nMap+".colorSpace") -type "string" "Raw";
print (" New color space: Raw\n");
}
else
{
print ("Normal map with incorrect color space found but could not find a suitable color space, please check the color space setting for this texture!\n");
}
}
int $NameBasedSearchWarning = false;
string $result = `match "_n." $filename`;
if($result == "_n.")
{
$NameBasedSearchWarning = true;
}
$result = `match "normals" $filename`;
if($result == "normals")
{
$NameBasedSearchWarning = true;
}
if($NameBasedSearchWarning == true)
{
print (" Prediction (name based): most likely a normal map)\n");
}
}
SimplygonShadingNetwork
// create texture nodes
-cn $directx_mat TextureNode DiffuseSampler
-cn $directx_mat TextureNode SpecularSampler
-cn $directx_mat TextureNode RoughnessSampler
-cn $directx_mat TextureNode MetalnessSampler
-cn $directx_mat TextureNode NormalsSampler
// override uv channels
-uva DiffuseSampler map1
-uva SpecularSampler map1
-uva RoughnessSampler map1
-uva MetalnessSampler map1
-uva NormalsSampler map1
// override sRGB
-sa DiffuseSampler true
-sa SpecularSampler true
-sa RoughnessSampler true
-sa MetalnessSampler true
-sa NormalsSampler false
// connect textures to simplygon channels
-sce $directx_mat Diffuse DiffuseSampler
-sce $directx_mat Specular SpecularSampler
-sce $directx_mat Roughness RoughnessSampler
-sce $directx_mat Metalness MetalnessSampler
-sce $directx_mat Normals NormalsSampler
-tmc $directx_mat Diffuse DiffuseSampler 1.0 1.0
-omc $directx_mat Diffuse DiffuseSampler 0.0 0.0
// export material channel templates to xml for later use
-exf $directx_mat Diffuse ($xmlExportPath + $directx_mat + "_Diffuse.xml")
-exf $directx_mat Specular ($xmlExportPath + $directx_mat + "_Specular.xml")
-exf $directx_mat Roughness ($xmlExportPath + $directx_mat + "_Roughness.xml")
-exf $directx_mat Metalness ($xmlExportPath + $directx_mat + "_Metalness.xml")
-exf $directx_mat Normals ($xmlExportPath + $directx_mat + "_Normals.xml");
// append material name to string (for later use)
$inputMaterialString += $directx_mat + "|";
}
// parse arguments
string $arguments = "";
for( $directx_material in $directxMaterials )
{
if(size ($directx_material) == 0)
continue;
$arguments += " -ixf \"" + $directx_material + "\" Diffuse \"" + ($xmlExportPath + $directx_material + "_Diffuse.xml\"");
$arguments += " -ixf \"" + $directx_material + "\" Specular \"" + ($xmlExportPath + $directx_material + "_Specular.xml\"");
$arguments += " -ixf \"" + $directx_material + "\" Roughness \"" + ($xmlExportPath + $directx_material + "_Roughness.xml\"");
$arguments += " -ixf \"" + $directx_material + "\" Metalness \"" + ($xmlExportPath + $directx_material + "_Metalness.xml\"");
$arguments += " -ixf \"" + $directx_material + "\" Normals \"" + ($xmlExportPath + $directx_material + "_Normals.xml\"");
}
// correct double slashes
$arguments = substituteAllString($arguments, "\\", "\\\\");
// launch Simplygon and fetch the result
string $processedMeshes[];
if($batchMode)
{
$processedMeshes = eval("Simplygon -b -dgm -tod $textureOutputDirectory -sf $simplygonSettingsFile -asm $inputMaterialString" + $arguments);
}
else
{
$processedMeshes = eval("Simplygon -ssh \"PBR\" -dgm -tod $textureOutputDirectory -sf $simplygonSettingsFile -asm $inputMaterialString" + $arguments);
}
// link cube map
string $cubeMapNode = `shadingNode -isColorManaged -asTexture file`;
setAttr -type "string" ( $cubeMapNode + ".fileTextureName" ) $cubeMapFilePath;
// fetch lod names
int $lodIndex = 0;
string $materialNameArray[];
string $SGNameArray[];
//$processedMeshes = `SimplygonQuery -gpm`;
for( $m in $processedMeshes )
{
// correct mesh names from Simplygon call
$m = substituteAllString($m, "|", "");
print ("\nMesh: " + $m + "\n");
// offset new mesh along x-axis
select -r $m;
$transAttr = $m+".translateX";
float $bbox[] = `exactWorldBoundingBox`;
float $moveOffset = ($lodIndex+1)*($bbox[3]-$bbox[0])*1.1;
setAttr -lock off $transAttr;
move -r $moveOffset 0 0;
string $material;
// fetch material name for mesh,
// start with checking for reused materials
$reuseMaterial = `SimplygonQuery -mrm $m`;
if(size ($reuseMaterial) == 0)
{
$material = `SimplygonQuery -gmm $m`;
}
else
{
print (" Reusing material: " + $reuseMaterial + "\n");
int $i = stringArrayFind( $reuseMaterial, 0, $materialNameArray );
if($i < 0)
{
print (" This mesh does not have material baking, ignoring!\n");
$lodIndex++;
continue;
}
// material was found in lod chain, continuing
string $nameOfMaterialNode = $SGNameArray[$i];
// assign material to selected mesh
select -r $m;
if(size ($nameOfMaterialNode) != 0)
{
sets -e -forceElement $nameOfMaterialNode;
}
// offset new mesh along x-axis
$lodIndex++;
continue;
}
print (" Material: " + $material + "\n");
// create DirectX11 shader and matching SG node
string $shader = `shadingNode -asShader dx11Shader`;
string $shaderSG = `sets -renderable true -noSurfaceShader true -empty -name ($shader + "SG")`;
stringArrayInsertAtIndex($lodIndex, $SGNameArray, $shaderSG);
stringArrayInsertAtIndex($lodIndex, $materialNameArray, $material);
// connect shader to shaderSG
connectAttr -f ( $shader + ".outColor" ) ( $shaderSG + ".surfaceShader" );
// user Simplygon PBR shader with default PBR teqhnique
setAttr -type "string" ($shader + ".shader") $pbrShaderFile;
setAttr -type "string" ($shader + ".technique") "PBR";
// connect cube map
connectAttr -force ($cubeMapNode + ".outColor") ($shader + ".CubemapSampler");
// fetch channel names for mesh material
$channels = `SimplygonQuery -gcm $material`;
for( $c in $channels )
{
// fetch texture path for channel
print (" Channel: " + $c + "\n");
$filepath = `SimplygonQuery -gtc $material $c`;
print (" Texture: " + $filepath + "\n");
// create texture nodes and link textures
string $fileNode = `shadingNode -isColorManaged -asTexture file`;
setAttr -type "string" ( $fileNode + ".fileTextureName" ) $filepath;
if($c == "Diffuse")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".DiffuseSampler");
}
else if($c == "Specular")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".SpecularSampler");
}
else if($c == "Roughness")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".RoughnessSampler");
}
else if($c == "Metalness")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".MetalnessSampler");
}
else if($c == "Normals")
{
connectAttr -force ($fileNode + ".outColor") ($shader + ".NormalsSampler");
// force colorspace, if possible
string $currentColorSpace = `getAttr ($fileNode + ".colorSpace")`;
if($currentColorSpace != "Raw")
{
string $availableColorSpaces[] = `colorManagementPrefs -q -inputSpaceNames`;
if ( stringArrayContains("Raw", $availableColorSpaces) )
{
setAttr ($fileNode+".ignoreColorSpaceFileRules") 1;
setAttr ($fileNode+".colorSpace") -type "string" "Raw";
}
}
}
}
// assign material to selected mesh
select -r $m;
sets -e -forceElement $shaderSG;
// offset new mesh along x-axis
$lodIndex++;
}
Texture blending
This script sets up two texture nodes that are blended using the InterpolateNode
.
select -all;
SimplygonShadingNetwork
-cn cgfx_mat VertexColorNode DiffuseColor1
-cn cgfx_mat VertexColorNode DiffuseColor2
-cn cgfx_mat VertexColorNode DiffuseColor3
-svn cgfx_mat DiffuseColor1 redColorSet // link red set
-svn cgfx_mat DiffuseColor2 greenColorSet // link green set
-svn cgfx_mat DiffuseColor3 blendColorSet // link blend set
-cn cgfx_mat InterpolateNode VertexColorInterpolator
-si cgfx_mat VertexColorInterpolator 0 DiffuseColor1 // red set -> 0
-si cgfx_mat VertexColorInterpolator 1 DiffuseColor2 // green set -> 1
-si cgfx_mat VertexColorInterpolator 2 DiffuseColor3 // blend set -> 2
-sce cgfx_mat Diffuse VertexColorInterpolator
-exf cgfx_mat Diffuse "D:\\diffuse.xml";
Simplygon -asm cgfx_mat
-ixf cgfx_mat Diffuse "D:\\diffuse.xml";
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
.
select -all;
SimplygonShadingNetwork
-cn cgfx_mat ColorNode DiffuseColor1
-cn cgfx_mat ColorNode DiffuseColor2
-cn cgfx_mat VertexColorNode BlendColor
-sd cgfx_mat DiffuseColor1 0 1.0 0.0 0.0 // base 1, red
-sd cgfx_mat DiffuseColor2 0 0.0 1.0 0.0 // base 2, green
-svn cgfx_mat BlendColor blendColorSet // link blend set
-cn cgfx_mat InterpolateNode VertexColorInterpolator
-si cgfx_mat VertexColorInterpolator 0 DiffuseColor1 // red -> 0
-si cgfx_mat VertexColorInterpolator 1 DiffuseColor2 // green -> 1
-si cgfx_mat VertexColorInterpolator 2 BlendColor // vertex color -> 3
-sce cgfx_mat Diffuse VertexColorInterpolator
-exf cgfx_mat Diffuse "D:\\diffuse.xml";
Simplygon -asm cgfx_mat
-ixf cgfx_mat Diffuse "D:\\diffuse.xml";