Skip to content

Commit

Permalink
Shaders exports should be bound to a material #2047 (#2097)
Browse files Browse the repository at this point in the history
  • Loading branch information
sebastienblor authored Sep 16, 2024
1 parent 3dfb31c commit 46ea6f2
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 21 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
- [usd#2082](https://github.com/Autodesk/arnold-usd/issues/2082) - Support arnold cameras in hydra
- [usd#2084](https://github.com/Autodesk/arnold-usd/issues/2084) - Imagers should be applied to all drivers
- [usd#2086](https://github.com/Autodesk/arnold-usd/issues/2086) - Compute FOV in the procedural and hydra in a similar manner
- [usd#2047](https://github.com/Autodesk/arnold-usd/issues/2047) - Shaders exports should be bound to a material

### Bug fixes
- [usd#1961](https://github.com/Autodesk/arnold-usd/issues/1961) - Random curves width in Hydra when radius primvars are authored
Expand Down
3 changes: 3 additions & 0 deletions libs/common/constant_strings.h
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,9 @@ ASTR(log_flags_console);
ASTR(log_flags_file);
ASTR(mask);
ASTR(MATERIALX_NODE_DEFINITIONS);
ASTR(material_surface);
ASTR(material_displacement);
ASTR(material_volume);
ASTR(matrix);
ASTR(matrix_multiply_vector);
ASTR(matte);
Expand Down
45 changes: 24 additions & 21 deletions libs/translator/writer/prim_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,6 +555,29 @@ std::string UsdArnoldPrimWriter::GetArnoldNodeName(const AtNode* node, const Usd
name = ss.str();
}

_SanitizePrimName(name);

// If we need to strip a hierarchy from the arnold node's name,
// we need to find if this node name starts with the expected hierarchy
// and do it before prefixing it with the scope
const std::string &stripHierarchy = writer.GetStripHierarchy();
if (!stripHierarchy.empty()) {
if (TfStringStartsWith(name, stripHierarchy)) {
name = name.substr(stripHierarchy.size());
}
}
name = writer.GetScope() + name;

const AtNodeEntry* nodeEntry = AiNodeGetNodeEntry(node);
// Drivers should always be under the scope /Render/Products
if (AiNodeEntryGetType(nodeEntry) == AI_NODE_DRIVER) {
name = writer.GetRenderProductsScope().GetString() + name;
}

return name;
}
void UsdArnoldPrimWriter::_SanitizePrimName(std::string &name)
{
std::locale loc;

// We need to determine which parameters must be converted to underscores
Expand All @@ -578,27 +601,7 @@ std::string UsdArnoldPrimWriter::GetArnoldNodeName(const AtNode* node, const Usd
i++;
}
}

// If we need to strip a hierarchy from the arnold node's name,
// we need to find if this node name starts with the expected hierarchy
// and do it before prefixing it with the scope
const std::string &stripHierarchy = writer.GetStripHierarchy();
if (!stripHierarchy.empty()) {
if (TfStringStartsWith(name, stripHierarchy)) {
name = name.substr(stripHierarchy.size());
}
}
name = writer.GetScope() + name;

const AtNodeEntry* nodeEntry = AiNodeGetNodeEntry(node);
// Drivers should always be under the scope /Render/Products
if (AiNodeEntryGetType(nodeEntry) == AI_NODE_DRIVER) {
name = writer.GetRenderProductsScope().GetString() + name;
}

return name;
}

// Ensure a connected node is properly translated, handle the output attributes,
// and return its name
static inline std::string GetConnectedNode(UsdArnoldWriter& writer, AtNode* target, int outComp = -1)
Expand Down Expand Up @@ -1215,7 +1218,7 @@ static void processMaterialBinding(AtNode* shader, AtNode* displacement, UsdPrim
if (materialPath != "/")
writer.SetStripHierarchy(materialPath);

TfToken arnoldContext("arnold");
const TfToken arnoldContext("arnold");
if (shader) {
// Write the surface shader under the material's scope.
// Here we only want to consider the last name in the prim
Expand Down
3 changes: 3 additions & 0 deletions libs/translator/writer/prim_writer.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ class UsdArnoldPrimWriter {
void _WriteMatrix(UsdGeomXformable &xform, const AtNode *node, UsdArnoldWriter &writer);
void _WriteMaterialBinding(
const AtNode *node, UsdPrim &prim, UsdArnoldWriter &writer, AtArray *shidxsArray = nullptr);

static void _SanitizePrimName(std::string &name);

std::unordered_set<std::string> _exportedAttrs; // list of arnold attributes that were exported

float _motionStart;
Expand Down
36 changes: 36 additions & 0 deletions libs/translator/writer/write_shader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -82,4 +82,40 @@ void UsdArnoldWriteShader::Write(const AtNode *node, UsdArnoldWriter &writer)
}
// Ensure all shaders have an output attribute
prim.CreateAttribute(str::t_outputs_out, SdfValueTypeNames->Token, false);

if (!(writer.GetMask() & AI_NODE_SHAPE)) {
// If shapes are not exported and a material is specified for this shader, let's create it
AtString materialName;
bool isDisplacement = false;
// material_surface / material_displacement / material_volume are user data
// authored by the arnold plugins when a shader library is exported, so that
// materials can be restored at import. We can use this to create the
// USD material primitives #2047
if (AiNodeLookUpUserParameter(node, str::material_surface)) {
materialName = AiNodeGetStr(node, str::material_surface);
} else if (AiNodeLookUpUserParameter(node, str::material_displacement)) {
materialName = AiNodeGetStr(node, str::material_displacement);
isDisplacement = true;
} else if (AiNodeLookUpUserParameter(node, str::material_volume)) {
// Note that volume assignments are treated the same way as
// surface shader assignments in our usd support
materialName = AiNodeGetStr(node, str::material_volume);
}

if (!materialName.empty()) {
std::string matName(materialName.c_str());
_SanitizePrimName(matName);
std::string mtlScope = writer.GetScope() + writer.GetMtlScope();
writer.CreateScopeHierarchy(SdfPath(mtlScope));
matName = mtlScope + matName;
UsdShadeMaterial mat = UsdShadeMaterial::Define(writer.GetUsdStage(), SdfPath(matName));
const TfToken arnoldContext("arnold");
std::string shaderOutput = prim.GetPath().GetString() + std::string(".outputs:out");
UsdShadeOutput matOutput = (isDisplacement) ?
mat.CreateDisplacementOutput(arnoldContext) :
mat.CreateSurfaceOutput(arnoldContext);

matOutput.ConnectToSource(SdfPath(shaderOutput));
}
}
}
7 changes: 7 additions & 0 deletions testsuite/test_2047/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Shaders exports should be bound to a material

See #2047

author: sebastien ortega

PARAMS: {'resaved':'usda'}
127 changes: 127 additions & 0 deletions testsuite/test_2047/data/shaders.ass
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
### exported: Thu Sep 12 08:53:34 2024
### from: Arnold 7.3.5.0 [6795639c] windows x86_64 clang-15.0.7 oiio-2.4.1 osl-1.13.0 vdb-11.0.0 adlsdk-8.0.7.50 clmhub-3.1.1.43 rlm-14.2.5 optix-8.0.0 2024/09/01 22:15:30
### host app: MtoA 5.4.5 41e56b38 (master) Maya 2024
### render_layer: defaultRenderLayer
### user: blaines
### scene: C:/arnold/mtoa/testsuite/test_0086/data/test.ma
### meters_per_unit: 0.010000



standard_surface
{
name aiStandard5
base 0.699999988
base_color 0 1 0
specular 0
specular_roughness 0.4669047
specular_IOR 10
subsurface_radius 0.100000001 0.100000001 0.100000001
coat_roughness 0
coat_IOR 10
declare material_surface constant STRING
material_surface "aiStandard5SG"
}

range
{
name displacementShader6
input checker3/cc.a
output_min -1
output_max 0
contrast_pivot 0
declare material_displacement constant STRING
material_displacement "aiStandard5SG"
}

checkerboard
{
name checker3
color1 0 0 0
color2 1 1 1
u_frequency 16
v_frequency 16
u_offset 0
v_offset 0
filter_strength 0
uvset ""
}

color_correct
{
name checker3/cc
input checker3
alpha_is_luminance on
}

standard
{
name aiStandard3
Kd_color 1 0 0
declare material_surface constant STRING
material_surface "aiStandard3SG"
}

range
{
name displacementShader4
input checker1
output_min 0
output_max 1
contrast_pivot 0
declare material_displacement constant STRING
material_displacement "aiStandard3SG"
}

checkerboard
{
name checker1
color1 0 0 0
color2 1 1 1
u_frequency 4
v_frequency 4
u_offset 0
v_offset 0
filter_strength 0
uvset ""
}

standard
{
name aiStandard4
Kd_color 0 0 1
declare material_surface constant STRING
material_surface "aiStandard4SG"
}

range
{
name displacementShader5
input checker2/cc.a
output_min 0
output_max 1
contrast_pivot 0
declare material_displacement constant STRING
material_displacement "aiStandard4SG"
}

checkerboard
{
name checker2
color1 0 0 0
color2 3 3 3
u_frequency 8
v_frequency 8
u_offset 0
v_offset 0
filter_strength 0
uvset ""
}

color_correct
{
name checker2/cc
input checker2
alpha_is_luminance on
}

46 changes: 46 additions & 0 deletions testsuite/test_2047/data/test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import sys

sys.path.append(os.path.join(os.environ['ARNOLD_PATH'], 'python'))
from arnold import *

AiBegin()
universe = AiUniverse()

usdScene = 'shaders_resaved.usda'
AiSceneLoad(universe, 'shaders.ass', None)
params = AiParamValueMap()
AiParamValueMapSetInt(params, 'mask', AI_NODE_SHADER)
AiSceneWrite(universe, usdScene, params)
AiEnd()

expectedLines = ['def Scope "mtl"',
'def Material "aiStandard5SG"',
'token outputs:arnold:displacement.connect = </displacementShader6.outputs:out>',
'token outputs:arnold:surface.connect = </aiStandard5.outputs:out>',
'def Material "aiStandard3SG"',
'token outputs:arnold:displacement.connect = </displacementShader4.outputs:out>',
'token outputs:arnold:surface.connect = </aiStandard3.outputs:out>',
'def Material "aiStandard4SG"',
'token outputs:arnold:displacement.connect = </displacementShader5.outputs:out>',
'token outputs:arnold:surface.connect = </aiStandard4.outputs:out>']

expectedLinesCount = len(expectedLines)
currentLine = 0

success = False
with open(usdScene, 'r') as f:
lines = f.readlines()
for line in lines:
if expectedLines[currentLine] in line:
currentLine = currentLine + 1
if currentLine == expectedLinesCount:
success = True
break

if not success:
print('Line not found in the output usd file:')
print(expectedLines[currentLine])
sys.exit(-1)

print('SUCCESS')

0 comments on commit 46ea6f2

Please sign in to comment.