diff --git a/documents/Specification/MaterialX.Proposals.md b/documents/Specification/MaterialX.Proposals.md index b96c688f25..d387d28718 100644 --- a/documents/Specification/MaterialX.Proposals.md +++ b/documents/Specification/MaterialX.Proposals.md @@ -188,7 +188,25 @@ We have a standard 3d fractal noise, but a 2d variant would be useful as well. * `period` (float or vector3): the positive integer distance at which the noise function returns the same value for input coordinate repeated at that step. Default is 0, meaning the noise is not periodic. * `in` (float): the 1D coordinate at which the noise is evaluated. + +Expanded 2D Worley noise to support different distance metrics and periodicity. + +* **`worleynoise2d`**: 2D Worley noise using centered jitter, outputting float (distance metric to closest feature), vector2 (distance metrics to closest 2 features) or vector3 (distance metrics to closest 3 features). + * `metric` (uniform string): the distance metric to return, one of "distance" (Euclidean distance to feature), "distance2" (Euclidean distance squared), "manhattan" or "chebyshev". Default is "distance". + * `period` (float or vector3): the positive integer distance at which the noise function returns the same value for texture coordinates repeated at that step. Default is 0, meaning the noise is not periodic. + + + +Expanded 3D Worley noise to support different distance metrics and periodicity. + +* **`worleynoise3d`**: 3D Worley noise using centered jitter, outputting float (distance metric to closest feature), vector2 (distance metrics to closest 2 features) or vector3 (distance metrics to closest 3 features). + * `metric` (uniform string): the distance metric to return, one of "distance" (Euclidean distance to feature), "distance2" (Euclidean distance squared), "manhattan" or "chebyshev". Default is "distance". + * `period` (float or vector3): the positive integer distance at which the noise function returns the same value for position coordinates repeated at that step. Default is 0, meaning the noise is not periodic. + +#### Periodic Noises + +In #1201 it was decided that separate periodic versions of all of the noises is preferred to adding it to the existing noises. ### Shape Nodes diff --git a/documents/Specification/MaterialX.Specification.md b/documents/Specification/MaterialX.Specification.md index 070587340c..65027694d8 100644 --- a/documents/Specification/MaterialX.Specification.md +++ b/documents/Specification/MaterialX.Specification.md @@ -887,16 +887,12 @@ Standard Noise nodes: * **`worleynoise2d`**: 2D Worley noise using centered jitter, outputting float (distance metric to closest feature), vector2 (distance metrics to closest 2 features) or vector3 (distance metrics to closest 3 features). - * `metric` (uniform string): the distance metric to return, one of "distance" (Euclidean distance to feature), "distance2" (Euclidean distance squared), "manhattan" or "chebyshev". Default is "distance". - * `period` (float or vector3): the positive integer distance at which the noise function returns the same value for texture coordinates repeated at that step. Default is 0, meaning the noise is not periodic. * `jitter` (float): amount to jitter the cell center position, with smaller values creating a more regular pattern. Default is 1.0. * `texcoord` (vector2): the 2D position at which the noise is evaluated. Default is to use the first set of texture coordinates. * **`worleynoise3d`**: 3D Worley noise using centered jitter, outputting float (distance metric to closest feature), vector2 (distance metrics to closest 2 features) or vector3 (distance metrics to closest 3 features). - * `metric` (uniform string): the distance metric to return, one of "distance" (Euclidean distance to feature), "distance2" (Euclidean distance squared), "manhattan" or "chebyshev". Default is "distance". - * `period` (float or vector3): the positive integer distance at which the noise function returns the same value for position coordinates repeated at that step. Default is 0, meaning the noise is not periodic. * `jitter` (float): amount to jitter the cell center position, with smaller values creating a more regular pattern. Default is 1.0. * `position` (vector3): the 3D position at which the noise is evaluated. Default is to use the current 3D object-space coordinate. diff --git a/libraries/bxdf/translation/standard_surface_to_usd.mtlx b/libraries/bxdf/translation/standard_surface_to_usd.mtlx index 099465e369..51c7d5fc6e 100644 --- a/libraries/bxdf/translation/standard_surface_to_usd.mtlx +++ b/libraries/bxdf/translation/standard_surface_to_usd.mtlx @@ -80,10 +80,14 @@ - + + + + + diff --git a/libraries/bxdf/usd_preview_surface.mtlx b/libraries/bxdf/usd_preview_surface.mtlx index d99fab3a8c..a521c756bc 100644 --- a/libraries/bxdf/usd_preview_surface.mtlx +++ b/libraries/bxdf/usd_preview_surface.mtlx @@ -152,16 +152,10 @@ - - - - - - - + @@ -176,12 +170,8 @@ - - - - - + @@ -223,12 +213,8 @@ - - - - - + diff --git a/source/JsMaterialX/JsMaterialXCore/JsValue.cpp b/source/JsMaterialX/JsMaterialXCore/JsValue.cpp index d0ddba7e17..5e45ef16cd 100644 --- a/source/JsMaterialX/JsMaterialXCore/JsValue.cpp +++ b/source/JsMaterialX/JsMaterialXCore/JsValue.cpp @@ -47,7 +47,7 @@ EMSCRIPTEN_BINDINGS(value) .function("copy", &mx::Value::copy, ems::pure_virtual()) .function("getTypeString", &mx::Value::getTypeString) .function("getValueString", &mx::Value::getValueString) - .class_function("createValueFromStrings", &mx::Value::createValueFromStrings) + BIND_CLASS_FUNC("createValueFromStrings", mx::Value, createValueFromStrings, 2, 3, stRef, stRef, mx::ConstTypeDefPtr) .class_function("setFloatFormat", &mx::Value::setFloatFormat) .class_function("setFloatPrecision", &mx::Value::setFloatPrecision) .class_function("getFloatFormat", &mx::Value::getFloatFormat) diff --git a/source/MaterialXCore/Definition.cpp b/source/MaterialXCore/Definition.cpp index 22c111f03c..da6c3ca3af 100644 --- a/source/MaterialXCore/Definition.cpp +++ b/source/MaterialXCore/Definition.cpp @@ -196,4 +196,12 @@ vector UnitTypeDef::getUnitDefs() const return unitDefs; } +ValuePtr AttributeDef::getValue() const +{ + if (!hasValue()) + return ValuePtr(); + + return Value::createValueFromStrings(getValueString(), getType(), getDocument()->getTypeDef(getType())); +} + MATERIALX_NAMESPACE_END diff --git a/source/MaterialXCore/Definition.h b/source/MaterialXCore/Definition.h index 6498f21c89..0ce7761b6d 100644 --- a/source/MaterialXCore/Definition.h +++ b/source/MaterialXCore/Definition.h @@ -423,11 +423,11 @@ class MX_CORE_API TargetDef : public TypedElement /// @class Member /// A member element within a TypeDef. -class MX_CORE_API Member : public TypedElement +class MX_CORE_API Member : public ValueElement { public: Member(ElementPtr parent, const string& name) : - TypedElement(parent, CATEGORY, name) + ValueElement(parent, CATEGORY, name) { } virtual ~Member() { } @@ -625,12 +625,7 @@ class MX_CORE_API AttributeDef : public TypedElement /// /// @return A shared pointer to the typed value of this element, or an /// empty shared pointer if no value is present. - ValuePtr getValue() const - { - if (!hasValue()) - return ValuePtr(); - return Value::createValueFromStrings(getValueString(), getType()); - } + ValuePtr getValue() const; /// @} /// @name Elements diff --git a/source/MaterialXCore/Element.cpp b/source/MaterialXCore/Element.cpp index af47a6a2ba..648d7df000 100644 --- a/source/MaterialXCore/Element.cpp +++ b/source/MaterialXCore/Element.cpp @@ -638,6 +638,22 @@ string ValueElement::getResolvedValueString(StringResolverPtr resolver) const return resolver->resolve(getValueString(), getType()); } +ValuePtr ValueElement::getValue() const +{ + if (!hasValue()) + return ValuePtr(); + + return Value::createValueFromStrings(getValueString(), getType(), getDocument()->getTypeDef(getType())); +} + +ValuePtr ValueElement::getResolvedValue(StringResolverPtr resolver) const +{ + if (!hasValue()) + return ValuePtr(); + + return Value::createValueFromStrings(getResolvedValueString(resolver), getType(), getDocument()->getTypeDef(getType())); +} + ValuePtr ValueElement::getDefaultValue() const { ConstElementPtr parent = getParent(); diff --git a/source/MaterialXCore/Element.h b/source/MaterialXCore/Element.h index a80bc3a49f..fb4bbe07ce 100644 --- a/source/MaterialXCore/Element.h +++ b/source/MaterialXCore/Element.h @@ -1037,12 +1037,7 @@ class MX_CORE_API ValueElement : public TypedElement /// /// @return A shared pointer to the typed value of this element, or an /// empty shared pointer if no value is present. - ValuePtr getValue() const - { - if (!hasValue()) - return ValuePtr(); - return Value::createValueFromStrings(getValueString(), getType()); - } + ValuePtr getValue() const; /// Return the resolved value of an element as a generic value object, which /// may be queried to access its data. @@ -1052,12 +1047,7 @@ class MX_CORE_API ValueElement : public TypedElement /// will be created at this scope and applied to the return value. /// @return A shared pointer to the typed value of this element, or an /// empty shared pointer if no value is present. - ValuePtr getResolvedValue(StringResolverPtr resolver = nullptr) const - { - if (!hasValue()) - return ValuePtr(); - return Value::createValueFromStrings(getResolvedValueString(resolver), getType()); - } + ValuePtr getResolvedValue(StringResolverPtr resolver = nullptr) const; /// Return the default value for this element as a generic value object, which /// may be queried to access its data. diff --git a/source/MaterialXCore/Exception.h b/source/MaterialXCore/Exception.h index 4fc0c728ec..4cf51f1cd5 100644 --- a/source/MaterialXCore/Exception.h +++ b/source/MaterialXCore/Exception.h @@ -50,6 +50,14 @@ class MX_CORE_API Exception : public std::exception string _msg; }; +/// @class ExceptionTypeError +/// An exception that is thrown when a type mismatch is encountered. +class MX_CORE_API ExceptionTypeError : public Exception +{ + public: + using Exception::Exception; +}; + MATERIALX_NAMESPACE_END #endif diff --git a/source/MaterialXCore/Value.cpp b/source/MaterialXCore/Value.cpp index 68da4b5b9d..bb70aa04ae 100644 --- a/source/MaterialXCore/Value.cpp +++ b/source/MaterialXCore/Value.cpp @@ -3,6 +3,8 @@ // SPDX-License-Identifier: Apache-2.0 // +#include +#include #include #include @@ -267,12 +269,18 @@ template ValuePtr TypedValue::createFromString(const string& value) // Value methods // -ValuePtr Value::createValueFromStrings(const string& value, const string& type) +ValuePtr Value::createValueFromStrings(const string& value, const string& type, ConstTypeDefPtr typeDef) { CreatorMap::iterator it = _creatorMap.find(type); if (it != _creatorMap.end()) return it->second(value); + if (typeDef && !typeDef->getMembers().empty()) + { + // If we're given a TypeDef pointer that has child members, then we can create a new AggregateValue. + return AggregateValue::createAggregateValueFromString(value, type, typeDef); + } + return TypedValue::createFromString(value); } @@ -291,6 +299,69 @@ template const T& Value::asA() const return typedVal->getData(); } +template <> +MX_CORE_API bool Value::isA() const +{ + return dynamic_cast(this) != nullptr; +} + +template <> +MX_CORE_API const AggregateValue& Value::asA() const +{ + const AggregateValue* typedVal = dynamic_cast(this); + if (!typedVal) + { + throw ExceptionTypeError("Incorrect type specified for value"); + } + return *typedVal; +} + +string AggregateValue::getValueString() const +{ + if (_data.empty()) + return EMPTY_STRING; + + std::string result = "{"; + std::string separator = ""; + for (const auto& val : _data) + { + result += separator + val->getValueString(); + separator = ";"; + } + result += "}"; + + return result; +} + +AggregateValuePtr AggregateValue::createAggregateValueFromString(const string& value, const string& type, ConstTypeDefPtr typeDef) +{ + StringVec subValues = parseStructValueString(value); + + AggregateValuePtr result = AggregateValue::createAggregateValue(type); + const auto& members = typeDef->getMembers(); + + if (subValues.size() != members.size()) + { + std::stringstream ss; + ss << "Wrong number of initializers - expect " << members.size(); + throw Exception(ss.str()); + } + + auto doc = typeDef->getDocument(); + for (size_t i = 0; i < members.size(); ++i) + { + const auto& member = members[i]; + + // This will return nullptr if the type is not a listed typedef. + ConstTypeDefPtr subTypeDef = doc->getTypeDef(members[i]->getType()); + + // Calling Value::createValueFromStrings() here allows support for recursively nested structs. + result->appendValue(Value::createValueFromStrings(subValues[i], member->getType(), subTypeDef)); + } + + return result; +} + ScopedFloatFormatting::ScopedFloatFormatting(Value::FloatFormat format, int precision) : _format(Value::getFloatFormat()), _precision(Value::getFloatPrecision()) diff --git a/source/MaterialXCore/Value.h b/source/MaterialXCore/Value.h index 764a0ccc01..4dae4ab135 100644 --- a/source/MaterialXCore/Value.h +++ b/source/MaterialXCore/Value.h @@ -24,21 +24,22 @@ using BoolVec = vector; using FloatVec = vector; class Value; +class AggregateValue; /// A shared pointer to a Value using ValuePtr = shared_ptr; /// A shared pointer to a const Value using ConstValuePtr = shared_ptr; -template class TypedValue; +/// A shared pointer to an Aggregate Value +using AggregateValuePtr = shared_ptr; +/// A shared pointer to a const Aggregate Value +using ConstAggregateValuePtr = shared_ptr; -/// @class ExceptionTypeError -/// An exception that is thrown when a type mismatch is encountered. -class MX_CORE_API ExceptionTypeError : public Exception -{ - public: - using Exception::Exception; -}; +class TypeDef; +using ConstTypeDefPtr = shared_ptr; + +template class TypedValue; /// A generic, discriminated value, whose type may be queried dynamically. class MX_CORE_API Value @@ -73,7 +74,7 @@ class MX_CORE_API Value /// Create a new value instance from value and type strings. /// @return A shared pointer to a typed value, or an empty shared pointer /// if the conversion to the given data type cannot be performed. - static ValuePtr createValueFromStrings(const string& value, const string& type); + static ValuePtr createValueFromStrings(const string& value, const string& type, ConstTypeDefPtr typeDef = nullptr); /// Create a deep copy of the value. virtual ValuePtr copy() const = 0; @@ -193,6 +194,68 @@ template class MX_CORE_API TypedValue : public Value T _data; }; +/// A subclass for aggregate values with multiple members +class MX_CORE_API AggregateValue : public Value +{ + public: + AggregateValue(const string& typeName) : + _typeName(typeName) + { + } + virtual ~AggregateValue() { } + + /// Create a deep copy of the value. + ValuePtr copy() const override + { + auto result = createAggregateValue(_typeName); + for (const auto& val : _data) + { + result->appendValue(val->copy()); + } + return result; + } + + /// Append a member value to the aggregate. + void appendValue(ConstValuePtr valuePtr) + { + _data.emplace_back(valuePtr); + } + + const vector& getMembers() const + { + return _data; + } + + /// Query an indexed member value from the aggregate. + ConstValuePtr getMemberValue(size_t index) const + { + return _data[index]; + } + + /// Return type string. + const string& getTypeString() const override { return _typeName; } + + /// Return value string. + string getValueString() const override; + + // + // Static helper methods + // + + /// Create a new value from an object of any valid MaterialX type. + static AggregateValuePtr createAggregateValue(const string& typeName) + { + return std::make_shared(typeName); + } + + static AggregateValuePtr createAggregateValueFromString(const string& value, const string& type, ConstTypeDefPtr typeDefPtr); + + private: + const string _typeName; + + vector _data; +}; + /// @class ScopedFloatFormatting /// An RAII class for controlling the float formatting of values. class MX_CORE_API ScopedFloatFormatting diff --git a/source/MaterialXGenGlsl/GlslSyntax.cpp b/source/MaterialXGenGlsl/GlslSyntax.cpp index f91305f1a6..651a4ab5ff 100644 --- a/source/MaterialXGenGlsl/GlslSyntax.cpp +++ b/source/MaterialXGenGlsl/GlslSyntax.cpp @@ -375,4 +375,41 @@ bool GlslSyntax::remapEnumeration(const string& value, TypeDesc type, const stri return true; } +StructTypeSyntaxPtr GlslSyntax::createStructSyntax(const string& structTypeName, const string& defaultValue, + const string& uniformDefaultValue, const string& typeAlias, + const string& typeDefinition) const +{ + return std::make_shared( + this, + structTypeName, + defaultValue, + uniformDefaultValue, + typeAlias, + typeDefinition); +} + +string GlslStructTypeSyntax::getValue(const Value& value, bool /* uniform */) const +{ + const AggregateValue& aggValue = static_cast(value); + + string result = aggValue.getTypeString() + "("; + + string separator = ""; + for (const auto& memberValue : aggValue.getMembers()) + { + result += separator; + separator = ","; + + auto memberTypeName = memberValue->getTypeString(); + auto memberTypeDesc = TypeDesc::get(memberTypeName); + + // Recursively use the syntax to generate the output, so we can supported nested structs. + result += _parentSyntax->getValue(memberTypeDesc, *memberValue, true); + } + + result += ")"; + + return result; +} + MATERIALX_NAMESPACE_END diff --git a/source/MaterialXGenGlsl/GlslSyntax.h b/source/MaterialXGenGlsl/GlslSyntax.h index 8408534e7e..1e82e354bd 100644 --- a/source/MaterialXGenGlsl/GlslSyntax.h +++ b/source/MaterialXGenGlsl/GlslSyntax.h @@ -45,6 +45,20 @@ class MX_GENGLSL_API GlslSyntax : public Syntax static const StringVec VEC2_MEMBERS; static const StringVec VEC3_MEMBERS; static const StringVec VEC4_MEMBERS; + + protected: + StructTypeSyntaxPtr createStructSyntax(const string& structTypeName, const string& defaultValue, + const string& uniformDefaultValue, const string& typeAlias, + const string& typeDefinition) const override; +}; + +/// Specialization of TypeSyntax for aggregate types. +class MX_GENGLSL_API GlslStructTypeSyntax : public StructTypeSyntax +{ + public: + using StructTypeSyntax::StructTypeSyntax; + + string getValue(const Value& value, bool uniform) const override; }; MATERIALX_NAMESPACE_END diff --git a/source/MaterialXGenMdl/mdl/materialx/stdlib_1_8.mdl b/source/MaterialXGenMdl/mdl/materialx/stdlib_1_8.mdl index bef1e430d4..bb6eaf157a 100644 --- a/source/MaterialXGenMdl/mdl/materialx/stdlib_1_8.mdl +++ b/source/MaterialXGenMdl/mdl/materialx/stdlib_1_8.mdl @@ -441,3 +441,23 @@ export float3 mx_viewdirection_vector3( ::state::coordinate_object, internal_space_direction)); } + + +export float3 mx_extractGroup( + uniform texcoordGroup_struct in = {{0.1,0.1},{0.2,0.2},{0.3,0.3}}, + uniform int index = 0 +) + [[ + anno::description("Node Group: experimental") + ]] +{ + result = in.st_0.ss; + + if (index == 1) + result = in.st_1.ss; + else if (index == 2) + result = in.st_2.ss; + + return result; +} + diff --git a/source/MaterialXGenOsl/OslShaderGenerator.cpp b/source/MaterialXGenOsl/OslShaderGenerator.cpp index f29719a39b..d0d9d96cba 100644 --- a/source/MaterialXGenOsl/OslShaderGenerator.cpp +++ b/source/MaterialXGenOsl/OslShaderGenerator.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include #include @@ -510,10 +511,10 @@ void OslShaderGenerator::emitMetadata(const ShaderPort* port, ShaderStage& stage { static const std::unordered_map UI_WIDGET_METADATA = { - { Type::FLOAT, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING.getName())) }, - { Type::INTEGER, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("number", Type::STRING.getName())) }, - { Type::FILENAME, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("filename", Type::STRING.getName())) }, - { Type::BOOLEAN, ShaderMetadata("widget", Type::STRING, Value::createValueFromStrings("checkBox", Type::STRING.getName())) } + { Type::FLOAT, ShaderMetadata("widget", Type::STRING, Type::STRING.createValueFromStrings("number")) }, + { Type::INTEGER, ShaderMetadata("widget", Type::STRING, Type::STRING.createValueFromStrings("number")) }, + { Type::FILENAME, ShaderMetadata("widget", Type::STRING, Type::STRING.createValueFromStrings("filename")) }, + { Type::BOOLEAN, ShaderMetadata("widget", Type::STRING, Type::STRING.createValueFromStrings("checkBox")) } }; static const std::set METADATA_TYPE_BLACKLIST = diff --git a/source/MaterialXGenShader/ShaderGenerator.cpp b/source/MaterialXGenShader/ShaderGenerator.cpp index 6c23382f81..398232bf39 100644 --- a/source/MaterialXGenShader/ShaderGenerator.cpp +++ b/source/MaterialXGenShader/ShaderGenerator.cpp @@ -344,6 +344,47 @@ ShaderNodeImplPtr ShaderGenerator::getImplementation(const NodeDef& nodedef, Gen return impl; } +/// Load any struct type definitions from the document in to the type cache. +void ShaderGenerator::loadStructTypeDefs(const DocumentPtr& doc) +{ + for (const auto& mxTypeDef : doc->getTypeDefs()) + { + const auto& typeDefName = mxTypeDef->getName(); + const auto& members = mxTypeDef->getMembers(); + + // If we don't have any member children then we're not going to consider ourselves a struct. + if (members.empty()) + continue; + + StructTypeDesc newStructTypeDesc; + for (const auto& member : members) + { + auto memberName = member->getName(); + auto memberTypeName = member->getType(); + auto memberType = TypeDesc::get(memberTypeName); + auto memberDefaultValue = member->getValueString(); + + newStructTypeDesc.addMember(memberName, memberType, memberDefaultValue); + } + + auto structIndex = StructTypeDesc::emplace_back(newStructTypeDesc); + + TypeDesc structTypeDesc(typeDefName, TypeDesc::BASETYPE_STRUCT, TypeDesc::SEMANTIC_NONE, 1, structIndex); + + TypeDescRegistry(structTypeDesc, typeDefName); + + StructTypeDesc::get(structIndex).setTypeDesc(TypeDesc::get(typeDefName)); + } + + _syntax->registerStructTypeDescSyntax(); +} + +/// Clear any struct type definitions loaded +void ShaderGenerator::clearStructTypeDefs() +{ + StructTypeDesc::clear(); +} + namespace { diff --git a/source/MaterialXGenShader/ShaderGenerator.h b/source/MaterialXGenShader/ShaderGenerator.h index fa60a4d63e..a2f54fec2e 100644 --- a/source/MaterialXGenShader/ShaderGenerator.h +++ b/source/MaterialXGenShader/ShaderGenerator.h @@ -191,6 +191,12 @@ class MX_GENSHADER_API ShaderGenerator return _tokenSubstitutions; } + /// Load any struct type definitions from the document in to the type cache. + void loadStructTypeDefs(const DocumentPtr& doc); + + /// Clear any struct type definitions loaded + void clearStructTypeDefs(); + /// Register metadata that should be exported to the generated shaders. /// Supported metadata includes standard UI attributes like "uiname", "uifolder", /// "uimin", "uimax", etc. diff --git a/source/MaterialXGenShader/ShaderNode.cpp b/source/MaterialXGenShader/ShaderNode.cpp index 4e90fcbb2d..d52810ffc4 100644 --- a/source/MaterialXGenShader/ShaderNode.cpp +++ b/source/MaterialXGenShader/ShaderNode.cpp @@ -437,7 +437,7 @@ void ShaderNode::createMetadata(const NodeDef& nodeDef, GenContext& context) const string& attrValue = nodeDef.getAttribute(nodedefAttr); if (!attrValue.empty()) { - ValuePtr value = Value::createValueFromStrings(attrValue, metadataEntry->type.getName()); + ValuePtr value = metadataEntry->type.createValueFromStrings(attrValue); if (!value) { value = metadataEntry->value; @@ -472,7 +472,7 @@ void ShaderNode::createMetadata(const NodeDef& nodeDef, GenContext& context) if (!attrValue.empty()) { const TypeDesc type = metadataEntry->type != Type::NONE ? metadataEntry->type : input->getType(); - ValuePtr value = Value::createValueFromStrings(attrValue, type.getName()); + ValuePtr value = type.createValueFromStrings(attrValue); if (!value) { value = metadataEntry->value; diff --git a/source/MaterialXGenShader/Syntax.cpp b/source/MaterialXGenShader/Syntax.cpp index 661aa41660..b8eed3e9b8 100644 --- a/source/MaterialXGenShader/Syntax.cpp +++ b/source/MaterialXGenShader/Syntax.cpp @@ -190,6 +190,36 @@ bool Syntax::remapEnumeration(const string&, TypeDesc, const string&, std::pair< return false; } +void Syntax::registerStructTypeDescSyntax() +{ + for (const auto& typeName : StructTypeDesc::getStructTypeNames()) + { + const auto& typeDesc = TypeDesc::get(typeName); + const auto& structTypeDesc = StructTypeDesc::get(typeDesc.getStructIndex()); + + string structTypeName = typeName; + string defaultValue = typeName + "( "; + string uniformDefaultValue = EMPTY_STRING; + string typeAlias = EMPTY_STRING; + string typeDefinition = "struct " + structTypeName + " { "; + + for (const auto& x : structTypeDesc.getMembers()) + { + string memberName = x._name; + string memberType = x._typeDesc.getName(); + string memberDefaultValue = x._defaultValueStr; + + defaultValue += memberDefaultValue + ", "; + typeDefinition += memberType + " " + memberName + "; "; + } + + typeDefinition += " };"; + defaultValue += " )"; + + registerTypeSyntax(typeDesc, createStructSyntax(structTypeName, defaultValue, uniformDefaultValue, typeAlias, typeDefinition)); + } +} + const StringVec TypeSyntax::EMPTY_MEMBERS; TypeSyntax::TypeSyntax(const string& name, const string& defaultValue, const string& uniformDefaultValue, @@ -246,4 +276,39 @@ string AggregateTypeSyntax::getValue(const Value& value, bool /*uniform*/) const return valueString.empty() ? valueString : getName() + "(" + valueString + ")"; } +StructTypeSyntax::StructTypeSyntax(const Syntax* parentSyntax, const string& name, const string& defaultValue, const string& uniformDefaultValue, + const string& typeAlias, const string& typeDefinition, const StringVec& members) : + TypeSyntax(name, defaultValue, uniformDefaultValue, typeAlias, typeDefinition, members), _parentSyntax(parentSyntax) +{ +} + +string StructTypeSyntax::getValue(const Value& value, bool /*uniform*/) const +{ + const AggregateValue& aggValue = static_cast(value); + + auto typeDesc = TypeDesc::get(aggValue.getTypeString()); + auto structTypeDesc = StructTypeDesc::get(typeDesc.getStructIndex()); + + string result = "{"; + + string separator = ""; + for (const auto& memberValue : aggValue.getMembers()) + { + result += separator; + separator = ";"; + + auto memberTypeName = memberValue->getTypeString(); + auto memberTypeDesc = TypeDesc::get(memberTypeName); + + // Recursively use the syntax to generate the output, so we can support nested structs. + const string valueStr = _parentSyntax->getValue(memberTypeDesc, *memberValue, true); + + result += valueStr; + } + + result += "}"; + + return result; +} + MATERIALX_NAMESPACE_END diff --git a/source/MaterialXGenShader/Syntax.h b/source/MaterialXGenShader/Syntax.h index a4bb7e49b5..6f16633b59 100644 --- a/source/MaterialXGenShader/Syntax.h +++ b/source/MaterialXGenShader/Syntax.h @@ -20,6 +20,7 @@ MATERIALX_NAMESPACE_BEGIN class Syntax; class TypeSyntax; +class StructTypeSyntax; class TypeDesc; class ShaderPort; @@ -29,6 +30,8 @@ using SyntaxPtr = shared_ptr; using ConstSyntaxPtr = shared_ptr; /// Shared pointer to a TypeSyntax using TypeSyntaxPtr = shared_ptr; +/// Shared pointer to a StructTypeSyntax +using StructTypeSyntaxPtr = shared_ptr; /// Map holding identifier names and a counter for /// creating unique names from them. @@ -67,6 +70,8 @@ class MX_GENSHADER_API Syntax /// Multiple calls will add to the internal set of tokens. void registerInvalidTokens(const StringMap& tokens); + virtual void registerStructTypeDescSyntax(); + /// Returns a set of names that are reserved words for this language syntax. const StringSet& getReservedWords() const { return _reservedWords; } @@ -199,6 +204,19 @@ class MX_GENSHADER_API Syntax /// Protected constructor Syntax(); + virtual StructTypeSyntaxPtr createStructSyntax(const string& structTypeName, const string& defaultValue, + const string& uniformDefaultValue, const string& typeAlias, + const string& typeDefinition) const + { + return std::make_shared( + this, + structTypeName, + defaultValue, + uniformDefaultValue, + typeAlias, + typeDefinition); + } + vector _typeSyntaxes; std::unordered_map _typeSyntaxIndexByType; @@ -292,6 +310,20 @@ class MX_GENSHADER_API AggregateTypeSyntax : public TypeSyntax string getValue(const Value& value, bool uniform) const override; }; +/// Specialization of TypeSyntax for aggregate types. +class MX_GENSHADER_API StructTypeSyntax : public TypeSyntax +{ + public: + StructTypeSyntax(const Syntax* parentSyntax, const string& name, const string& defaultValue, const string& uniformDefaultValue, + const string& typeAlias = EMPTY_STRING, const string& typeDefinition = EMPTY_STRING, + const StringVec& members = EMPTY_MEMBERS); + + string getValue(const Value& value, bool uniform) const override; + + protected: + const Syntax* _parentSyntax; +}; + MATERIALX_NAMESPACE_END #endif diff --git a/source/MaterialXGenShader/TypeDesc.cpp b/source/MaterialXGenShader/TypeDesc.cpp index 1267caf14d..5d51680211 100644 --- a/source/MaterialXGenShader/TypeDesc.cpp +++ b/source/MaterialXGenShader/TypeDesc.cpp @@ -5,7 +5,7 @@ #include -#include +#include MATERIALX_NAMESPACE_BEGIN @@ -28,6 +28,16 @@ TypeDescNameMap& typeNameMap() return map; } +using StructTypeDescStorage = vector; +StructTypeDescStorage& structTypeStorage() +{ + // TODO: Our use of the singleton pattern for TypeDescMap and StructTypeDestStorage + // is not thread-safe, and we should consider replacing this with thread-local + // data in the GenContext object. + static StructTypeDescStorage storage; + return storage; +} + } // anonymous namespace const string TypeDesc::NONE_TYPE_NAME = "none"; @@ -46,7 +56,52 @@ TypeDesc TypeDesc::get(const string& name) return it != types.end() ? it->second : Type::NONE; } -TypeDescRegistry::TypeDescRegistry(TypeDesc type, const std::string& name) +void TypeDesc::remove(const string& name) +{ + TypeDescNameMap& typenames = typeNameMap(); + + TypeDescMap& types = typeMap(); + + auto it = types.find(name); + if (it == types.end()) + return; + + typenames.erase(it->second.typeId()); + types.erase(it); +} + +ValuePtr TypeDesc::createValueFromStrings(const string& value) const +{ + ValuePtr newValue = Value::createValueFromStrings(value, getName()); + if (!isStruct()) + return newValue; + + // Value::createValueFromStrings() can only create a valid Value for a struct if it is passed + // the optional TypeDef argument, otherwise it just returns a "string" typed Value. + // So if this is a struct type we need to create a new AggregateValue. + + StringVec subValues = parseStructValueString(value); + + AggregateValuePtr result = AggregateValue::createAggregateValue(getName()); + auto structTypeDesc = StructTypeDesc::get(getStructIndex()); + const auto& members = structTypeDesc.getMembers(); + + if (subValues.size() != members.size()) + { + std::stringstream ss; + ss << "Wrong number of initializers - expect " << members.size(); + throw ExceptionShaderGenError(ss.str()); + } + + for (size_t i = 0; i < members.size(); ++i) + { + result->appendValue( members[i]._typeDesc.createValueFromStrings(subValues[i])); + } + + return result; +} + +TypeDescRegistry::TypeDescRegistry(TypeDesc type, const string& name) { TypeDescMap& types = typeMap(); TypeDescNameMap& typenames = typeNameMap(); @@ -86,4 +141,74 @@ TYPEDESC_REGISTER_TYPE(MATERIAL, "material") } // namespace Type +// +// StructTypeDesc methods +// + +void StructTypeDesc::addMember(const string& name, TypeDesc type, string defaultValueStr) +{ + _members.emplace_back(StructTypeDesc::StructMemberTypeDesc(name, type, defaultValueStr)); +} + +vector StructTypeDesc::getStructTypeNames() +{ + StructTypeDescStorage& structs = structTypeStorage(); + vector structNames; + for (const auto& x : structs) + { + structNames.emplace_back(x.typeDesc().getName()); + } + return structNames; +} + +StructTypeDesc& StructTypeDesc::get(unsigned int index) +{ + StructTypeDescStorage& structs = structTypeStorage(); + return structs[index]; +} + +uint16_t StructTypeDesc::emplace_back(StructTypeDesc structTypeDesc) +{ + StructTypeDescStorage& structs = structTypeStorage(); + if (structs.size() >= std::numeric_limits::max()) + { + throw ExceptionShaderGenError("Maximum number of custom struct types has been exceeded."); + } + uint16_t index = static_cast(structs.size()); + structs.emplace_back(structTypeDesc); + return index; +} + +void StructTypeDesc::clear() +{ + StructTypeDescStorage& structs = structTypeStorage(); + for (const auto& structType: structs) + { + // Need to add typeID to structTypeDesc - and use it here to reference back to typeDesc obj and remove it. + TypeDesc::remove(structType.typeDesc().getName()); + } + structs.clear(); +} + +const string& StructTypeDesc::getName() const +{ + return _typedesc.getName(); +} + +const vector& StructTypeDesc::getMembers() const +{ + return _members; +} + +TypeDesc createStructTypeDesc(std::string_view name) +{ + return {name, TypeDesc::BASETYPE_STRUCT}; +} + +void registerStructTypeDesc(std::string_view name) +{ + auto structTypeDesc = createStructTypeDesc(name); + TypeDescRegistry register_struct(structTypeDesc, string(name)); +} + MATERIALX_NAMESPACE_END diff --git a/source/MaterialXGenShader/TypeDesc.h b/source/MaterialXGenShader/TypeDesc.h index 1a537b7e58..95d2851915 100644 --- a/source/MaterialXGenShader/TypeDesc.h +++ b/source/MaterialXGenShader/TypeDesc.h @@ -10,6 +10,7 @@ /// Type descriptor for a MaterialX data type. #include +#include #include @@ -61,14 +62,21 @@ class MX_GENSHADER_API TypeDesc /// Empty constructor. constexpr TypeDesc() noexcept : - _id(0), _basetype(BASETYPE_NONE), _semantic(SEMANTIC_NONE), _size(0) { } + _id(0), + _basetype(BASETYPE_NONE), + _semantic(SEMANTIC_NONE), + _size(0), + _structIndex(0) + { + } /// Constructor. - constexpr TypeDesc(std::string_view name, uint8_t basetype, uint8_t semantic = SEMANTIC_NONE, uint16_t size = 1) noexcept : + constexpr TypeDesc(std::string_view name, uint8_t basetype, uint8_t semantic = SEMANTIC_NONE, uint16_t size = 1, uint16_t structIndex = 0) noexcept : _id(constexpr_hash(name)), // Note: We only store the hash to keep the class size minimal. _basetype(basetype), _semantic(semantic), - _size(size) + _size(size), + _structIndex(structIndex) { } @@ -112,6 +120,12 @@ class MX_GENSHADER_API TypeDesc /// Return true if the type represents a closure. bool isClosure() const { return (_semantic == SEMANTIC_CLOSURE || _semantic == SEMANTIC_SHADER || _semantic == SEMANTIC_MATERIAL); } + /// Return true if the type represents a struct. + bool isStruct() const { return _basetype == BASETYPE_STRUCT; } + + /// Return the index for the struct member information in StructTypeDesc, the result is invalid if `isStruct()` returns false. + uint16_t getStructIndex() const { return _structIndex; } + /// Equality operator bool operator==(TypeDesc rhs) const { @@ -143,8 +157,14 @@ class MX_GENSHADER_API TypeDesc /// If no type is found Type::NONE is returned. static TypeDesc get(const string& name); + /// Remove a type description by name, if it exists. + static void remove(const string& name); + static const string NONE_TYPE_NAME; + /// Create a Value from a string for a given typeDesc + ValuePtr createValueFromStrings(const string& value) const; + private: /// Simple constexpr hash function, good enough for the small set of short strings that /// are used for our data type names. @@ -157,6 +177,7 @@ class MX_GENSHADER_API TypeDesc uint8_t _basetype; uint8_t _semantic; uint16_t _size; + uint16_t _structIndex; }; /// @class TypeDescRegistry @@ -208,6 +229,58 @@ TYPEDESC_DEFINE_TYPE(MATERIAL, "material", TypeDesc::BASETYPE_NONE, TypeDesc::SE } // namespace Type + +/// @class StructTypeDesc +/// A type descriptor for MaterialX struct types. +/// +/// All types need to have a type descriptor registered in order for shader generators +/// to know about the type. If the type represented is of basetype=BASETYPE_STRUCT then +/// the type also needs to have an associated StructTypeDesc that describes the members +/// of the struct. +/// +class MX_GENSHADER_API StructTypeDesc +{ + public: + struct StructMemberTypeDesc + { + StructMemberTypeDesc(string name, TypeDesc typeDesc, string defaultValueStr) : + _name(name), _typeDesc(typeDesc), _defaultValueStr(defaultValueStr) + { + } + string _name; + TypeDesc _typeDesc; + string _defaultValueStr; + }; + + /// Empty constructor. + StructTypeDesc() noexcept{} + + void addMember(const string& name, TypeDesc type, string defaultValueStr); + void setTypeDesc(TypeDesc typedesc) { _typedesc = typedesc; } + + /// Return a type description by index. + static StructTypeDesc& get(unsigned int index); + static vector getStructTypeNames(); + static uint16_t emplace_back(StructTypeDesc structTypeDesc); + static void clear(); + + TypeDesc typeDesc() const { return _typedesc; } + + const string& getName() const; + + const vector& getMembers() const; + + private: + TypeDesc _typedesc; + vector _members; +}; + +class MX_GENSHADER_API StructTypeDescRegistry +{ + public: + StructTypeDescRegistry(); +}; + MATERIALX_NAMESPACE_END #endif diff --git a/source/MaterialXGraphEditor/RenderView.cpp b/source/MaterialXGraphEditor/RenderView.cpp index dbc9b4f980..92cd8f07c7 100644 --- a/source/MaterialXGraphEditor/RenderView.cpp +++ b/source/MaterialXGraphEditor/RenderView.cpp @@ -630,6 +630,9 @@ void RenderView::initContext(mx::GenContext& context) unitSystem->setUnitConverterRegistry(_unitRegistry); context.getShaderGenerator().setUnitSystem(unitSystem); context.getOptions().targetDistanceUnit = "meter"; + + // Register struct type definitions + context.getShaderGenerator().loadStructTypeDefs(_document); } void RenderView::drawContents() diff --git a/source/MaterialXRender/Util.cpp b/source/MaterialXRender/Util.cpp index 881f1b7eea..00fdc65381 100644 --- a/source/MaterialXRender/Util.cpp +++ b/source/MaterialXRender/Util.cpp @@ -170,7 +170,7 @@ unsigned int getUIProperties(InputPtr input, const string& target, UIProperties& else { valueString += val; - uiProperties.enumerationValues.push_back(Value::createValueFromStrings(valueString, input->getType())); + uiProperties.enumerationValues.push_back(typeDesc.createValueFromStrings(valueString)); valueString.clear(); index = 0; } diff --git a/source/MaterialXRenderGlsl/GlslMaterial.cpp b/source/MaterialXRenderGlsl/GlslMaterial.cpp index 788af713b2..064ab898ff 100644 --- a/source/MaterialXRenderGlsl/GlslMaterial.cpp +++ b/source/MaterialXRenderGlsl/GlslMaterial.cpp @@ -360,7 +360,7 @@ void GlslMaterial::modifyUniform(const std::string& path, ConstValuePtr value, s { valueString = value->getValueString(); } - uniform->setValue(Value::createValueFromStrings(valueString, uniform->getType().getName())); + uniform->setValue(uniform->getType().createValueFromStrings(valueString)); if (_doc) { ElementPtr element = _doc->getDescendant(uniform->getPath()); diff --git a/source/MaterialXRenderGlsl/GlslProgram.cpp b/source/MaterialXRenderGlsl/GlslProgram.cpp index b2ca0aa66e..b63e1faecc 100644 --- a/source/MaterialXRenderGlsl/GlslProgram.cpp +++ b/source/MaterialXRenderGlsl/GlslProgram.cpp @@ -508,7 +508,7 @@ ImagePtr GlslProgram::bindTexture(unsigned int uniformType, int uniformLocation, return nullptr; } -MaterialX::ValuePtr GlslProgram::findUniformValue(const string& uniformName, const GlslProgram::InputMap& uniformList) +MaterialX::ConstValuePtr GlslProgram::findUniformValue(const string& uniformName, const GlslProgram::InputMap& uniformList) { auto uniform = uniformList.find(uniformName); if (uniform != uniformList.end()) @@ -946,47 +946,87 @@ const GlslProgram::InputMap& GlslProgram::updateUniformsList() for (size_t i = 0; i < uniforms.size(); ++i) { const ShaderPort* v = uniforms[i]; - int glType = mapTypeToOpenGLType(v->getType()); - // There is no way to match with an unnamed variable - if (v->getVariable().empty()) - { - continue; - } - - // Ignore types which are unsupported in GLSL. - if (glType == Input::INVALID_OPENGL_TYPE) - { - continue; - } + const auto& variablePath = v->getPath(); + const auto& variableUnit = v->getUnit(); + const auto& variableColorspace = v->getColorSpace(); + const auto& variableSemantic = v->getSemantic(); - auto inputIt = _uniformList.find(v->getVariable()); - if (inputIt != _uniformList.end()) + const auto populateUniformInput = + [this, variablePath, variableUnit, variableColorspace, variableSemantic, &errors, uniforms, &uniformTypeMismatchFound] + (TypeDesc typedesc, const string& variableName, ConstValuePtr variableValue) -> void { - Input* input = inputIt->second.get(); - input->path = v->getPath(); - input->unit = v->getUnit(); - input->colorspace = v->getColorSpace(); - input->value = v->getValue(); - if (input->gltype == glType) - { - input->typeString = v->getType().getName(); - } - else + auto populateUniformInput_impl = + [this, variablePath, variableUnit, variableColorspace, variableSemantic, &errors, uniforms, &uniformTypeMismatchFound] + (TypeDesc typedesc, const string& variableName, ConstValuePtr variableValue, auto& populateUniformInput_ref) -> void { - errors.push_back( - "Pixel shader uniform block type mismatch [" + uniforms.getName() + "]. " - + "Name: \"" + v->getVariable() - + "\". Type: \"" + v->getType().getName() - + "\". Semantic: \"" + v->getSemantic() - + "\". Value: \"" + (v->getValue() ? v->getValue()->getValueString() : "") - + "\". Unit: \"" + (!v->getUnit().empty() ? v->getUnit() : "") - + "\". Colorspace: \"" + (!v->getColorSpace().empty() ? v->getColorSpace() : "") - + "\". GLType: " + std::to_string(mapTypeToOpenGLType(v->getType())) - ); - uniformTypeMismatchFound = true; - } - } + if (!typedesc.isStruct()) + { + // Handle non-struct types + int glType = mapTypeToOpenGLType(typedesc); + + // There is no way to match with an unnamed variable + if (variableName.empty()) + { + return; + } + + // Ignore types which are unsupported in GLSL. + if (glType == Input::INVALID_OPENGL_TYPE) + { + return; + } + + auto inputIt = _uniformList.find(variableName); + if (inputIt != _uniformList.end()) + { + Input* input = inputIt->second.get(); + input->path = variablePath; + input->unit = variableUnit; + input->colorspace = variableColorspace; + input->value = variableValue; + if (input->gltype == glType) + { + input->typeString = typedesc.getName(); + } + else + { + errors.push_back( + "Pixel shader uniform block type mismatch [" + uniforms.getName() + "]. " + + "Name: \"" + variableName + + "\". Type: \"" + typedesc.getName() + + "\". Semantic: \"" + variableSemantic + + "\". Value: \"" + (variableValue ? variableValue->getValueString() : "") + + "\". Unit: \"" + (!variableUnit.empty() ? variableUnit : "") + + "\". Colorspace: \"" + (!variableColorspace.empty() ? variableColorspace : "") + + "\". GLType: " + std::to_string(glType)); + uniformTypeMismatchFound = true; + } + } + } + else + { + // If we're a struct - we need to loop over each member + auto structTypeDesc = StructTypeDesc::get(typedesc.getStructIndex()); + auto aggregateValue = std::static_pointer_cast(variableValue); + + const auto& members = structTypeDesc.getMembers(); + for (size_t i = 0, n = members.size(); i < n; ++i) + { + const auto& member = members[i]; + auto memberTypeDesc = member._typeDesc; + auto memberVariableName = variableName + "." + member._name; + auto memberVariableValue = aggregateValue->getMemberValue(i); + + populateUniformInput_ref(memberTypeDesc, memberVariableName, memberVariableValue, populateUniformInput_ref); + } + } + }; + + return populateUniformInput_impl(typedesc, variableName, variableValue, populateUniformInput_impl); + }; + + populateUniformInput(v->getType(), v->getVariable(), v->getValue()); } } @@ -1100,12 +1140,12 @@ const GlslProgram::InputMap& GlslProgram::updateAttributesList() if (string::npos != sattributeName.find(colorSet)) { string setNumber = sattributeName.substr(colorSet.size(), sattributeName.size()); - inputPtr->value = Value::createValueFromStrings(setNumber, getTypeString()); + inputPtr->value = Type::INTEGER.createValueFromStrings(setNumber); } else if (string::npos != sattributeName.find(uvSet)) { string setNumber = sattributeName.substr(uvSet.size(), sattributeName.size()); - inputPtr->value = Value::createValueFromStrings(setNumber, getTypeString()); + inputPtr->value = Type::INTEGER.createValueFromStrings(setNumber); } _attributeList[sattributeName] = inputPtr; diff --git a/source/MaterialXRenderGlsl/GlslProgram.h b/source/MaterialXRenderGlsl/GlslProgram.h index 7d58dbaca7..4addd7d54b 100644 --- a/source/MaterialXRenderGlsl/GlslProgram.h +++ b/source/MaterialXRenderGlsl/GlslProgram.h @@ -102,7 +102,7 @@ class MX_RENDERGLSL_API GlslProgram string typeString; /// Input value. Will only be non-empty if initialized stages with a HwShader and a value was set during /// shader generation. - MaterialX::ValuePtr value; + MaterialX::ConstValuePtr value; /// Is this a constant bool isConstant; /// Element path (if any) @@ -223,7 +223,7 @@ class MX_RENDERGLSL_API GlslProgram // Utility to find a uniform value in an uniform list. // If uniform cannot be found a null pointer will be return. - ValuePtr findUniformValue(const string& uniformName, const InputMap& uniformList); + ConstValuePtr findUniformValue(const string& uniformName, const InputMap& uniformList); // Bind an individual texture to a program uniform location ImagePtr bindTexture(unsigned int uniformType, int uniformLocation, const FilePath& filePath, diff --git a/source/MaterialXRenderMsl/MslMaterial.mm b/source/MaterialXRenderMsl/MslMaterial.mm index 311d84acee..eb13ea7900 100644 --- a/source/MaterialXRenderMsl/MslMaterial.mm +++ b/source/MaterialXRenderMsl/MslMaterial.mm @@ -318,7 +318,7 @@ { valueString = value->getValueString(); } - uniform->setValue(Value::createValueFromStrings(valueString, uniform->getType().getName())); + uniform->setValue(uniform->getType().createValueFromStrings(valueString)); if (_doc) { ElementPtr element = _doc->getDescendant(uniform->getPath()); diff --git a/source/MaterialXRenderMsl/MslPipelineStateObject.h b/source/MaterialXRenderMsl/MslPipelineStateObject.h index b335e29af5..a823662f5f 100644 --- a/source/MaterialXRenderMsl/MslPipelineStateObject.h +++ b/source/MaterialXRenderMsl/MslPipelineStateObject.h @@ -99,7 +99,7 @@ class MX_RENDERMSL_API MslProgram string typeString; /// Input value. Will only be non-empty if initialized stages with a HwShader and a value was set during /// shader generation. - MaterialX::ValuePtr value; + MaterialX::ConstValuePtr value; /// Is this a constant bool isConstant; /// Element path (if any) @@ -256,7 +256,7 @@ class MX_RENDERMSL_API MslProgram // Utility to find a uniform value in an uniform list. // If uniform cannot be found a null pointer will be return. - ValuePtr findUniformValue(const string& uniformName, const InputMap& uniformList); + ConstValuePtr findUniformValue(const string& uniformName, const InputMap& uniformList); // Bind an individual texture to a program uniform location ImagePtr bindTexture(id renderCmdEncoder, diff --git a/source/MaterialXRenderMsl/MslPipelineStateObject.mm b/source/MaterialXRenderMsl/MslPipelineStateObject.mm index 2d5685646f..470fa549b2 100644 --- a/source/MaterialXRenderMsl/MslPipelineStateObject.mm +++ b/source/MaterialXRenderMsl/MslPipelineStateObject.mm @@ -235,12 +235,12 @@ int GetStrideOfMetalType(MTLDataType type) if (_shader->hasAttribute(HW::ATTR_TRANSPARENT)) { - psoDesc.colorAttachments[0].blendingEnabled = YES; - psoDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; - psoDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; - psoDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; - psoDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; - psoDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; + psoDesc.colorAttachments[0].blendingEnabled = YES; + psoDesc.colorAttachments[0].rgbBlendOperation = MTLBlendOperationAdd; + psoDesc.colorAttachments[0].alphaBlendOperation = MTLBlendOperationAdd; + psoDesc.colorAttachments[0].sourceRGBBlendFactor = MTLBlendFactorSourceAlpha; + psoDesc.colorAttachments[0].sourceAlphaBlendFactor = MTLBlendFactorSourceAlpha; + psoDesc.colorAttachments[0].destinationRGBBlendFactor = MTLBlendFactorOneMinusSourceAlpha; psoDesc.colorAttachments[0].destinationAlphaBlendFactor = MTLBlendFactorOneMinusSourceAlpha; _alphaBlendingEnabled = true; @@ -265,12 +265,12 @@ int GetStrideOfMetalType(MTLDataType type) if (string::npos != sattributeName.find(colorSet)) { string setNumber = sattributeName.substr(colorSet.size(), sattributeName.size()); - inputPtr->value = Value::createValueFromStrings(setNumber, getTypeString()); + inputPtr->value = Type::INTEGER.createValueFromStrings(setNumber); } else if (string::npos != sattributeName.find(uvSet)) { string setNumber = sattributeName.substr(uvSet.size(), sattributeName.size()); - inputPtr->value = Value::createValueFromStrings(setNumber, getTypeString()); + inputPtr->value = Type::INTEGER.createValueFromStrings(setNumber); } _attributeList[sattributeName] = inputPtr; @@ -566,8 +566,8 @@ int GetStrideOfMetalType(MTLDataType type) return nullptr; } -MaterialX::ValuePtr MslProgram::findUniformValue(const string& uniformName, - const MslProgram::InputMap& uniformList) +MaterialX::ConstValuePtr MslProgram::findUniformValue(const string& uniformName, + const MslProgram::InputMap& uniformList) { auto uniform = uniformList.find(uniformName); if (uniform != uniformList.end()) @@ -925,31 +925,54 @@ int GetStrideOfMetalType(MTLDataType type) { if (arg.type == MTLArgumentTypeBuffer && arg.bufferDataType == MTLDataTypeStruct) { - for (MTLStructMember* member in arg.bufferStructType.members) - { - std::string uboObjectName = std::string(arg.name.UTF8String); - std::string memberName = member.name.UTF8String; - std::string uboDotMemberName = uboObjectName + "." + memberName; + const auto uboObjectName = string(arg.name.UTF8String); - InputPtr inputPtr = std::make_shared(arg.index, member.dataType, arg.bufferDataSize, EMPTY_STRING); - _uniformList[uboDotMemberName] = inputPtr; - _globalUniformNameList[memberName] = uboDotMemberName; - - if (MTLArrayType* arrayMember = member.arrayType) + const auto addUniformToList = + [this, uboObjectName] + (MTLStructMember* member, int index, int size, const string& memberNamePrefix) -> void + { + auto addUniformToList_impl = + [this, uboObjectName] + (MTLStructMember* member, int index, int size, const string& memberNamePrefix, auto& addUniformToList_ref) -> void { - for (int i = 0; i < arrayMember.arrayLength; ++i) + auto memberName = memberNamePrefix + member.name.UTF8String; + + if (MTLStructType* structMember = member.structType) { - for (MTLStructMember* ArrayOfStructMember in arrayMember.elementStructType.members) + for (MTLStructMember* subMember in structMember.members) { - std::string memberNameDotSubmember = memberName + "[" + std::to_string(i) + "]." + ArrayOfStructMember.name.UTF8String; - std::string uboDotMemberNameDotSubmemberName = uboObjectName + "." + memberNameDotSubmember; + auto namePrefix = memberName + "."; + addUniformToList_ref(subMember, subMember.argumentIndex, subMember.offset, namePrefix, addUniformToList_ref); + } + } + else + { + auto uboDotMemberName = uboObjectName + "." + memberName; + + InputPtr inputPtr = std::make_shared(index, member.dataType, size, EMPTY_STRING); + this->_uniformList[uboDotMemberName] = inputPtr; + this->_globalUniformNameList[memberName] = uboDotMemberName; - InputPtr inputPtr = std::make_shared(ArrayOfStructMember.argumentIndex, ArrayOfStructMember.dataType, ArrayOfStructMember.offset, EMPTY_STRING); - _uniformList[uboDotMemberNameDotSubmemberName] = inputPtr; - _globalUniformNameList[memberNameDotSubmember] = uboDotMemberNameDotSubmemberName; + if (MTLArrayType* arrayMember = member.arrayType) + { + for (int i = 0; i < arrayMember.arrayLength; ++i) + { + for (MTLStructMember* ArrayOfStructMember in arrayMember.elementStructType.members) + { + auto namePrefix = memberName + "[" + std::to_string(i) + "]."; + addUniformToList_ref(ArrayOfStructMember, ArrayOfStructMember.argumentIndex, ArrayOfStructMember.offset, namePrefix, addUniformToList_ref); + } + } } } - } + }; + + return addUniformToList_impl(member, index, size, memberNamePrefix, addUniformToList_impl); + }; + + for (MTLStructMember* member in arg.bufferStructType.members) + { + addUniformToList(member, arg.index, arg.bufferDataSize, ""); } } @@ -1006,55 +1029,90 @@ int GetStrideOfMetalType(MTLDataType type) for (size_t i = 0; i < uniforms.size(); ++i) { const ShaderPort* v = uniforms[i]; - MTLDataType resourceType = mapTypeToMetalType(v->getType()); - - // There is no way to match with an unnamed variable - if (v->getVariable().empty()) - { - continue; - } - // Ignore types which are unsupported in MSL. - if (resourceType == MTLDataTypeNone) - { - continue; - } + const string& variablePath = v->getPath(); + const string& variableSemantic = v->getSemantic(); - auto inputIt = _uniformList.find(v->getVariable()); - if (inputIt == _uniformList.end()) + const auto populateUniformInput = + [this, uniforms, variablePath, variableSemantic, &errors, &uniformTypeMismatchFound] + (TypeDesc variableTypeDesc, const string& variableName, ConstValuePtr variableValue) -> void { - if (v->getType() == Type::FILENAME) + auto populateUniformInput_impl = + [this, uniforms, variablePath, variableSemantic, &errors, &uniformTypeMismatchFound] + (TypeDesc variableTypeDesc, const string& variableName, ConstValuePtr variableValue, auto& populateUniformInput_ref) -> void { - inputIt = _uniformList.find(TEXTURE_NAME(v->getVariable())); - } - else - { - inputIt = _uniformList.find(uniforms.getInstance() + "." + v->getVariable()); - } - } + // There is no way to match with an unnamed variable + if (variableName.empty()) + { + return; + } - if (inputIt != _uniformList.end()) - { - Input* input = inputIt->second.get(); - input->path = v->getPath(); - input->value = v->getValue(); - if (input->resourceType == resourceType) - { - input->typeString = v->getType().getName(); - } - else - { - errors.push_back( - "Pixel shader uniform block type mismatch [" + uniforms.getName() + "]. " - + "Name: \"" + v->getVariable() - + "\". Type: \"" + v->getType().getName() - + "\". Semantic: \"" + v->getSemantic() - + "\". Value: \"" + (v->getValue() ? v->getValue()->getValueString() : "") - + "\". resourceType: " + std::to_string(mapTypeToMetalType(v->getType())) - ); - uniformTypeMismatchFound = true; - } - } + MTLDataType resourceType = mapTypeToMetalType(variableTypeDesc); + // Ignore types which are unsupported in MSL. + if (resourceType == MTLDataTypeNone) + { + return; + } + + if (!variableTypeDesc.isStruct()) + { + auto inputIt = _uniformList.find(variableName); + + if (inputIt == _uniformList.end()) { + if(variableTypeDesc == Type::FILENAME) + { + inputIt = _uniformList.find(TEXTURE_NAME(variableName)); + } + else + { + inputIt = _uniformList.find(uniforms.getInstance() + "." + variableName); + } + } + + if (inputIt != _uniformList.end()) + { + Input* input = inputIt->second.get(); + input->path = variablePath; + input->value = variableValue; + if (input->resourceType == resourceType) + { + input->typeString = variableTypeDesc.getName(); + } + else + { + errors.push_back( + "Pixel shader uniform block type mismatch [" + uniforms.getName() + "]. " + + "Name: \"" + variableName + + "\". Type: \"" + variableTypeDesc.getName() + + "\". Semantic: \"" + variableSemantic + + "\". Value: \"" + (variableValue ? variableValue->getValueString() : "") + + "\". resourceType: " + std::to_string(mapTypeToMetalType(variableTypeDesc)) + ); + uniformTypeMismatchFound = true; + } + } + } + else + { + auto structTypeDesc = StructTypeDesc::get(variableTypeDesc.getStructIndex()); + auto aggregateValue = std::static_pointer_cast(variableValue); + + const auto& members = structTypeDesc.getMembers(); + for (size_t i = 0, n = members.size(); i < n; ++i) + { + const auto& structMember = members[i]; + auto memberVariableName = variableName+"."+structMember._name; + auto memberVariableValue = aggregateValue->getMemberValue(i); + + populateUniformInput_ref(structMember._typeDesc, memberVariableName, memberVariableValue, populateUniformInput_ref); + } + } + }; + + return populateUniformInput_impl(variableTypeDesc, variableName, variableValue, populateUniformInput_impl); + }; + + populateUniformInput(v->getType(), v->getVariable(), v->getValue()); } } @@ -1231,7 +1289,7 @@ int GetStrideOfMetalType(MTLDataType type) return false; }; - auto setValue = [](MaterialX::ValuePtr value, std::vector& data, size_t offset) + auto setValue = [](MaterialX::ConstValuePtr value, std::vector& data, size_t offset) { if (value->getTypeString() == "float") { @@ -1307,7 +1365,7 @@ throw ExceptionRenderError( { if (!setCommonUniform(lightHandler, cam, member.name.UTF8String, uniformBufferData, member.offset)) { - MaterialX::ValuePtr value = _uniformList[string(arg.name.UTF8String) + "." + member.name.UTF8String]->value; + auto value = _uniformList[string(arg.name.UTF8String) + "." + member.name.UTF8String]->value; if (value) { setValue(value, uniformBufferData, member.offset); @@ -1332,39 +1390,60 @@ throw ExceptionRenderError( if (!setCommonUniform(lightHandler, cam, member.name.UTF8String, uniformBufferData, member.offset)) { - auto uniformInfo = _uniformList.find(uniformName); - if (uniformInfo != _uniformList.end()) - { - MaterialX::ValuePtr value = uniformInfo->second->value; - if (value) - { - setValue(value, uniformBufferData, member.offset); - } - } - else - { - } - } - - if (MTLArrayType* arrayMember = member.arrayType) - { - for (int i = 0; i < arrayMember.arrayLength; ++i) + const auto setUniformValue = + [this, setValue] + (MTLStructMember* member, const string& uniformName, std::vector& uniformBufferData, int offset ) -> void { - for (MTLStructMember* ArrayOfStructMember in arrayMember.elementStructType.members) + auto setUniformValue_impl = + [this, setValue] + (MTLStructMember* member, const string& uniformName, std::vector& uniformBufferData, int offset, auto &setUniformValue_ref ) -> void { - string uniformNameSubArray = uniformName + "[" + std::to_string(i) + "]." + ArrayOfStructMember.name.UTF8String; - - auto uniformInfo = _uniformList.find(uniformNameSubArray); - if (uniformInfo != _uniformList.end()) + if(MTLArrayType* arrayMember = member.arrayType) { - MaterialX::ValuePtr value = uniformInfo->second->value; - if (value) + for(int i = 0; i < arrayMember.arrayLength; ++i) { - setValue(value, uniformBufferData, member.offset + i * arrayMember.stride + ArrayOfStructMember.offset); + for (MTLStructMember* ArrayOfStructMember in arrayMember.elementStructType.members) + { + string uniformNameSubArray = uniformName + "[" + std::to_string(i) + "]." + ArrayOfStructMember.name.UTF8String; + auto uniformInfo = _uniformList.find(uniformNameSubArray); + if (uniformInfo != _uniformList.end()) + { + auto value = uniformInfo->second->value; + if(value) + { + setValue(value, uniformBufferData, offset + i * arrayMember.stride + ArrayOfStructMember.offset); + } + } + } } } - } - } + else if (MTLStructType* structMember = member.structType) + { + // this code does not support struct recursion yet.... + for (MTLStructMember* subMember in structMember.members) + { + string subUniformName = uniformName+"."+subMember.name.UTF8String; + setUniformValue_ref(subMember, subUniformName, uniformBufferData, offset+subMember.offset, setUniformValue_ref); + } + } + else + { + auto uniformInfo = _uniformList.find(uniformName); + if (uniformInfo != _uniformList.end()) + { + auto value = uniformInfo->second->value; + if(value) + { + setValue(value, uniformBufferData, offset); + } + } + } + }; + + return setUniformValue_impl(member, uniformName, uniformBufferData, offset, setUniformValue_impl); + }; + + setUniformValue(member, uniformName, uniformBufferData, member.offset); } } @@ -1415,14 +1494,15 @@ throw ExceptionRenderError( // A "filename" is not indicative of type, so just return a 2d sampler. return MTLDataTypeTexture; } - else if (type == Type::BSDF || - type == Type::MATERIAL || + else if (type == Type::BSDF || + type == Type::MATERIAL || type == Type::DISPLACEMENTSHADER || - type == Type::EDF || - type == Type::VDF || - type == Type::SURFACESHADER || - type == Type::LIGHTSHADER || - type == Type::VOLUMESHADER) + type == Type::EDF || + type == Type::VDF || + type == Type::SURFACESHADER || + type == Type::LIGHTSHADER || + type == Type::VOLUMESHADER || + type.isStruct()) return MTLDataTypeStruct; return MTLDataTypeNone; diff --git a/source/MaterialXTest/MaterialXCore/Document.cpp b/source/MaterialXTest/MaterialXCore/Document.cpp index 94f8b98708..ae7623084e 100644 --- a/source/MaterialXTest/MaterialXCore/Document.cpp +++ b/source/MaterialXTest/MaterialXCore/Document.cpp @@ -10,8 +10,6 @@ #include #include -#include - namespace mx = MaterialX; TEST_CASE("Document", "[document]") @@ -123,16 +121,6 @@ TEST_CASE("Document", "[document]") REQUIRE(doc->validate()); } -void printDifferences(const mx::ElementEquivalenceResultVec& results, const std::string& label) -{ - for (const mx::ElementEquivalenceResult& result : results) - { - std::cout << label << ": " << "Element: " << result.path1 << - ", Element: " << result.path2 << ", Difference Type: " << result.differenceType - << ", Value: " << result.attributeName << std::endl; - } -} - TEST_CASE("Document equivalence", "[document]") { mx::DocumentPtr doc = mx::createDocument(); @@ -224,44 +212,18 @@ TEST_CASE("Document equivalence", "[document]") // Check skipping all value compares options.skipValueComparisons = true; bool equivalent = doc->isEquivalent(doc2, options, &results); - if (equivalent) - { - std::cout << "Unexpected skip value equivalence:" << std::endl; - std::cout << "Document 1: " << mx::prettyPrint(doc) << std::endl; - std::cout << "Document 2: " << mx::prettyPrint(doc2) << std::endl; - } - else - { - printDifferences(results, "Expected value differences"); - } REQUIRE(!equivalent); // Check attibute values options.skipValueComparisons = false; results.clear(); equivalent = doc->isEquivalent(doc2, options, &results); - if (!equivalent) - { - printDifferences(results, "Unexpected value difference"); - std::cout << "Document 1: " << mx::prettyPrint(doc) << std::endl; - std::cout << "Document 2: " << mx::prettyPrint(doc2) << std::endl; - } REQUIRE(equivalent); unsigned int currentPrecision = mx::Value::getFloatPrecision(); // This will compare 0.012345608 versus: 1, 0.012345611 for input10 options.precision = 8; equivalent = doc->isEquivalent(doc2, options); - if (equivalent) - { - std::cout << "Unexpected precision equivalence:" << std::endl; - std::cout << "Document 1: " << mx::prettyPrint(doc) << std::endl; - std::cout << "Document 2: " << mx::prettyPrint(doc2) << std::endl; - } - else - { - printDifferences(results, "Expected precision difference"); - } REQUIRE(!equivalent); options.precision = currentPrecision; @@ -274,12 +236,6 @@ TEST_CASE("Document equivalence", "[document]") floatInput->setAttribute(mx::ValueElement::UI_MAX_ATTRIBUTE, "100.0"); } equivalent = doc->isEquivalent(doc2, options, &results); - if (!equivalent) - { - printDifferences(results, "Unexpected filtering differences"); - std::cout << "Document 1: " << mx::prettyPrint(doc) << std::endl; - std::cout << "Document 2: " << mx::prettyPrint(doc2) << std::endl; - } REQUIRE(equivalent); for (mx::InputPtr floatInput : floatInputs) { @@ -293,16 +249,6 @@ TEST_CASE("Document equivalence", "[document]") mismatchElement->setName("mismatch_color4"); results.clear(); equivalent = doc->isEquivalent(doc2, options, &results); - if (!equivalent) - { - printDifferences(results, "Expected name mismatch differences"); - } - else - { - std::cout << "Unexpected name match equivalence:" << std::endl; - std::cout << "Document 1: " << mx::prettyPrint(doc) << std::endl; - std::cout << "Document 2: " << mx::prettyPrint(doc2) << std::endl; - } REQUIRE(!equivalent); mismatchElement->setName(previousName); results.clear(); @@ -320,15 +266,5 @@ TEST_CASE("Document equivalence", "[document]") nodeGraph2->setNodeDefString("ND_mygraph"); results.clear(); equivalent = doc->isEquivalent(doc2, options, &results); - if (!equivalent) - { - printDifferences(results, "Expected functional graph differences"); - } - else - { - std::cout << "Unexpected functional graph equivalence:" << std::endl; - std::cout << "Document 1: " << mx::prettyPrint(doc) << std::endl; - std::cout << "Document 2: " << mx::prettyPrint(doc2) << std::endl; - } REQUIRE(!equivalent); } diff --git a/source/MaterialXTest/MaterialXCore/Node.cpp b/source/MaterialXTest/MaterialXCore/Node.cpp index ef746bfe05..f3ce7d40de 100644 --- a/source/MaterialXTest/MaterialXCore/Node.cpp +++ b/source/MaterialXTest/MaterialXCore/Node.cpp @@ -159,12 +159,12 @@ TEST_CASE("Node", "[node]") REQUIRE(typeDef->getMembers().size() == scalarCount); // Reference the custom type. - std::string d65("400.0,82.75,500.0,109.35,600.0,90.01,700.0,71.61,800.0,59.45"); + std::string d65("{400;82.75;500;109.35;600;90.01;700;71.61;800;59.45}"); constant->setInputValue("value", d65, "spectrum"); REQUIRE(constant->getInput("value")->getType() == "spectrum"); REQUIRE(constant->getInput("value")->getValueString() == d65); - REQUIRE(constant->getInputValue("value")->isA()); - REQUIRE(constant->getInputValue("value")->asA() == d65); + REQUIRE(constant->getInputValue("value")->isA()); + REQUIRE(constant->getInputValue("value")->asA().getValueString() == d65); // Validate the document. REQUIRE(doc->validate()); diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 4a8e61645a..78da7e50f3 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -649,6 +649,9 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons addColorManagement(); addUnitSystem(); + // Register struct typedefs from the library files. + _shaderGenerator->loadStructTypeDefs(_dependLib); + // Test suite setup addSkipFiles(); @@ -705,6 +708,8 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons preprocessDocument(doc); _shaderGenerator->registerShaderMetadata(doc, context); + _shaderGenerator->loadStructTypeDefs(doc); + // For each new file clear the implementation cache. // Since the new file might contain implementations with names // colliding with implementations in previous test cases. diff --git a/source/MaterialXView/Viewer.cpp b/source/MaterialXView/Viewer.cpp index 24aea1eb42..c4c69bb812 100644 --- a/source/MaterialXView/Viewer.cpp +++ b/source/MaterialXView/Viewer.cpp @@ -1769,6 +1769,9 @@ void Viewer::initContext(mx::GenContext& context) unitSystem->setUnitConverterRegistry(_unitRegistry); context.getShaderGenerator().setUnitSystem(unitSystem); context.getOptions().targetDistanceUnit = "meter"; + + // Initialize the struct typedefs from the stdlib + context.getShaderGenerator().loadStructTypeDefs(_stdLib); } void Viewer::loadStandardLibraries() diff --git a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp index 501f9d5f04..c36bd6c288 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyValue.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyValue.cpp @@ -6,6 +6,7 @@ #include #include +#include #define BIND_TYPE_INSTANCE(NAME, T) \ py::class_, std::shared_ptr< mx::TypedValue >, mx::Value>(mod, "TypedValue_" #NAME) \ @@ -22,7 +23,10 @@ void bindPyValue(py::module& mod) py::class_(mod, "Value") .def("getValueString", &mx::Value::getValueString) .def("getTypeString", &mx::Value::getTypeString) - .def_static("createValueFromStrings", &mx::Value::createValueFromStrings); + .def_static("createValueFromStrings", &mx::Value::createValueFromStrings, + py::arg("value"), + py::arg("type"), + py::arg("typeDefPtr") = nullptr); BIND_TYPE_INSTANCE(integer, int) BIND_TYPE_INSTANCE(boolean, bool) diff --git a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp index b952bd4651..0c8a086e58 100644 --- a/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp +++ b/source/PyMaterialX/PyMaterialXGenShader/PyShaderGenerator.cpp @@ -22,5 +22,7 @@ void bindPyShaderGenerator(py::module& mod) .def("setUnitSystem", &mx::ShaderGenerator::setUnitSystem) .def("getUnitSystem", &mx::ShaderGenerator::getUnitSystem) .def("getTokenSubstitutions", &mx::ShaderGenerator::getTokenSubstitutions) + .def("loadStructTypeDefs", &mx::ShaderGenerator::loadStructTypeDefs) + .def("clearStructTypeDefs", &mx::ShaderGenerator::clearStructTypeDefs) .def("registerShaderMetadata", &mx::ShaderGenerator::registerShaderMetadata); }