Skip to content
On this page

Mapping of DirectX (HLSL) materials

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

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.

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

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

// path to Simplygon PBR shader
string $pbrShaderFile = $rootPath + "\\Shader\\SimplygonPBRShaderDirectX.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 $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 $normalNodes[] = `listConnections -type "bump2d" ($directx_mat + ".NormalTexture")`;
    string $normalMaps[] = `listConnections -type "file" ($directx_mat + ".NormalTexture")`;

    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");
        }
    }

    SimplygonShadingNetwork
    // create texture nodes
    -cn $directx_mat TextureNode DiffuseTexture
    -cn $directx_mat TextureNode SpecularTexture
    -cn $directx_mat TextureNode RoughnessTexture
    -cn $directx_mat TextureNode MetalnessTexture
    -cn $directx_mat TextureNode NormalTexture

    // override uv channels
    -uva DiffuseTexture map1
    -uva SpecularTexture map1
    -uva RoughnessTexture map1
    -uva MetalnessTexture map1
    -uva NormalTexture map1

    // override sRGB
    -sa DiffuseTexture true
    -sa SpecularTexture true
    -sa RoughnessTexture true
    -sa MetalnessTexture true
    -sa NormalTexture false

    // connect textures to simplygon channels
    -sce $directx_mat Diffuse DiffuseTexture
    -sce $directx_mat Specular SpecularTexture
    -sce $directx_mat Roughness RoughnessTexture
    -sce $directx_mat Metalness MetalnessTexture
    -sce $directx_mat Normals NormalTexture

    -tmc $directx_mat Diffuse DiffuseTexture 1.0 1.0
    -omc $directx_mat Diffuse DiffuseTexture 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;

    print ("\nDirectX11 Shader " + $directx_material + " has the following maps:\n");

    int $hasDiffuseTexture = false;
    string $diffuseMaps[] = `listConnections -type "file" ($directx_material + ".DiffuseTexture")`;
    for ($dMap in $diffuseMaps)
    {
        string $filename = `getAttr($dMap + ".fileTextureName")`;
        $hasDiffuseTexture = size($filename) > 0;
        print (" Diffuse map (" + $dMap + "): "  + $filename + " (" + $hasDiffuseTexture + ")\n");
    }

    int $hasSpecularTexture = false;
    string $specularMaps[] = `listConnections -type "file" ($directx_material + ".SpecularTexture")`;
    for ($sMap in $specularMaps)
    {
        string $filename = `getAttr($dMap + ".fileTextureName")`;
        $hasSpecularTexture = size($filename) > 0;
        print (" Specular map (" + $dMap + "): "  + $filename + " (" + $hasSpecularTexture + ")\n");
    }

    int $hasRoughnessTexture = false;
    string $roughnessMaps[] = `listConnections -type "file" ($directx_material + ".RoughnessTexture")`;
    for ($rMap in $roughnessMaps)
    {
        string $filename = `getAttr($rMap + ".fileTextureName")`;
        $hasRoughnessTexture = size($filename) > 0;
        print (" Roughness map (" + $rMap + "): "  + $filename + " (" + $hasRoughnessTexture + ")\n");
    }

    int $hasMetalnessTexture = false;
    string $metalnessMaps[] = `listConnections -type "file" ($directx_material + ".MetalnessTexture")`;
    for ($mMap in $metalnessMaps)
    {
        string $filename = `getAttr($mMap + ".fileTextureName")`;
        $hasMetalnessTexture = size($filename) > 0;
        print (" Metalness map (" + $mMap + "): "  + $filename + " (" + $hasMetalnessTexture + ")\n");
    }

    int $hasNormalTexture = false;
    string $normalNodes[] = `listConnections -type "bump2d" ($directx_material + ".NormalTexture")`;
    string $normalMaps[] = `listConnections -type "file" ($directx_material + ".NormalTexture")`;

    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")`;
        $hasNormalTexture = size($filename) > 0;
        print (" Normal map (" + $nMap + "): "  + $filename + " (" + $hasNormalTexture + ")\n");
    }

    if($hasDiffuseTexture == true)
    {
        $arguments += " -ixf \"" + $directx_material + "\" Diffuse \"" + ($xmlExportPath + $directx_material + "_Diffuse.xml\"");
    }
    if($hasSpecularTexture == true)
    {
        $arguments += " -ixf \"" + $directx_material + "\" Specular \"" + ($xmlExportPath + $directx_material + "_Specular.xml\"");
    }
    if($hasRoughnessTexture == true)
    {
        $arguments += " -ixf \"" + $directx_material + "\" Roughness \"" + ($xmlExportPath + $directx_material + "_Roughness.xml\"");
    }
    if($hasMetalnessTexture == true)
    {
        $arguments += " -ixf \"" + $directx_material + "\" Metalness \"" + ($xmlExportPath + $directx_material + "_Metalness.xml\"");
    }
    if($hasNormalTexture == true)
    {
        $arguments += " -ixf \"" + $directx_material + "\" Normals \"" + ($xmlExportPath + $directx_material + "_Normals.xml\"");
    }
}

// correct slashes
$arguments = substituteAllString($arguments, "\\", "\\\\");

// launch Simplygon and fetch the result
string $processedMeshes[] = eval("Simplygon -sf $pipelineFilePath -dgm -tod $textureOutputDirectory -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;
        }
    }

    // if not reuse, create new material
    print ("  Creating 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";

    // assign UV-set to the shader
    string $currentUVSet[] = `polyUVSet -q -currentUVSet $m`;
    setAttr -type "string" ($shader + ".TexCoord0_Source") ("uv:" + $currentUVSet[0]);
    setAttr -type "string" ($shader + ".Tangent0_Source") ("tangent:" + $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 + ".DiffuseTexture");
            }
            else if($c == "Specular")
            {
                connectAttr -force ($fileNode + ".outColor") ($shader + ".SpecularTexture");
            }
            else if($c == "Roughness")
            {
                connectAttr -force ($fileNode + ".outColor") ($shader + ".RoughnessTexture");
            }
            else if($c == "Metalness")
            {
                connectAttr -force ($fileNode + ".outColor") ($shader + ".MetalnessTexture");
            }
            else if($c == "Normals")
            {
                string $bNode =`shadingNode -asUtility bump2d`;
                setAttr ($bNode + ".bumpInterp") 1;
                connectAttr -f ($bNode + ".outNormal") ($shader + ".NormalTexture");
                connectAttr -f ($fileNode + ".outAlpha") ($bNode + ".bumpValue");

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