Fix #107062: support opacityThreshold when exporting USD #107149

Merged
Michael Kowalski merged 3 commits from wave/blender_wave_Apple:contribs/T107062_opacity_threshold into main 2023-05-01 17:28:30 +02:00
2 changed files with 84 additions and 0 deletions

View File

@ -46,6 +46,7 @@ static const pxr::TfToken primvar_float2("UsdPrimvarReader_float2", pxr::TfToken
static const pxr::TfToken roughness("roughness", pxr::TfToken::Immortal);
static const pxr::TfToken specular("specular", pxr::TfToken::Immortal);
static const pxr::TfToken opacity("opacity", pxr::TfToken::Immortal);
static const pxr::TfToken opacityThreshold("opacityThreshold", pxr::TfToken::Immortal);
static const pxr::TfToken surface("surface", pxr::TfToken::Immortal);
static const pxr::TfToken perspective("perspective", pxr::TfToken::Immortal);
static const pxr::TfToken orthographic("orthographic", pxr::TfToken::Immortal);
@ -143,6 +144,8 @@ void create_usd_preview_surface_material(const USDExporterContext &usd_export_co
const InputSpecMap &input_map = preview_surface_input_map();
bool has_opacity = false;
/* Set the preview surface inputs. */
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
@ -166,6 +169,10 @@ void create_usd_preview_surface_material(const USDExporterContext &usd_export_co
preview_surface.CreateInput(input_spec.input_name, input_spec.input_type)
.ConnectToSource(created_shader.ConnectableAPI(), input_spec.source_name);
set_normal_texture_range(created_shader, input_spec);
if (input_spec.input_name == usdtokens::opacity) {
has_opacity = true;
}
}
else if (input_spec.set_default_value) {
/* Set hardcoded value. */
@ -202,6 +209,14 @@ void create_usd_preview_surface_material(const USDExporterContext &usd_export_co
create_uvmap_shader(
usd_export_context, input_node, usd_material, created_shader, default_uv_sampler);
}
/* Set opacityThreshold if an alpha cutout is used. */
if (has_opacity) {
if ((material->blend_method == MA_BM_CLIP) && (material->alpha_threshold > 0.0)) {
pxr::UsdShadeInput opacity_threshold_input = preview_surface.CreateInput(usdtokens::opacityThreshold, pxr::SdfValueTypeNames->Float);
opacity_threshold_input.GetAttr().Set(pxr::VtValue(material->alpha_threshold));
}
}
}
void set_normal_texture_range(pxr::UsdShadeShader &usd_shader, const InputSpec &input_spec)

View File

@ -8,6 +8,7 @@ import unittest
from pxr import Usd
from pxr import UsdUtils
from pxr import UsdGeom
from pxr import UsdShade
from pxr import Gf
import bpy
@ -124,6 +125,74 @@ 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"))
Matt-McLin marked this conversation as resolved
Review

I suspect this print statement was for debugging and should probably be removed.

I suspect this print statement was for debugging and should probably be removed.
Review

Indeed, thanks for catching that.

Indeed, thanks for catching that.
export_path = self.tempdir / "opaque_material.usda"
res = bpy.ops.wm.usd_export(
filepath=str(export_path),
export_materials=True,
evaluation_mode="RENDER",
)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
# Inspect and validate the exported USD for the opaque blend case.
stage = Usd.Stage.Open(str(export_path))
shader_prim = stage.GetPrimAtPath("/_materials/Material/Principled_BSDF")
shader = UsdShade.Shader(shader_prim)
opacity_input = shader.GetInput('opacity')
self.assertEqual(opacity_input.HasConnectedSource(), False, "Opacity input should not be connected for opaque material")
self.assertAlmostEqual(opacity_input.Get(), 1.0, "Opacity input should be set to 1")
# The material already has a texture input to the Base Color.
# Now also link this texture to the Alpha input.
# Set an opacity threshold appropriate for alpha clipping.
mat = bpy.data.materials['Material']
bsdf = mat.node_tree.nodes['Principled BSDF']
tex_output = bsdf.inputs['Base Color'].links[0].from_node.outputs['Color']
alpha_input = bsdf.inputs['Alpha']
mat.node_tree.links.new(tex_output,alpha_input)
bpy.data.materials['Material'].blend_method = 'CLIP'
bpy.data.materials['Material'].alpha_threshold = 0.01
export_path = self.tempdir / "alphaclip_material.usda"
res = bpy.ops.wm.usd_export(
filepath=str(export_path),
export_materials=True,
evaluation_mode="RENDER",
)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
# Inspect and validate the exported USD for the alpha clip case.
stage = Usd.Stage.Open(str(export_path))
shader_prim = stage.GetPrimAtPath("/_materials/Material/Principled_BSDF")
shader = UsdShade.Shader(shader_prim)
opacity_input = shader.GetInput('opacity')
opacity_thres_input = shader.GetInput('opacityThreshold')
self.assertEqual(opacity_input.HasConnectedSource(), True, "Alpha input should be connected")
self.assertGreater(opacity_thres_input.Get(), 0.0, "Opacity threshold input should be > 0")
# Modify material again, this time with alpha blend.
bpy.data.materials['Material'].blend_method = 'BLEND'
export_path = self.tempdir / "alphablend_material.usda"
res = bpy.ops.wm.usd_export(
filepath=str(export_path),
export_materials=True,
evaluation_mode="RENDER",
)
self.assertEqual({'FINISHED'}, res, f"Unable to export to {export_path}")
# Inspect and validate the exported USD for the alpha blend case.
stage = Usd.Stage.Open(str(export_path))
shader_prim = stage.GetPrimAtPath("/_materials/Material/Principled_BSDF")
shader = UsdShade.Shader(shader_prim)
opacity_input = shader.GetInput('opacity')
opacity_thres_input = shader.GetInput('opacityThreshold')
self.assertEqual(opacity_input.HasConnectedSource(), True, "Alpha input should be connected")
self.assertEqual(opacity_thres_input.Get(), None, "Opacity threshold should not be specified for alpha blend")
def main():
global args