USD: Add more thorough testing of various material setups

Newly validates the following:
- Image mapping transforms on import and export
- Typical normal map setups on import and export
- Alpha-clip node setups on import (was already tested for export)

Pull Request: https://projects.blender.org/blender/blender/pulls/126776
This commit is contained in:
Jesse Yurkovich
2024-08-26 03:26:32 +02:00
committed by Jesse Yurkovich
parent 6bd515e0d2
commit b265161aea
5 changed files with 127 additions and 16 deletions

View File

@@ -264,9 +264,9 @@ TEST_F(UsdExportTest, usd_export_material)
}
/* File sanity checks. */
EXPECT_EQ(BLI_listbase_count(&bfile->main->objects), 3);
/* There are 4 materials because of the Dots Stroke. */
EXPECT_EQ(BLI_listbase_count(&bfile->main->materials), 4);
EXPECT_EQ(BLI_listbase_count(&bfile->main->objects), 6);
/* There is 1 additional material because of the "Dots Stroke". */
EXPECT_EQ(BLI_listbase_count(&bfile->main->materials), 7);
Material *material = reinterpret_cast<Material *>(
BKE_libblock_find_name(bfile->main, ID_MA, "Material"));
@@ -279,6 +279,8 @@ TEST_F(UsdExportTest, usd_export_material)
params.export_textures = false;
params.export_uvmaps = true;
params.generate_preview_surface = true;
params.generate_materialx_network = false;
params.convert_world_material = false;
params.relative_paths = false;
const bool result = USD_export(context, output_filename.c_str(), &params, false, nullptr);

View File

@@ -96,6 +96,7 @@ TEST_F(UsdUsdzExportTest, usdz_export)
USDExportParams params;
params.export_materials = false;
params.convert_world_material = false;
params.visible_objects_only = false;
bool result = USD_export(context, output_filepath, &params, false, nullptr);

View File

@@ -132,18 +132,68 @@ class USDExportTest(AbstractUSDTest):
Gf.Vec3d(extent[1]), Gf.Vec3d(0.7515701, 0.5500924, 0.9027928)
)
def test_opacity_threshold(self):
# Note that the scene file used here is shared with a different test.
# Here we assume that it has a Principled BSDF material with
# a texture connected to its Base Color input.
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_materials_export.blend"))
def test_material_transforms(self):
"""Validate correct export of image mapping parameters to the UsdTransform2d shader def"""
export_path = self.tempdir / "opaque_material.usda"
res = bpy.ops.wm.usd_export(
filepath=str(export_path),
export_materials=True,
evaluation_mode="RENDER",
)
# Use the common materials .blend file
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_materials_export.blend"))
export_path = self.tempdir / "material_transforms.usda"
res = bpy.ops.wm.usd_export(filepath=str(export_path), export_materials=True)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
# Inspect the UsdTransform2d prim on the "Transforms" material
stage = Usd.Stage.Open(str(export_path))
shader_prim = stage.GetPrimAtPath("/root/_materials/Transforms/Mapping")
shader = UsdShade.Shader(shader_prim)
self.assertEqual(shader.GetIdAttr().Get(), "UsdTransform2d")
input_trans = shader.GetInput('translation')
input_rot = shader.GetInput('rotation')
input_scale = shader.GetInput('scale')
self.assertEqual(input_trans.Get(), [0.75, 0.75])
self.assertEqual(input_rot.Get(), 180)
self.assertEqual(input_scale.Get(), [0.5, 0.5])
def test_material_normal_maps(self):
"""Validate correct export of typical normal map setups to the UsdUVTexture shader def.
Namely validate that scale, bias, and ColorSpace settings are correct"""
# Use the common materials .blend file
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_materials_export.blend"))
export_path = self.tempdir / "material_normalmaps.usda"
res = bpy.ops.wm.usd_export(filepath=str(export_path), export_materials=True)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
# Inspect the UsdUVTexture prim on the "typical" "NormalMap" material
stage = Usd.Stage.Open(str(export_path))
shader_prim = stage.GetPrimAtPath("/root/_materials/NormalMap/Image_Texture")
shader = UsdShade.Shader(shader_prim)
self.assertEqual(shader.GetIdAttr().Get(), "UsdUVTexture")
input_scale = shader.GetInput('scale')
input_bias = shader.GetInput('bias')
input_colorspace = shader.GetInput('sourceColorSpace')
self.assertEqual(input_scale.Get(), [2, 2, 2, 2])
self.assertEqual(input_bias.Get(), [-1, -1, -1, -1])
self.assertEqual(input_colorspace.Get(), 'raw')
# Inspect the UsdUVTexture prim on the "inverted" "NormalMap_Scale_Bias" material
stage = Usd.Stage.Open(str(export_path))
shader_prim = stage.GetPrimAtPath("/root/_materials/NormalMap_Scale_Bias/Image_Texture")
shader = UsdShade.Shader(shader_prim)
self.assertEqual(shader.GetIdAttr().Get(), "UsdUVTexture")
input_scale = shader.GetInput('scale')
input_bias = shader.GetInput('bias')
input_colorspace = shader.GetInput('sourceColorSpace')
self.assertEqual(input_scale.Get(), [2, -2, 2, 1])
self.assertEqual(input_bias.Get(), [-1, 1, -1, 0])
self.assertEqual(input_colorspace.Get(), 'raw')
def test_material_opacity_threshold(self):
"""Validate correct export of opacity and opacity_threshold parameters to the UsdPreviewSurface shader def"""
# Use the common materials .blend file
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_materials_export.blend"))
export_path = self.tempdir / "material_opacities.usda"
res = bpy.ops.wm.usd_export(filepath=str(export_path), export_materials=True)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
# Inspect and validate the exported USD for the opaque blend case.

View File

@@ -33,7 +33,6 @@ class AbstractUSDTest(unittest.TestCase):
class USDImportTest(AbstractUSDTest):
def test_import_operator(self):
"""Test running the import operator on valid and invalid files."""
@@ -203,6 +202,65 @@ class USDImportTest(AbstractUSDTest):
self.assertAlmostEqual(2.281, test_cam.shift_x, 3)
self.assertAlmostEqual(0.496, test_cam.shift_y, 3)
def test_import_materials(self):
"""Validate UsdPreviewSurface shader graphs."""
# Use the existing materials test file to create the USD file
# for import. It is validated as part of the bl_usd_export test.
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "usd_materials_export.blend"))
testfile = str(self.tempdir / "temp_materials.usda")
res = bpy.ops.wm.usd_export(filepath=str(testfile), export_materials=True)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {testfile}")
# Reload the empty file and import back in
bpy.ops.wm.open_mainfile(filepath=str(self.testdir / "empty.blend"))
res = bpy.ops.wm.usd_import(filepath=testfile)
self.assertEqual({'FINISHED'}, res, f"Unable to import USD file {testfile}")
# Most shader graph validation should occur through the Hydra render test suite. Here we
# will only check some high-level criteria for each expected node graph.
def assert_all_nodes_present(mat, node_list):
nodes = mat.node_tree.nodes
self.assertEqual(len(nodes), len(node_list))
for node in node_list:
self.assertTrue(nodes.find(node) >= 0, f"Could not find node '{node}' in material '{mat.name}'")
def round_vector(vector):
return [round(c, 5) + 0 for c in vector]
mat = bpy.data.materials["Material"]
assert_all_nodes_present(mat, ["Principled BSDF", "Image Texture", "UV Map", "Material Output"])
mat = bpy.data.materials["Clip_With_LessThanInvert"]
assert_all_nodes_present(
mat, ["Principled BSDF", "Image Texture", "UV Map", "Math", "Math.001", "Material Output"])
node = [n for n in mat.node_tree.nodes if n.type == 'MATH' and n.operation == "LESS_THAN"][0]
self.assertAlmostEqual(node.inputs[1].default_value, 0.2, 3)
mat = bpy.data.materials["Clip_With_Round"]
assert_all_nodes_present(
mat, ["Principled BSDF", "Image Texture", "UV Map", "Math", "Math.001", "Material Output"])
node = [n for n in mat.node_tree.nodes if n.type == 'MATH' and n.operation == "LESS_THAN"][0]
self.assertAlmostEqual(node.inputs[1].default_value, 0.5, 3)
mat = bpy.data.materials["Transforms"]
assert_all_nodes_present(mat, ["Principled BSDF", "Image Texture", "UV Map", "Mapping", "Material Output"])
node = mat.node_tree.nodes["Mapping"]
self.assertEqual(round_vector(node.inputs[1].default_value), [0.75, 0.75, 0])
self.assertEqual(round_vector(node.inputs[2].default_value), [0, 0, 3.14159])
self.assertEqual(round_vector(node.inputs[3].default_value), [0.5, 0.5, 1])
mat = bpy.data.materials["NormalMap"]
assert_all_nodes_present(mat, ["Principled BSDF", "Image Texture", "UV Map", "Normal Map", "Material Output"])
mat = bpy.data.materials["NormalMap_Scale_Bias"]
assert_all_nodes_present(mat, ["Principled BSDF", "Image Texture", "UV Map",
"Normal Map", "Vector Math", "Vector Math.001", "Material Output"])
node = mat.node_tree.nodes["Vector Math"]
self.assertEqual(round_vector(node.inputs[1].default_value), [2, -2, 2])
self.assertEqual(round_vector(node.inputs[2].default_value), [-1, 1, -1])
def test_import_shader_varname_with_connection(self):
"""Test importing USD shader where uv primvar is a connection"""