Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build_scripts/build_usd.py
Original file line number Diff line number Diff line change
Expand Up @@ -1525,7 +1525,7 @@ def InstallDraco(context, force, buildArgs):
############################################################
# MaterialX

MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.38.10.zip"
MATERIALX_URL = "https://github.com/materialx/MaterialX/archive/v1.39.1.zip"

def InstallMaterialX(context, force, buildArgs):
with CurrentWorkingDirectory(DownloadURL(MATERIALX_URL, context, force)):
Expand Down
28 changes: 28 additions & 0 deletions pxr/imaging/hdMtlx/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,31 @@ pxr_library(hdMtlx
PUBLIC_HEADERS
api.h
)

pxr_build_test(testHdMtlx
LIBRARIES
gf
tf
vt
geomUtil
pxOsd
hdMtlx
hdSt
usdImaging
CPPFILES
testenv/testHdMtlx.cpp
)

pxr_install_test_dir(
SRC testenv/testHdMtlx
DEST testHdMtlx
)

pxr_register_test(testHdMtlx
COMMAND "${CMAKE_INSTALL_PREFIX}/tests/testHdMtlx"
DIFF_COMPARE
out_StdSurface.mtlx
out_SwitchExample.mtlx
out_USD_Car_Paint.mtlx
# out_Atan2Example.mtlx
)
235 changes: 235 additions & 0 deletions pxr/imaging/hdMtlx/testenv/testHdMtlx.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
//
// Copyright 2024 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://openusd.org/license.
//

#include "pxr/base/tf/errorMark.h"
#include "pxr/usd/usd/stage.h"
#include "pxr/usd/usd/primRange.h"
#include "pxr/usd/usdShade/material.h"
#include "pxr/imaging/hd/dataSourceMaterialNetworkInterface.h"
#include "pxr/imaging/hd/renderIndex.h"
#include "pxr/imaging/hd/unitTestNullRenderDelegate.h"
#include "pxr/imaging/hdMtlx/hdMtlx.h"
#include "pxr/usdImaging/usdImaging/dataSourceMaterial.h"
#include "pxr/usdImaging/usdImaging/sceneIndices.h"
#include "pxr/usdImaging/usdImaging/stageSceneIndex.h"

#include <MaterialXFormat/XmlIo.h>

#include <iostream>

PXR_NAMESPACE_USING_DIRECTIVE

namespace mx = MaterialX;

class TestStageGlobals : public UsdImagingDataSourceStageGlobals {
public:
TestStageGlobals() : _time(0) {}
~TestStageGlobals() override = default;

UsdTimeCode GetTime() const override { return UsdTimeCode(_time); }

void FlagAsTimeVarying(const SdfPath &hydraPath,
const HdDataSourceLocator &locator) const override {
_timeVarying[hydraPath].insert(locator);
}

void FlagAsAssetPathDependent(const SdfPath &usdPath) const override {
_assetPathDependent.insert(usdPath);
}

HdDataSourceLocatorSet const &
GetTimeVaryingLocators(SdfPath const &hydraPath) const {
return _timeVarying[hydraPath];
}

std::set<SdfPath> const &GetAssetPathDependents() const {
return _assetPathDependent;
}

private:
double _time;
mutable std::map<SdfPath, HdDataSourceLocatorSet> _timeVarying;
mutable std::set<SdfPath> _assetPathDependent;
};

// element predicate to remove includes added by importing standard library
bool elementPredicate(mx::ConstElementPtr elem)
{
if (elem->hasSourceUri())
{
return false;
}
return true;
}


void TestHdMtlx(const std::string& inputFilename,
const std::string& outputFilenamePrefix) {
UsdStageRefPtr stage = UsdStage::Open(inputFilename);

UsdTimeCode frame(0);
Hd_UnitTestNullRenderDelegate renderDelegate;
std::unique_ptr<HdRenderIndex> renderIndex(
HdRenderIndex::New(&renderDelegate, HdDriverVector()));

UsdImagingCreateSceneIndicesInfo info;
info.stage = stage;
const UsdImagingSceneIndices sceneIndices = UsdImagingCreateSceneIndices(info);
UsdImagingStageSceneIndexRefPtr stageSceneIndex = sceneIndices.stageSceneIndex;
stageSceneIndex->SetTime(frame);

// NOTE: this makes assumptions based on scene index emulation and will
// need to be updated when HdRenderIndex no longer uses the
// emulated legacy APIs.
auto registeredSceneIndexNames = HdSceneIndexNameRegistry::GetInstance().GetRegisteredNames();
if (registeredSceneIndexNames.size() != 1) {
std::cerr << "expecting 1 registered scene index (via emulation) and found "
<< registeredSceneIndexNames.size() << " instead." << std::endl;
return;
}

HdSceneIndexBaseRefPtr sceneIndex =
HdSceneIndexNameRegistry::GetInstance().GetNamedSceneIndex(
registeredSceneIndexNames[0]);

if (!sceneIndex) {
std::cerr << "registered scene index is null." << std::endl;
return;
}

std::string prefix = outputFilenamePrefix;
if (prefix.empty()) {
prefix = inputFilename;
}

for (const auto& prim : stage->TraverseAll()) {
if (!prim.IsA<UsdShadeMaterial>()) {
continue;
}

auto mtlSdfPath = prim.GetPath();
std::string outputFilename = prefix+mtlSdfPath.GetName()+".mtlx";

TestStageGlobals stageGlobals;
auto imgPrim = UsdImagingDataSourceMaterialPrim::New(
mtlSdfPath, stage->GetPrimAtPath(mtlSdfPath), stageGlobals);

auto mtlDataSource = imgPrim->Get(TfToken("material"));
HdContainerDataSourceHandle mtlDataSourceContainer =
HdContainerDataSource::Cast(mtlDataSource);
auto mtlxDataSource = mtlDataSourceContainer->Get(TfToken("mtlx"));
HdContainerDataSourceHandle mtlxDataSourceContainer =
HdContainerDataSource::Cast(mtlxDataSource);

HdSceneIndexPrim hdPrim = sceneIndex->GetPrim(mtlSdfPath);

auto hdNetwork = HdDataSourceMaterialNetworkInterface(
mtlSdfPath, mtlxDataSourceContainer, hdPrim.dataSource);

auto terminalNames = hdNetwork.GetTerminalNames();
if (terminalNames.empty()) {
std::cerr << "No terminals defined." << std::endl;
return;
}

auto terminalNode = hdNetwork.GetTerminalConnection(terminalNames[0]);
if (!terminalNode.first) {
std::cerr << "No terminal node." << std::endl;
return;
}

const mx::DocumentPtr &stdLibraries = HdMtlxStdLibraries();
TfToken terminalNodeName = terminalNode.second.upstreamNodeName;
TfTokenVector terminalNodeConnectionNames =
hdNetwork.GetNodeInputConnectionNames(terminalNodeName);

HdMtlxTexturePrimvarData *mxHdData = nullptr;

MaterialX::DocumentPtr doc =
HdMtlxCreateMtlxDocumentFromHdMaterialNetworkInterface(
&hdNetwork, terminalNodeName, terminalNodeConnectionNames,
stdLibraries, mxHdData);

mx::XmlWriteOptions writeOptions;
writeOptions.elementPredicate = [](mx::ConstElementPtr elem) -> bool {
// skip writing all includes for brevity
if (elem->hasSourceUri()) {
return false;
}
return true;
};

mx::writeToXmlFile(doc, outputFilename, &writeOptions);
}
}

int main(int argc, char *argv[]) {
TfErrorMark mark;

if (argc == 1) {
// standard cmake unit test run
// We are intentionally loading usd files here that hold UsdShade Materials
// representing MaterialX, instead of loading .mtlx files, because the UsdMtlx
// plugin can only create UsdShade network for the current MaterialX library
// version.

// Test a case where no config:mtlx:version is specified.
TestHdMtlx("standard_surface_no_mtlx_version.usda",
"out_");

TestHdMtlx("usd_preview_surface_carpaint.usda",
"out_");

// Test a version where MaterialX actually has to mutate the data - switch
// used to only have 5 inputs but now has 10, so previously a 'which' of anything
// 5 or over acted like a 0, the MaterialX upgrade modifies those values to 0
TestHdMtlx("switch_example.usda",
"out_");

// This atan2 test currently fails because HdMtlxCreateMtlxDocumentFromHdMaterialNetworkInterface()
// is unable to determine the correct type of the input ports, because their names have changed and
// thus the MaterialX lookup fails.
// I think we might need to update/extend _GetInputType() in hdMtlx.cpp to derive the correct MaterialX
// type using the data from HdNetwork - but perhaps not enough data is stored there currently?
//
// TestHdMtlx("atan2_example.usda",
// "out_");

} else {
// ad-hoc testing allows for passing options on the command line.

std::string inputFilename = "";
std::string outputFilenamePrefix = "";

for (int i = 0; i < argc; ++i) {
const std::string arg(argv[i]);

if (arg == "--inputFilename") {
inputFilename = mx::FilePath(argv[++i]);
} else if (arg == "--outputFilenamePrefix") {
outputFilenamePrefix = mx::FilePath(argv[++i]);
}
}

if (inputFilename.empty() ) {
std::cerr << "--inputFilename is required.";
return EXIT_FAILURE;
}

TestHdMtlx(inputFilename, outputFilenamePrefix);
}

TF_VERIFY(mark.IsClean());

if (mark.IsClean()) {
std::cout << "OK" << std::endl;
return EXIT_SUCCESS;
} else {
std::cout << "FAILED" << std::endl;
return EXIT_FAILURE;
}
}
81 changes: 81 additions & 0 deletions pxr/imaging/hdMtlx/testenv/testHdMtlx/atan2_example.usda
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#usda 1.0
(
customLayerData = {
string colorSpace = "lin_rec709"
}
)

def "MaterialX"
{
def "Materials"
{
def Material "Atan2Example" (
prepend apiSchemas = ["MaterialXConfigAPI"]
)
{
string config:mtlx:version = "1.38"
float inputs:clearcoat
float inputs:clearcoatRoughness
color3f inputs:diffuseColor = (0.10470402, 0.24188282, 0.818)
float inputs:displacement
color3f inputs:emissiveColor
float inputs:ior
float inputs:metallic
float3 inputs:normal
float inputs:occlusion
float inputs:opacity
float inputs:opacityThreshold
float inputs:roughness
color3f inputs:specularColor
int inputs:useSpecularWorkflow
token outputs:mtlx:surface.connect = </MaterialX/Materials/Atan2Example/ND_UsdPreviewSurface_surfaceshader.outputs:surface>

def Shader "ND_UsdPreviewSurface_surfaceshader" (
prepend references = </MaterialX/Shaders/ND_UsdPreviewSurface_surfaceshader>
)
{
float inputs:clearcoat.connect = </MaterialX/Materials/Atan2Example.inputs:clearcoat>
float inputs:clearcoatRoughness.connect = </MaterialX/Materials/Atan2Example.inputs:clearcoatRoughness>
color3f inputs:diffuseColor.connect = </MaterialX/Materials/Atan2Example.inputs:diffuseColor>
float inputs:displacement.connect = </MaterialX/Materials/Atan2Example.inputs:displacement>
color3f inputs:emissiveColor.connect = </MaterialX/Materials/Atan2Example.inputs:emissiveColor>
float inputs:ior.connect = </MaterialX/Materials/Atan2Example.inputs:ior>
float inputs:metallic.connect = </MaterialX/Materials/Atan2Example/NodeGraphs/atan2.outputs:out>
float3 inputs:normal.connect = </MaterialX/Materials/Atan2Example.inputs:normal>
float inputs:occlusion.connect = </MaterialX/Materials/Atan2Example.inputs:occlusion>
float inputs:opacity.connect = </MaterialX/Materials/Atan2Example.inputs:opacity>
float inputs:opacityThreshold.connect = </MaterialX/Materials/Atan2Example.inputs:opacityThreshold>
float inputs:roughness.connect = </MaterialX/Materials/Atan2Example.inputs:roughness>
color3f inputs:specularColor.connect = </MaterialX/Materials/Atan2Example.inputs:specularColor>
int inputs:useSpecularWorkflow.connect = </MaterialX/Materials/Atan2Example.inputs:useSpecularWorkflow>
}

def "NodeGraphs" (
prepend references = </MaterialX/NodeGraphs>
)
{
}
}
}

def "Shaders"
{
def Shader "ND_UsdPreviewSurface_surfaceshader"
{
uniform token info:id = "ND_UsdPreviewSurface_surfaceshader"
token outputs:surface
}
}

def "NodeGraphs"
{
def Shader "atan2"
{
uniform token info:id = "ND_atan2_float"
float inputs:in1 = 0.2
float inputs:in2 = 0.7
float outputs:out
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0"?>
<materialx version="1.39">
<UsdPreviewSurface name="Surface" type="surfaceshader">
<input name="metallic" type="float" output="metallic_out" nodegraph="NodeGraphs" />
<input name="diffuseColor" type="color3" value="0.104704, 0.241883, 0.818" />
</UsdPreviewSurface>
<surfacematerial name="Atan2Example" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="Surface" />
</surfacematerial>
<nodegraph name="NodeGraphs">
<atan2 name="atan2" type="float" nodedef="ND_atan2_float">
<input name="iny" type="float" value="0.2" />
<input name="inx" type="float" value="0.7" />
</atan2>
<output name="metallic_out" type="float" nodename="atan2" />
</nodegraph>
</materialx>
10 changes: 10 additions & 0 deletions pxr/imaging/hdMtlx/testenv/testHdMtlx/baseline/out_StdSurface.mtlx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0"?>
<materialx version="1.39">
<standard_surface name="Surface" type="surfaceshader">
<input name="base" type="float" value="0.2" />
<input name="base_color" type="color3" value="0.2, 0.4, 0.2" />
</standard_surface>
<surfacematerial name="StdSurface" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="Surface" />
</surfacematerial>
</materialx>
Loading