Skip to content
On this page

Mapping of CGFX (HLSL) materials

The following sections contain examples on how to map CGFX materials to Simplygon materials. It is recommended to read Shading network concepts before proceeding.

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.

MEL
// CGFX export / import script
string $rootPath = "C:\\SimplygonExample\\";

// pipeline settings
string $pipelineFilePath = $rootPath + "\\Pipelines\\ReductionWithBaking.json";

// 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\\";

// 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 slashes
$arguments = substituteAllString($arguments, "\\", "\\\\");

// launch Simplygon and fetch the result
string $processedMeshes[];

$processedMeshes = eval("Simplygon -dgm -tod $textureOutputDirectory -sf $pipelineFilePath -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
    $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;
        }
    }

    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 $pbrShaderFile $shader;
    setAttr -type "string" ($shader + ".technique") "PBR";

    // assign UV-set to the shader
    string $currentUVSet[] = `polyUVSet -q -currentUVSet $m`;
    setAttr ($shader + ".vertexAttributeSource") -type stringArray 5 "position" ("uv:" + $currentUVSet[0]) "normal" ("tangent:" + $currentUVSet[0]) ("binormal:" + $currentUVSet[0]);

    // 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++;
}