Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial implementation of structs in MaterialX #1831

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
a338308
Add support for recursive structs, with example.
ld-kerley Feb 28, 2024
3c70e1e
fix python unit tests.
ld-kerley Jun 2, 2024
72ae46f
Addressing notes and refactor 'experimental' files to unit test locat…
ld-kerley Jun 11, 2024
fc7b260
fix signed vs unsigned windows warning.
ld-kerley Jun 11, 2024
ac69fff
fix signed vs unsigned windows warning (part 2)
ld-kerley Jun 11, 2024
7134623
fix unreferenced parameter warning
ld-kerley Jun 11, 2024
45e8468
fix dll linkage error
ld-kerley Jun 12, 2024
36f934a
fix JS error
ld-kerley Jun 12, 2024
9613e5b
fix typo in comment
ld-kerley Jun 13, 2024
f7a3b6a
remove separate header declaring forward declarations.
ld-kerley Sep 17, 2024
d54ba07
Removing unit test infrastructure for now - per Jonathan - will add s…
ld-kerley Sep 17, 2024
a892a8d
removing separate Exceptions.h file - per Jonathans request.
ld-kerley Sep 18, 2024
1fdcfa9
refactor to use parseStructValueString()
ld-kerley Sep 18, 2024
483998c
fix unit tests
ld-kerley Sep 19, 2024
52d2cc3
Attempting to fix windows vs2019 build failure.
ld-kerley Sep 30, 2024
d8b5c71
Attempting to fix windows vs2019 build failure - again
ld-kerley Sep 30, 2024
1aaaa1e
Update letter case
jstone-lucasfilm Sep 30, 2024
11393c0
Merge branch 'main' into struct_experiment_dont_unfold
jstone-lucasfilm Sep 30, 2024
fba6902
Update letter case
jstone-lucasfilm Sep 30, 2024
33dc3c7
Alphabetize includes within a module
jstone-lucasfilm Sep 30, 2024
217bf19
Merge branch 'main' into struct_experiment_dont_unfold
jstone-lucasfilm Oct 3, 2024
42ffa91
Remove extra newline
jstone-lucasfilm Oct 3, 2024
ff98042
Minor formatting update
jstone-lucasfilm Oct 3, 2024
1378e96
Minor formatting updates
jstone-lucasfilm Oct 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions python/Scripts/generateshader.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,9 @@ def main():
shadergen.setUnitSystem(unitsystem)
genoptions.targetDistanceUnit = 'meter'

print('- Set up Struct typedefs ...')
shadergen.loadStructTypeDefs(doc)

# Look for renderable nodes
nodes = mx_gen_shader.findRenderableElements(doc)
if not nodes:
Expand All @@ -165,6 +168,8 @@ def main():
if gentarget in ['glsl', 'essl', 'vulkan', 'msl']:
pixelSource = shader.getSourceCode(mx_gen_shader.PIXEL_STAGE)
filename = pathPrefix + "/" + shader.getName() + "." + gentarget + ".frag"
if gentarget == "msl":
filename += ".metal"
print('--- Wrote pixel shader to: ' + filename)
file = open(filename, 'w+')
file.write(pixelSource)
Expand All @@ -173,6 +178,8 @@ def main():

vertexSource = shader.getSourceCode(mx_gen_shader.VERTEX_STAGE)
filename = pathPrefix + "/" + shader.getName() + "." + gentarget + ".vert"
if gentarget == "msl":
filename += ".metal"
print('--- Wrote vertex shader to: ' + filename)
file = open(filename, 'w+')
file.write(vertexSource)
Expand Down
2 changes: 1 addition & 1 deletion source/JsMaterialX/JsMaterialXCore/JsValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 8 additions & 0 deletions source/MaterialXCore/Definition.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,12 @@ vector<UnitDefPtr> UnitTypeDef::getUnitDefs() const
return unitDefs;
}

ValuePtr AttributeDef::getValue() const
{
if (!hasValue())
return ValuePtr();

return Value::createValueFromStrings(getValueString(), getType(), getDocument()->getTypeDef(getType()));
}

MATERIALX_NAMESPACE_END
11 changes: 3 additions & 8 deletions source/MaterialXCore/Definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -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() { }
Expand Down Expand Up @@ -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
Expand Down
16 changes: 16 additions & 0 deletions source/MaterialXCore/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -495,6 +495,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();
Expand Down
14 changes: 2 additions & 12 deletions source/MaterialXCore/Element.h
Original file line number Diff line number Diff line change
Expand Up @@ -1025,12 +1025,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.
Expand All @@ -1040,12 +1035,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.
Expand Down
8 changes: 8 additions & 0 deletions source/MaterialXCore/Exception.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
74 changes: 73 additions & 1 deletion source/MaterialXCore/Value.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// SPDX-License-Identifier: Apache-2.0
//

#include <MaterialXCore/Definition.h>
#include <MaterialXCore/Document.h>
#include <MaterialXCore/Value.h>

#include <iomanip>
Expand Down Expand Up @@ -267,12 +269,18 @@ template <class T> ValuePtr TypedValue<T>::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 typeDefPtr)
{
CreatorMap::iterator it = _creatorMap.find(type);
if (it != _creatorMap.end())
return it->second(value);

if (typeDefPtr && !typeDefPtr->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, typeDefPtr);
}

return TypedValue<string>::createFromString(value);
}

Expand All @@ -291,6 +299,70 @@ template <class T> const T& Value::asA() const
return typedVal->getData();
}

template <>
MX_CORE_API bool Value::isA<AggregateValue>() const
{
return dynamic_cast<const AggregateValue*>(this) != nullptr;
}

template <>
MX_CORE_API const AggregateValue& Value::asA<AggregateValue>() const
{
const AggregateValue* typedVal = dynamic_cast<const AggregateValue*>(this);
if (!typedVal)
{
throw ExceptionTypeError("Incorrect type specified for value");
}
return *typedVal;
}

/// Return value string.
string AggregateValue::getValueString() const
{
if (_data.empty())
return "";

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 typeDefPtr)
{
StringVec subValues = parseStructValueString(value);

AggregateValuePtr result = AggregateValue::createAggregateValue(type);
const auto& members = typeDefPtr->getMembers();

if (subValues.size() != members.size())
{
std::stringstream ss;
ss << "Wrong number of initializers - expect " << members.size();
throw Exception(ss.str());
}

auto doc = typeDefPtr->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())
Expand Down
78 changes: 69 additions & 9 deletions source/MaterialXCore/Value.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,22 @@ using BoolVec = vector<bool>;
using FloatVec = vector<float>;

class Value;
class AggregateValue;

/// A shared pointer to a Value
using ValuePtr = shared_ptr<Value>;
/// A shared pointer to a const Value
using ConstValuePtr = shared_ptr<const Value>;

template <class T> class TypedValue;
/// A shared pointer to an Aggregate Value
using AggregateValuePtr = shared_ptr<AggregateValue>;
/// A shared pointer to a const Aggregate Value
using ConstAggregateValuePtr = shared_ptr<const AggregateValue>;

/// @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<const TypeDef>;

template <class T> class TypedValue;

/// A generic, discriminated value, whose type may be queried dynamically.
class MX_CORE_API Value
Expand Down Expand Up @@ -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 typeDefPtr = nullptr);

/// Create a deep copy of the value.
virtual ValuePtr copy() const = 0;
Expand Down Expand Up @@ -193,6 +194,65 @@ template <class T> class MX_CORE_API TypedValue : public Value
T _data;
};

/// The class template for typed subclasses of Value
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<ConstValuePtr>& 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<AggregateValue>(typeName);
}

static AggregateValuePtr createAggregateValueFromString(const string& value, const string& type, ConstTypeDefPtr typeDefPtr);

private:
const string _typeName;

vector<ConstValuePtr> _data;
};

/// @class ScopedFloatFormatting
/// An RAII class for controlling the float formatting of values.
class MX_CORE_API ScopedFloatFormatting
Expand Down
36 changes: 36 additions & 0 deletions source/MaterialXGenGlsl/GlslSyntax.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -375,4 +375,40 @@ 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<GlslStructTypeSyntax>(
this,
structTypeName,
defaultValue,
uniformDefaultValue,
typeAlias,
typeDefinition);
}

string GlslStructTypeSyntax::getValue(const Value& value, bool /* uniform */) const
{
const AggregateValue& aggValue = static_cast<const AggregateValue&>(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
Loading