Skip to content

Commit e20fa7b

Browse files
committed
docs: add --report output reference with shader gen transparency analysis
Comprehensive documentation of the --report JSON output format, covering: - JSON schema for validation errors and renderable analysis - How isTransparentSurface() works and its concrete rendering impact - Lit vs unlit surface shader alpha code paths - The hint system for custom nodedef transparency inputs - Two distinct threshold mechanisms: alpha_cutoff (graph-level) vs u_alphaThreshold (render-time) - Per-shader alpha mode detection (gltf_pbr, UsdPreviewSurface, standard_surface, open_pbr_surface, surface_unlit) - Surface shader nodedef reference table - Cutout roundtrip validation guide feat: add --report flag with structured validation and renderable analysis Add --report [text|json] to MaterialXView and MaterialXGraphEditor for headless material validation and shader analysis. Includes ValidationError structs, RenderableAnalysis with shaderNode/shaderNodeDef/shaderNodeDefVersion/isUnlit/transparency/alphaMode/alphaCutoff/transparencyInputs, per-shader alpha mode detection (gltf_pbr 3 modes, UsdPreviewSurface cutout-only, hint-based open_pbr), and materialXVersion at root level. refactor again to clean up and minimize code impact
1 parent 270b5cf commit e20fa7b

7 files changed

Lines changed: 634 additions & 62 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
build
22
dist
33
.DS_Store
4+
python/MaterialX/__pycache__
5+
python/MaterialX/*.so

source/MaterialXCore/Element.cpp

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@
1212

1313
MATERIALX_NAMESPACE_BEGIN
1414

15+
namespace
16+
{
17+
thread_local Element::ValidationErrors* g_validationErrors = nullptr;
18+
} // anonymous namespace
19+
1520
const string Element::NAME_ATTRIBUTE = "name";
1621
const string Element::FILE_PREFIX_ATTRIBUTE = "fileprefix";
1722
const string Element::GEOM_PREFIX_ATTRIBUTE = "geomprefix";
@@ -627,9 +632,87 @@ void Element::validateRequire(bool expression, bool& res, string* message, const
627632
res = false;
628633
if (message)
629634
{
630-
*message += errorDesc + ": " + asString() + "\n";
635+
string location = getNamePath();
636+
const string& sourceUri = getActiveSourceUri();
637+
if (!sourceUri.empty())
638+
{
639+
location += " (file=" + sourceUri + ")";
640+
}
641+
*message += errorDesc + " at " + location + ": " + asString() + "\n";
642+
}
643+
if (g_validationErrors)
644+
{
645+
ValidationError err;
646+
err.message = errorDesc;
647+
err.path = getNamePath();
648+
err.source = asString();
649+
err.file = getActiveSourceUri();
650+
err.severity = ValidationSeverity::ERROR;
651+
g_validationErrors->push_back(err);
652+
}
653+
}
654+
}
655+
656+
Element::ValidationErrorScope::ValidationErrorScope(ValidationErrors* errors)
657+
{
658+
_prev = g_validationErrors;
659+
g_validationErrors = errors;
660+
}
661+
662+
Element::ValidationErrorScope::~ValidationErrorScope()
663+
{
664+
g_validationErrors = _prev;
665+
}
666+
667+
const char* Element::validationSeverityToString(ValidationSeverity severity)
668+
{
669+
switch (severity)
670+
{
671+
case ValidationSeverity::ERROR: return "error";
672+
case ValidationSeverity::WARNING: return "warning";
673+
case ValidationSeverity::HINT: return "hint";
674+
default: return "error";
675+
}
676+
}
677+
678+
string Element::formatValidationErrorsJson(const ValidationErrors& errors)
679+
{
680+
string json = "[";
681+
for (size_t i = 0; i < errors.size(); i++)
682+
{
683+
if (i > 0)
684+
{
685+
json += ",";
686+
}
687+
json += "{\"message\":\"" + escapeJsonString(errors[i].message) + "\"";
688+
json += ",\"path\":\"" + escapeJsonString(errors[i].path) + "\"";
689+
json += ",\"source\":\"" + escapeJsonString(errors[i].source) + "\"";
690+
json += ",\"file\":\"" + escapeJsonString(errors[i].file) + "\"";
691+
json += ",\"severity\":\"" + string(validationSeverityToString(errors[i].severity)) + "\"}";
692+
}
693+
json += "]";
694+
return json;
695+
}
696+
697+
string Element::escapeJsonString(const string& input)
698+
{
699+
string out;
700+
out.reserve(input.size());
701+
for (char c : input)
702+
{
703+
switch (c)
704+
{
705+
case '"': out += "\\\""; break;
706+
case '\\': out += "\\\\"; break;
707+
case '\b': out += "\\b"; break;
708+
case '\f': out += "\\f"; break;
709+
case '\n': out += "\\n"; break;
710+
case '\r': out += "\\r"; break;
711+
case '\t': out += "\\t"; break;
712+
default: out += c; break;
631713
}
632714
}
715+
return out;
633716
}
634717

635718
//

source/MaterialXCore/Element.h

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <MaterialXCore/Util.h>
1616
#include <MaterialXCore/Value.h>
1717

18+
#include <vector>
19+
1820
MATERIALX_NAMESPACE_BEGIN
1921

2022
class Element;
@@ -754,6 +756,40 @@ class MX_CORE_API Element : public std::enable_shared_from_this<Element>
754756
/// @name Validation
755757
/// @{
756758

759+
enum class ValidationSeverity
760+
{
761+
ERROR,
762+
WARNING,
763+
HINT
764+
};
765+
766+
struct ValidationError
767+
{
768+
string message;
769+
string path;
770+
string source;
771+
string file;
772+
ValidationSeverity severity = ValidationSeverity::ERROR;
773+
};
774+
775+
using ValidationErrors = vector<ValidationError>;
776+
777+
class MX_CORE_API ValidationErrorScope
778+
{
779+
public:
780+
explicit ValidationErrorScope(ValidationErrors* errors);
781+
~ValidationErrorScope();
782+
783+
private:
784+
ValidationErrors* _prev = nullptr;
785+
};
786+
787+
static const char* validationSeverityToString(ValidationSeverity severity);
788+
static string formatValidationErrorsJson(const ValidationErrors& errors);
789+
790+
/// Escape a string for safe inclusion in JSON output.
791+
static string escapeJsonString(const string& input);
792+
757793
/// Validate that the given element tree, including all descendants, is
758794
/// consistent with the MaterialX specification.
759795
virtual bool validate(string* message = nullptr) const;

0 commit comments

Comments
 (0)