Mapping of ShaderFX / Stingray PBS materials
The following sections contain examples on how to map Stingray PBS materials to Simplygon materials. It is recommended to read Shading network concepts before proceeding.
Stingray PBS
This script loops through all Stingray PBS materials 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.
MEL
// Stingray PBS export / import script
string $rootPath = "C:\\SimplygonExample\\";
// pipeline settings
string $pipelineFilePath = $rootPath + "\\Pipelines\\Aggregation_With_Baking_Stingray.json";
// "" = default, "d:\\TextureOutputFolder\\" = custom
string $textureOutputDirectory = $rootPath + "\\OutputTextures\\";
// drive to store temporary xml files (c might be protected)
string $xmlExportPath = $rootPath + "\\XMLTempFolder\\";
// populate material list with Stingray PBS materials
string $stingrayMaterials[] = `ls -type StingrayPBS`;
// create Simplygon shading networks for each Stingray material
string $inputMaterialString = "";
for($stingray_mat in $stingrayMaterials)
{
if(size ($stingray_mat) == 0)
continue;
print ("\nStingray shader:\n Name: " + $stingray_mat + "\n");
// if there is a normal map, make sure it is in Raw color space
int $TEX_normal_map_enabled = `getAttr($stingray_mat + ".use_normal_map")`;
if($TEX_normal_map_enabled)
{
string $normalNodes[] = `listConnections -type "bump2d" ($stingray_mat + ".TEX_normal_map")`;
string $normalMaps[] = `listConnections -type "file" ($stingray_mat + ".TEX_normal_map")`;
if(size($normalNodes) > 0)
{
string $furtherNormalMaps[] = `listConnections -type "file" $normalNodes[0]`;
int $size = size($furtherNormalMaps);
appendStringArray($normalMaps, $furtherNormalMaps, $size);
}
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");
}
}
}
int $bakeShaderProperties = true;
float $uvOffsetX = 0.0;
float $uvOffsetY = 0.0;
float $uvScaleX = 1.0;
float $uvScaleY = 1.0;
float $baseColor[] = {0.5, 0.5, 0.5};
float $baseColorDisplay[] = {0.5, 0.5, 0.5};
float $metallicFactor = 0.0;
float $roughnessFactor = 1.0;
float $emissiveColor[] = {0.0, 0.0, 0.0};
float $emissiveColorDisplay[] = {0.0, 0.0, 0.0};
float $emissiveIntensity = 0.0;
// read Stingray PBS shader properties
if($bakeShaderProperties)
{
$uvOffsetX = `getAttr($stingray_mat + ".uv_offsetX")`;
$uvOffsetY = `getAttr($stingray_mat + ".uv_offsetY")`;
print(" Offset: " + $uvOffsetX + ", " + $uvOffsetY + "\n");
$uvScaleX = `getAttr($stingray_mat + ".uv_scaleX")`;
$uvScaleY = `getAttr($stingray_mat + ".uv_scaleY")`;
print(" Scale: " + $uvScaleX + ", " + $uvScaleY + "\n");
$baseColor = `getAttr($stingray_mat + ".base_color")`;
print(" BaseColor: " + $baseColor[0] + ", " + $baseColor[1] + ", " + $baseColor[2] + "\n");
$baseColorDisplay = `colorManagementConvert -toDisplaySpace $baseColor[0] $baseColor[1] $baseColor[2]`;
print(" BaseColor (display): " + $baseColorDisplay[0] + ", " + $baseColorDisplay[1] + ", " + $baseColorDisplay[2] + "\n");
$metallicFactor = `getAttr($stingray_mat + ".metallic")`;
print(" MetallicFactor: " + $metallicFactor + "\n");
$roughnessFactor = `getAttr($stingray_mat + ".roughness")`;
print(" RoughnessFactor: " + $roughnessFactor + "\n");
$emissiveColor = `getAttr($stingray_mat + ".emissive")`;
print(" EmissiveColor: " + $emissiveColor[0] + ", " + $emissiveColor[1] + ", " + $emissiveColor[2] + "\n");
$emissiveColorDisplay = `colorManagementConvert -toDisplaySpace $emissiveColor[0] $emissiveColor[1] $emissiveColor[2]`;
print(" EmissiveColor (display): " + $emissiveColorDisplay[0] + ", " + $emissiveColorDisplay[1] + ", " + $emissiveColorDisplay[2] + "\n");
$emissiveIntensity = `getAttr($stingray_mat + ".emissive_intensity")`;
print(" EmissiveIntensity: " + $emissiveIntensity + "\n");
}
// create base color texture node if there is a map, otherwise a color node
int $color_map_enabled = `getAttr($stingray_mat + ".use_color_map")`;
if($color_map_enabled)
{
SimplygonShadingNetwork
// create color texture node, point it to TEX_color_map
-cn $stingray_mat TextureNode TEX_color_map
// override uv channel
-uva TEX_color_map map1
// override sRGB
-sa TEX_color_map false
// connect texture node to simplygon material channel
-sce $stingray_mat color TEX_color_map
// override tiling and offset
-tmc $stingray_mat color TEX_color_map ($uvScaleX) ($uvScaleY)
-omc $stingray_mat color TEX_color_map $uvOffsetX $uvOffsetY
// export material channel templates to xml for later use
-exf $stingray_mat color ($xmlExportPath + $stingray_mat + "_color.xml");
}
else
{
SimplygonShadingNetwork
// create color node
-cn $stingray_mat ColorNode baseColor
// set the color to baseColorDisplay
-sd $stingray_mat baseColor 0 $baseColorDisplay[0] $baseColorDisplay[1] $baseColorDisplay[2]
// connect color node to simplygon material channel
-sce $stingray_mat color baseColor
// export material channel templates to xml for later use
-exf $stingray_mat color ($xmlExportPath + $stingray_mat + "_color.xml");
}
// create metallic texture node if there is a map, otherwise a color node
int $metallic_map_enabled = `getAttr($stingray_mat + ".use_metallic_map")`;
if($metallic_map_enabled)
{
SimplygonShadingNetwork
// create color texture node, point it to TEX_metallic_map
-cn $stingray_mat TextureNode TEX_metallic_map
// override uv channel
-uva TEX_metallic_map map1
// override sRGB
-sa TEX_metallic_map false
// connect texture node to simplygon material channel
-sce $stingray_mat metallic TEX_metallic_map
// override tiling and offset
-tmc $stingray_mat metallic TEX_metallic_map ($uvScaleX) ($uvScaleY)
-omc $stingray_mat metallic TEX_metallic_map $uvOffsetX $uvOffsetY
// export material channel templates to xml for later use
-exf $stingray_mat metallic ($xmlExportPath + $stingray_mat + "_metallic.xml");
}
else
{
SimplygonShadingNetwork
// create metallic color node
-cn $stingray_mat ColorNode metallicColor
-sd $stingray_mat metallicColor 0 $metallicFactor $metallicFactor $metallicFactor
// connect color node to simplygon material channel
-sce $stingray_mat metallic metallicColor
// export material channel templates to xml for later use
-exf $stingray_mat metallic ($xmlExportPath + $stingray_mat + "_metallic.xml");
}
// create roughness texture node if there is a map, otherwise a color node
int $roughness_map_enabled = `getAttr($stingray_mat + ".use_roughness_map")`;
if($roughness_map_enabled)
{
SimplygonShadingNetwork
// create color texture node, point it to TEX_roughness_map
-cn $stingray_mat TextureNode TEX_roughness_map
// override uv channel
-uva TEX_roughness_map map1
// override sRGB
-sa TEX_roughness_map false
// connect texture node to simplygon material channel
-sce $stingray_mat roughness TEX_roughness_map
// override tiling and offset
-tmc $stingray_mat roughness TEX_roughness_map ($uvScaleX) ($uvScaleY)
-omc $stingray_mat roughness TEX_roughness_map $uvOffsetX $uvOffsetY
// export material channel templates to xml for later use
-exf $stingray_mat roughness ($xmlExportPath + $stingray_mat + "_roughness.xml");
}
else
{
SimplygonShadingNetwork
// create roughness color node
-cn $stingray_mat ColorNode roughnessColor
-sd $stingray_mat roughnessColor 0 $roughnessFactor $roughnessFactor $roughnessFactor
// connect texture node to simplygon material channel
-sce $stingray_mat roughness roughnessColor
// export material channel templates to xml for later use
-exf $stingray_mat roughness ($xmlExportPath + $stingray_mat + "_roughness.xml");
}
// create emissive texture node if there is a map, otherwise a color node
int $emissive_map_enabled = `getAttr($stingray_mat + ".use_emissive_map")`;
if($emissive_map_enabled)
{
SimplygonShadingNetwork
// create color texture node, point it to TEX_emissive_map
-cn $stingray_mat TextureNode TEX_emissive_map
// override uv channel
-uva TEX_emissive_map map1
// override sRGB
-sa TEX_emissive_map false
// connect texture node to simplygon material channel
-sce $stingray_mat emissive TEX_emissive_map
// override tiling and offset
-tmc $stingray_mat emissive TEX_emissive_map ($uvScaleX) ($uvScaleY)
-omc $stingray_mat emissive TEX_emissive_map $uvOffsetX $uvOffsetY
// export material channel templates to xml for later use
-exf $stingray_mat emissive ($xmlExportPath + $stingray_mat + "_emissive.xml");
}
else
{
SimplygonShadingNetwork
// create emissive color and multiply nodes
-cn $stingray_mat ColorNode emissiveColor
-cn $stingray_mat ColorNode emissiveIntensity
-cn $stingray_mat MultiplyNode emissiveMultiply
// set color values
-sd $stingray_mat emissiveColor 0 $emissiveColorDisplay[0] $emissiveColorDisplay[1] $emissiveColorDisplay[2]
-sd $stingray_mat emissiveIntensity 0 $emissiveIntensity $emissiveIntensity $emissiveIntensity
// multiply emissive color with intensity
-si $stingray_mat emissiveMultiply 0 emissiveColor
-si $stingray_mat emissiveMultiply 1 emissiveIntensity
// connect multiply node to simplygon material channel
-sce $stingray_mat emissive emissiveMultiply
// export material channel templates to xml for later use
-exf $stingray_mat emissive ($xmlExportPath + $stingray_mat + "_emissive.xml");
}
SimplygonShadingNetwork
// create color texture nodes, point them to
// TEX_normal_map and TEX_normal_map
-cn $stingray_mat TextureNode TEX_normal_map
-cn $stingray_mat TextureNode TEX_ao_map
// override uv channel
-uva TEX_normal_map map1
-uva TEX_ao_map map1
// override sRGB
-sa TEX_normal_map false
-sa TEX_ao_map false
// connect texture nodes to simplygon material channels
-sce $stingray_mat normal TEX_normal_map
-sce $stingray_mat ao TEX_ao_map
// override tiling and offset
-tmc $stingray_mat normal TEX_normal_map ($uvScaleX) ($uvScaleY)
-omc $stingray_mat normal TEX_normal_map $uvOffsetX $uvOffsetY
-tmc $stingray_mat ao TEX_ao_map ($uvScaleX) ($uvScaleY)
-omc $stingray_mat ao TEX_ao_map $uvOffsetX $uvOffsetY
// export material channel templates to xml for later use
-exf $stingray_mat normal ($xmlExportPath + $stingray_mat + "_normal.xml")
-exf $stingray_mat ao ($xmlExportPath + $stingray_mat + "_ao.xml");
// append material name to string (for later use)
$inputMaterialString += $stingray_mat + "|";
}
// parse arguments
string $arguments = "";
for($stingray_material in $stingrayMaterials)
{
if(size ($stingray_material) == 0)
continue;
print ("\nStingray shader " + $stingray_material + " has the following maps:\n");
// color map detection (can be skipped if channel always exist)
int $hasTEX_color_map = false;
int $TEX_color_map_enabled = `getAttr($stingray_material + ".use_color_map")`;
if($TEX_color_map_enabled)
{
string $colorMaps[] = `listConnections -type "file" ($stingray_material + ".TEX_color_map")`;
for ($dMap in $colorMaps)
{
string $filename = `getAttr($dMap + ".fileTextureName")`;
$hasTEX_color_map = size($filename) > 0;
print (" Color map (" + $dMap + "): " + $filename + " (" + $hasTEX_color_map + ")\n");
}
}
// metallic map detection (can be skipped if channel always exist)
int $hasTEX_metallic_map = false;
int $TEX_metallic_map_enabled = `getAttr($stingray_material + ".use_metallic_map")`;
if($TEX_metallic_map_enabled)
{
string $metallicMaps[] = `listConnections -type "file" ($stingray_material + ".TEX_metallic_map")`;
for ($dMap in $metallicMaps)
{
string $filename = `getAttr($dMap + ".fileTextureName")`;
$hasTEX_metallic_map = size($filename) > 0;
print (" Metallic map (" + $dMap + "): " + $filename + " (" + $hasTEX_metallic_map + ")\n");
}
}
// roughness map detection (can be skipped if channel always exist)
int $hasTEX_roughness_map = false;
int $TEX_roughness_map_enabled = `getAttr($stingray_material + ".use_roughness_map")`;
if($TEX_roughness_map_enabled)
{
string $roughnessMaps[] = `listConnections -type "file" ($stingray_material + ".TEX_roughness_map")`;
for ($dMap in $roughnessMaps)
{
string $filename = `getAttr($dMap + ".fileTextureName")`;
$hasTEX_roughness_map = size($filename) > 0;
print (" Roughness map (" + $dMap + "): " + $filename + " (" + $hasTEX_roughness_map + ")\n");
}
}
// emissive map detection (can be skipped if channel always exist)
int $hasTEX_emissive_map = false;
int $TEX_emissive_map_enabled = `getAttr($stingray_material + ".use_emissive_map")`;
if($TEX_emissive_map_enabled)
{
string $emissiveMaps[] = `listConnections -type "file" ($stingray_material + ".TEX_emissive_map")`;
for ($dMap in $emissiveMaps)
{
string $filename = `getAttr($dMap + ".fileTextureName")`;
$hasTEX_emissive_map = size($filename) > 0;
print (" Emissive map (" + $dMap + "): " + $filename + " (" + $hasTEX_emissive_map + ")\n");
}
}
// ao map detection (can be skipped if channel always exist)
int $hasTEX_ao_map = false;
int $TEX_ao_map_enabled = `getAttr($stingray_material + ".use_ao_map")`;
if($TEX_ao_map_enabled)
{
string $aoMaps[] = `listConnections -type "file" ($stingray_material + ".TEX_ao_map")`;
for ($dMap in $aoMaps)
{
string $filename = `getAttr($dMap + ".fileTextureName")`;
$hasTEX_ao_map = size($filename) > 0;
print (" Ao map (" + $dMap + "): " + $filename + " (" + $hasTEX_ao_map + ")\n");
}
}
// normal map detection (can be skipped if channel always exist)
int $hasTEX_normal_map = false;
int $TEX_normal_map_enabled = `getAttr($stingray_material + ".use_normal_map")`;
if($TEX_normal_map_enabled)
{
string $normalNodes[] = `listConnections -type "bump2d" ($stingray_material + ".TEX_normal_map")`;
string $normalMaps[] = `listConnections -type "file" ($stingray_material + ".TEX_normal_map")`;
if(size($normalNodes) > 0)
{
string $furtherNormalMaps[] = `listConnections -type "file" $normalNodes[0]`;
int $size = size($furtherNormalMaps);
appendStringArray($normalMaps, $furtherNormalMaps, $size);
}
for ($nMap in $normalMaps)
{
string $filename = `getAttr($nMap + ".fileTextureName")`;
$hasTEX_normal_map = size($filename) > 0;
print (" Normal map (" + $nMap + "): " + $filename + " (" + $hasTEX_normal_map + ")\n");
}
}
// if a material channel always exists, with either a color node
// or texture node (with connected texture), there is no need to
// skip import. Baking of texture nodes without textures will fail!
//if($hasTEX_color_map == true)
{
$arguments += " -ixf \"" + $stingray_material + "\" color \"" + ($xmlExportPath + $stingray_material + "_color.xml\"");
}
//if($hasTEX_metallic_map == true)
{
$arguments += " -ixf \"" + $stingray_material + "\" metallic \"" + ($xmlExportPath + $stingray_material + "_metallic.xml\"");
}
//if($hasTEX_roughness_map == true)
{
$arguments += " -ixf \"" + $stingray_material + "\" roughness \"" + ($xmlExportPath + $stingray_material + "_roughness.xml\"");
}
//if($hasTEX_emissive_map == true)
{
$arguments += " -ixf \"" + $stingray_material + "\" emissive \"" + ($xmlExportPath + $stingray_material + "_emissive.xml\"");
}
if($hasTEX_ao_map == true)
{
$arguments += " -ixf \"" + $stingray_material + "\" ao \"" + ($xmlExportPath + $stingray_material + "_ao.xml\"");
}
if($hasTEX_normal_map == true)
{
$arguments += " -ixf \"" + $stingray_material + "\" normal \"" + ($xmlExportPath + $stingray_material + "_normal.xml\"");
}
}
// correct slashes
$arguments = substituteAllString($arguments, "\\", "\\\\");
// launch Simplygon and fetch the result
string $processedMeshes[] = eval("Simplygon -rsg -sf $pipelineFilePath -dgm -tod $textureOutputDirectory -asm $inputMaterialString" + $arguments);
// fetch lod names
int $lodIndex = 0;
string $materialNameArray[];
string $SGNameArray[];
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
$reuseMaterials = `SimplygonQuery -rms $m`;
if(size ($reuseMaterials) == 0)
{
print (" Not reusing material.\n");
$material = `SimplygonQuery -gmm $m`;
}
else
{
int $currentMaterialIndex = 0;
int $isReusingMaterial = false;
for($reuseMaterial in $reuseMaterials)
{
print (" Checking material: " + $reuseMaterial + "\n");
int $i = stringArrayFind( $reuseMaterial, 0, $materialNameArray );
if($i < 0)
{
print (" Reusing original material!\n");
select -cl;
$materialIds = `SimplygonQuery -gmi $m`;
//print $materialIds;
int $tid = 0;
for($mid in $materialIds)
{
if($mid == $currentMaterialIndex)
{
//print (" Selecting triangle " + $tid + " for material " + $mid + "\n");
select -tgl ($m + ".f[" + $tid + "]");
}
$tid++;
}
print (" Assigning material " + $reuseMaterial + " to selected triangles for mesh " + $m + "\n");
sets -e -forceElement $reuseMaterial;
select -cl;
$lodIndex++;
$currentMaterialIndex++;
$isReusingMaterial = true;
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++;
$currentMaterialIndex++;
$isReusingMaterial = true;
continue;
}
// if reuse, exit here
if($isReusingMaterial == true)
{
continue;
}
}
// if not reuse, create new (Stingray PBS) material
print (" Creating material: " + $material + "\n");
// create material and matching SG node
string $shader = `shadingNode -asShader StingrayPBS`;
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" );
shaderfx -sfxnode $shader -edit_string 6 "shaderresource" "core/stingray_renderer/shader_import/standard";
// assign UV-set to the shader
string $currentUVSet[] = `polyUVSet -q -currentUVSet $m`;
// 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 == "color")
{
setAttr ($shader + ".use_color_map") 1;
connectAttr -force ($fileNode + ".outColor") ($shader + ".TEX_color_map");
}
else if($c == "metallic")
{
setAttr ($shader + ".use_metallic_map") 1;
setAttr ($shader + ".metallic") 1.0;
connectAttr -force ($fileNode + ".outColor") ($shader + ".TEX_metallic_map");
}
else if($c == "roughness")
{
setAttr ($shader + ".use_roughness_map") 1;
setAttr ($shader + ".roughness") 1.0;
connectAttr -force ($fileNode + ".outColor") ($shader + ".TEX_roughness_map");
}
else if($c == "emissive")
{
setAttr ($shader + ".use_emissive_map") 1;
setAttr ($shader + ".emissive_intensity") 1.0;
connectAttr -force ($fileNode + ".outColor") ($shader + ".TEX_emissive_map");
}
else if($c == "ao")
{
setAttr ($shader + ".use_ao_map") 1;
connectAttr -force ($fileNode + ".outColor") ($shader + ".TEX_ao_map");
}
else if($c == "normal")
{
setAttr ($shader + ".use_normal_map") 1;
connectAttr -force ($fileNode + ".outColor") ($shader + ".TEX_normal_map");
// 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++;
}