Group create-node list by source and category; make editor columns resizable
The create-node list is now a two-level tree: a header per node source (USD, MaterialX, Arnold, Cycles - from the Sdr sourceType, with glslfx and OSL merged into USD so core nodes don't appear twice) containing Hypershade-style category sub-trees (Material / Texture / Geometry / Utility / Light) derived from Sdr context, terminal output types, role, and name keywords. Duplicate collapsing is per (source, label) so same-named nodes from different providers both stay; searching forces all sections open. The TAB popup shows [Source/Category] per entry. The browser and preview columns get drag splitters (viewport-divider styling, clamped widths); the canvas absorbs the remainder. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
#include "MaterialManager.h"
|
||||
#include <pxr/usd/sdr/registry.h>
|
||||
#include <pxr/usd/sdr/shaderProperty.h>
|
||||
#include <pxr/base/tf/stringUtils.h>
|
||||
#include <pxr/usd/usd/primRange.h>
|
||||
#include <pxr/usd/usdShade/material.h>
|
||||
#include <pxr/usd/usdShade/shader.h>
|
||||
@@ -9,10 +11,84 @@
|
||||
#include <pxr/base/tf/token.h>
|
||||
#include <pxr/base/vt/value.h>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <set>
|
||||
|
||||
namespace UsdLayerManager {
|
||||
|
||||
namespace {
|
||||
|
||||
// Coarse Hypershade-style bucket for the create-node list. Sdr metadata is
|
||||
// parser-dependent (glslfx vs MaterialX vs Arnold vs Cycles fill different
|
||||
// fields), so classify from the strongest signal available: context first,
|
||||
// then terminal output types, then role, then name keywords.
|
||||
std::string DeriveCategory(pxr::SdrShaderNodeConstPtr node) {
|
||||
const std::string context = pxr::TfStringify(node->GetContext());
|
||||
if (context == "surface" || context == "volume" || context == "displacement")
|
||||
return "Material";
|
||||
if (context == "light" || context == "lightFilter")
|
||||
return "Light";
|
||||
|
||||
// MaterialX/Arnold terminals often lack a context but type their outputs
|
||||
// as surfaceshader/volumeshader/displacementshader/closure.
|
||||
for (const pxr::TfToken& outName : node->GetShaderOutputNames()) {
|
||||
if (const auto* prop = node->GetShaderOutput(outName)) {
|
||||
const std::string outType = pxr::TfStringify(prop->GetType());
|
||||
if (outType.find("shader") != std::string::npos ||
|
||||
outType == "terminal" || outType == "closure")
|
||||
return "Material";
|
||||
}
|
||||
}
|
||||
|
||||
const std::string role = pxr::TfStringify(node->GetRole());
|
||||
if (role == "texture") return "Texture";
|
||||
if (role == "primvar") return "Geometry";
|
||||
if (role == "math") return "Utility";
|
||||
|
||||
const std::string name = pxr::TfStringToLower(
|
||||
node->GetIdentifier().GetString() + " " + node->GetName());
|
||||
for (const char* kw : {"texture", "image", "checker", "noise", "ramp",
|
||||
"fractal", "worley", "voronoi", "triplanar"})
|
||||
if (name.find(kw) != std::string::npos) return "Texture";
|
||||
for (const char* kw : {"primvar", "texcoord", "geomprop", "position",
|
||||
"normal", "tangent", "bitangent", "uv_"})
|
||||
if (name.find(kw) != std::string::npos) return "Geometry";
|
||||
|
||||
return "Utility";
|
||||
}
|
||||
|
||||
// Fixed display order for the known buckets; anything unexpected sorts after.
|
||||
int CategoryRank(const std::string& category) {
|
||||
if (category == "Material") return 0;
|
||||
if (category == "Texture") return 1;
|
||||
if (category == "Geometry") return 2;
|
||||
if (category == "Utility") return 3;
|
||||
if (category == "Light") return 4;
|
||||
return 5;
|
||||
}
|
||||
|
||||
// Top-level group from the Sdr source type. glslfx and OSL collapse into one
|
||||
// "USD" group — the core usd* nodes register under both parsers and would
|
||||
// otherwise show up twice under different headers.
|
||||
std::string DeriveSource(pxr::SdrShaderNodeConstPtr node) {
|
||||
std::string st = pxr::TfStringToLower(pxr::TfStringify(node->GetSourceType()));
|
||||
if (st == "glslfx" || st == "osl") return "USD";
|
||||
if (st == "mtlx") return "MaterialX";
|
||||
if (st == "arnold") return "Arnold";
|
||||
if (st == "cycles") return "Cycles";
|
||||
if (st.empty()) return "Other";
|
||||
st[0] = static_cast<char>(std::toupper(static_cast<unsigned char>(st[0])));
|
||||
return st;
|
||||
}
|
||||
|
||||
int SourceRank(const std::string& source) {
|
||||
if (source == "USD") return 0;
|
||||
if (source == "MaterialX") return 1;
|
||||
return 2; // renderers and anything else, alphabetical among themselves
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
const std::vector<ShaderNodeTypeInfo>& MaterialManager::GetAvailableShaderNodes() {
|
||||
if (m_shaderNodeCacheBuilt)
|
||||
return m_shaderNodeCache;
|
||||
@@ -24,12 +100,19 @@ const std::vector<ShaderNodeTypeInfo>& MaterialManager::GetAvailableShaderNodes(
|
||||
info.identifier = node->GetIdentifier().GetString();
|
||||
info.label = !node->GetLabel().IsEmpty() ? node->GetLabel().GetString() : node->GetName();
|
||||
info.family = node->GetFamily().GetString();
|
||||
info.category = DeriveCategory(node);
|
||||
info.source = DeriveSource(node);
|
||||
m_shaderNodeCache.push_back(std::move(info));
|
||||
}
|
||||
|
||||
std::sort(m_shaderNodeCache.begin(), m_shaderNodeCache.end(),
|
||||
[](const ShaderNodeTypeInfo& a, const ShaderNodeTypeInfo& b) {
|
||||
if (a.family != b.family) return a.family < b.family;
|
||||
const int sa = SourceRank(a.source), sb = SourceRank(b.source);
|
||||
if (sa != sb) return sa < sb;
|
||||
if (a.source != b.source) return a.source < b.source;
|
||||
const int ra = CategoryRank(a.category), rb = CategoryRank(b.category);
|
||||
if (ra != rb) return ra < rb;
|
||||
if (a.category != b.category) return a.category < b.category;
|
||||
return a.label < b.label;
|
||||
});
|
||||
|
||||
|
||||
@@ -15,7 +15,15 @@ namespace UsdLayerManager {
|
||||
struct ShaderNodeTypeInfo {
|
||||
std::string identifier; ///< Sdr identifier, authored as info:id
|
||||
std::string label; ///< display label for the create-node menu
|
||||
std::string family; ///< grouping for the create-node menu (may be empty)
|
||||
std::string family; ///< Sdr family (parser-provided, may be empty)
|
||||
/// Coarse Hypershade-style bucket ("Material", "Texture", "Geometry",
|
||||
/// "Utility", "Light") derived from Sdr context/role/output types; the
|
||||
/// create-node list groups by it. Never empty.
|
||||
std::string category;
|
||||
/// Top-level create-list group derived from the Sdr source type:
|
||||
/// "USD" (glslfx/OSL core nodes), "MaterialX", "Arnold", "Cycles", or a
|
||||
/// capitalized raw source type. Never empty.
|
||||
std::string source;
|
||||
};
|
||||
|
||||
/// One input or output pin on a shader node, with its USD value type so
|
||||
|
||||
Reference in New Issue
Block a user