[MAX-MAT-002] Strip buggy emission=1.0 / emission_color=(0,0,0) from MaterialX export#37
Open
doug-smith-miris wants to merge 2 commits into
Conversation
3ds Max's MtlxIOUtil bridge hardcodes specular_rotation=0.25 on every ND_standard_surface_surfaceshader, regardless of source PhysicalMaterial anisotropy. The MaterialX nodedef default is 0.0; the value has no visual effect when specular_anisotropy is zero (the BSDF multiplies them), but it silently introduces a 90deg highlight rotation if a downstream layer turns anisotropy on. Add a post-parse normalization pass in MtlxShaderWriter::Write that walks the parsed MaterialX document and drops specular_rotation from standard_surface nodes when: * the value is statically 0.25 (the bug pattern), AND * specular_anisotropy is absent or statically 0.0. Connected anisotropy, any non-0.25 rotation value, and connected rotation are conservatively left untouched. Validated against the diagnostic corpus (samples/complex_export.usda): 6/6 affected materials cleaned, Karma renders byte-identical pre/post (SHA-256 match — semantic-only fix). Synthetic negative test (BrushedSteel with specular_anisotropy=0.4) confirms the fix is surgical and preserves intentional rotation. Also adds doc/translation-mapping.md to track 3ds Max -> MaterialX property mappings going forward, and a MaxScript regression test in mtlxShaderWriter_test.ms. Build/run validation deferred: 3ds Max is Windows-only and this repo builds via Visual Studio (configure-vsdevcmd.bat). The Python validator in the arch-build dir mirrors the C++ logic exactly at the USD layer. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3ds Max's MtlxIOUtil bridge hardcodes emission=1.0 paired with
emission_color=(0,0,0) on every ND_standard_surface_surfaceshader,
regardless of the source PhysicalMaterial. The MaterialX nodedef defaults
are emission=0.0 and emission_color=(1,1,1); the buggy pair multiplies to
(0,0,0) so it has no visual effect, but it is semantically wrong --
any downstream layer that bumps emission_color away from black would
unexpectedly turn emission on at full strength, and any round-trip
importer reads (0,0,0) as "explicit black emission" rather than "no
emission authored".
Add a second post-parse normalization pass in MtlxShaderWriter::Write
(immediately after MAX-MAT-001's pass) that walks the parsed MaterialX
document and strips BOTH inputs from standard_surface nodes when:
* emission is statically 1.0 (no connection, tolerant of float noise),
* emission_color is statically (0,0,0) (no connection, same tolerance),
* AND both inputs are present (the bug is the pair; treating either
half in isolation could destroy a real authored value).
Any deviation -- non-1.0 emission, non-black color, connected input,
missing half -- leaves both inputs untouched.
Adds a small _TryGetStaticColor3 helper that parses MaterialX color3
value strings ("r, g, b" or "r g b") via istringstream with separator
normalization; rejects strings with the wrong number of components.
Validated against the diagnostic corpus (samples/complex_export.usda)
via a Python validator that mirrors the C++ logic 1:1 at the USD layer:
6/6 affected materials cleaned, exactly 12 deletions in the USDA diff
(6 emission + 6 emission_color), zero additions, zero structural
changes. Karma renders byte-identical pre/post (SHA-256 match -- the
buggy pair multiplies to zero emission, the nodedef defaults also
multiply to zero emission, both produce the same BSDF lighting result).
Synthetic negative-test fixture (BrushedSteel with intentional
emission_color=(0.8, 0.4, 0.1); Copper with intentional emission=0.5)
confirms the fix is surgical: 4/6 stripped, both intentional materials
preserved.
Also updates doc/translation-mapping.md and adds a MaxScript
regression test in mtlxShaderWriter_test.ms.
Build/run validation deferred: 3ds Max is Windows-only and this repo
builds via Visual Studio. The Python validator mirrors the C++ logic
exactly at the USD layer.
Stacked on top of Autodesk#36 (MAX-MAT-001).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Stacked on top of #36 (MAX-MAT-001). Until that PR merges, this PR's diff includes its commits too — once MAX-MAT-001 merges into
dev, this PR's diff will collapse to just the MAX-MAT-002 changes (1 commit, 4 files).Adds a second post-parse normalization pass in
MtlxShaderWriter::Write()that strips the spuriousemission = 1.0paired withemission_color = (0, 0, 0)that 3ds Max'sMtlxIOUtilbridge writes on everyND_standard_surface_surfaceshader.The MaterialX
standard_surfacenodedef defaults areemission = 0.0andemission_color = (1, 1, 1). The buggy pair multiplies to(0, 0, 0)so it has no visual effect, but it is semantically wrong:emission_coloraway from black inherits a full-strengthemission = 1.0the artist never asked for.emission_color = (0, 0, 0)as "explicit black emission" rather than "no emission authored" — semantically different states that diverge under later edits.What changed
src/translators/MtlxShaderWriter.cpp:_TryGetStaticColor3— parses MaterialX color3 value strings ("r, g, b"or"r g b") when not connected; rejects strings with the wrong component count._NormalizeStandardSurfaceEmissionDefault— walksstandard_surfacenodes and strips bothemissionandemission_colorinputs when both are statically the buggy pair. Any deviation (connected input, non-1.0 emission, non-black color, missing half) leaves both untouched.Write()immediately after_NormalizeStandardSurfaceSpecularRotation.doc/translation-mapping.md: adds a new row + section + change log entry.doc/changelog.md: adds an Unreleased fix entry.src/Tests/Integration/mtlxShaderWriter_test.ms: addstest_export_physical_material_strips_buggy_emission_default.Validation
3ds Max is Windows-only and this repo builds via Visual Studio; build/run was deferred (same constraint as #36). A Python validator that mirrors the C++ logic 1:1 at the USD layer was run on the diagnostic corpus and synthetic fixtures.
Diagnostic corpus (
complex_export_mat001_postfix.usda, MAX-MAT-001-clean): 6/6 standard_surface shaders affected. The USDA diff is exactly 12 deletions (6emission, 6emission_color), zero additions, zero structural changes.Karma renders of pre-fix and post-fix corpora are byte-identical (SHA-256
eaf38328...). This is the expected outcome and the success signal — the buggy pair multiplies to zero emission, the nodedef-default pair multiplies to zero emission, both produce the same BSDF lighting result.Synthetic negative-test fixture (
_synthetic_with_emission.usda):emission_colorset to(0.8, 0.4, 0.1)— intentional orange emission tint → kept.emissionset to0.5— intentional partial emission → kept.The MaxScript regression test extends the existing PhysicalMaterial test pattern from MAX-MAT-001.
Reference files (validator + corpus)
Under
agent/arch-builds/f72c97e6-301f-400a-9e77-c768979e8fa5/(outside the repo):normalize_emission_default.py— USD-layer mirror of the C++ logic.complex_export_mat001_postfix.usda— input (MAX-MAT-001-clean, MAX-MAT-002-dirty).complex_export_postfix.usda— output (MAX-MAT-001+002-clean).make_negative_fixture.py,_synthetic_with_emission.usda,_synthetic_postfix.usda.render_karma_prefix.png,render_karma_postfix.png(byte-identical),compare_side_by_side.png.intended_example.md— detailed observable / fixture description for the auditor.Test plan
mtlxShaderWriter_test.ms) and confirm the new test passes alongside the MAX-MAT-001 test.emission = 1.0+emission_color = (0, 0, 0)pair appears on anystandard_surfaceshader, while any intentionally authored emission is preserved.🤖 Generated with Claude Code