// // Copyright Contributors to the MaterialX Project // SPDX-License-Identifier: Apache-2.0 // #ifndef MATERIALX_SHADERNODE_H #define MATERIALX_SHADERNODE_H /// @file /// Classes for nodes created during shader generation #include #include #include #include #include MATERIALX_NAMESPACE_BEGIN class ShaderNode; class ShaderPort; class ShaderInput; class ShaderOutput; class ShaderGraph; /// Shared pointer to a ShaderPort using ShaderPortPtr = shared_ptr; /// Shared pointer to a ShaderInput using ShaderInputPtr = shared_ptr; /// Shared pointer to a ShaderOutput using ShaderOutputPtr = shared_ptr; /// Shared pointer to a ShaderNode using ShaderNodePtr = shared_ptr; /// A vector of ShaderInput pointers using ShaderInputVec = vector; /// Metadata to be exported to generated shader. struct MX_GENSHADER_API ShaderMetadata { string name; const TypeDesc* type; ValuePtr value; ShaderMetadata(const string& n, const TypeDesc* t, ValuePtr v = nullptr) : name(n), type(t), value(v) { } }; using ShaderMetadataVec = vector; using ShaderMetadataVecPtr = shared_ptr; /// @class ShaderMetadataRegistry /// A registry for metadata that will be exported to the generated shader /// if found on nodes and inputs during shader generation. class MX_GENSHADER_API ShaderMetadataRegistry : public GenUserData { public: static const string USER_DATA_NAME; /// Add a new metadata entry to the registry. /// The entry contains the name and data type /// for the metadata. void addMetadata(const string& name, const TypeDesc* type, ValuePtr value = nullptr) { if (_entryIndex.count(name) == 0) { _entryIndex[name] = _entries.size(); _entries.emplace_back(name, type, value); } } /// Return the metadata registered for the given name, or nullptr /// if no such entry is found. const ShaderMetadata* findMetadata(const string& name) const { auto it = _entryIndex.find(name); return it != _entryIndex.end() ? &_entries[it->second] : nullptr; } /// Return the metadata registered for the given name, or nullptr /// if no such entry is found. ShaderMetadata* findMetadata(const string& name) { auto it = _entryIndex.find(name); return it != _entryIndex.end() ? &_entries[it->second] : nullptr; } /// Return all entries in the registry. const ShaderMetadataVec& getAllMetadata() const { return _entries; } /// Clear all entries in the registry. void clear() { _entryIndex.clear(); _entries.clear(); } protected: vector _entries; std::unordered_map _entryIndex; }; using ShaderMetadataRegistryPtr = shared_ptr; /// Flags set on shader ports. class MX_GENSHADER_API ShaderPortFlag { public: static const uint32_t UNIFORM = 1u << 0; static const uint32_t EMITTED = 1u << 1; static const uint32_t BIND_INPUT = 1u << 2; }; /// @class ShaderPort /// An input or output port on a ShaderNode class MX_GENSHADER_API ShaderPort : public std::enable_shared_from_this { public: /// Constructor. ShaderPort(ShaderNode* node, const TypeDesc* type, const string& name, ValuePtr value = nullptr); /// Return a shared pointer instance of this object. ShaderPortPtr getSelf() { return shared_from_this(); } /// Return the node this port belongs to. ShaderNode* getNode() { return _node; } /// Return the node this port belongs to. const ShaderNode* getNode() const { return _node; } /// Set the data type for this port. void setType(const TypeDesc* type) { _type = type; } /// Return the data type for this port. const TypeDesc* getType() const { return _type; } /// Set the name of this port. void setName(const string& name) { _name = name; } /// Return the name of this port. const string& getName() const { return _name; } /// Return the name of this port. string getFullName() const; /// Set the variable name of this port. void setVariable(const string& name) { _variable = name; } /// Return the variable name of this port. const string& getVariable() const { return _variable; } /// Set the variable semantic of this port. void setSemantic(const string& semantic) { _semantic = semantic; } /// Return the variable semantic of this port. const string& getSemantic() const { return _semantic; } /// Set a value on this port. void setValue(ValuePtr value) { _value = value; } /// Return the value set on this port. ValuePtr getValue() const { return _value; } /// Return the value set on this port as a string, or an empty string if there is no value. string getValueString() const; /// Set a source color space for the value on this port. void setColorSpace(const string& colorspace) { _colorspace = colorspace; } /// Return the source color space for the value on this port. const string& getColorSpace() const { return _colorspace; } /// Set a unit type for the value on this port. void setUnit(const string& unit) { _unit = unit; } /// Return the unit type for the value on this port. const string& getUnit() const { return _unit; } /// Set geomprop name if the input has a default /// geomprop to be assigned when it is unconnected. void setGeomProp(const string& geomprop) { _geomprop = geomprop; } /// Get geomprop name. const string& getGeomProp() const { return _geomprop; } /// Set the path to this port. void setPath(const string& path) { _path = path; } /// Return the path to this port. const string& getPath() const { return _path; } /// Set flags on this port. void setFlags(uint32_t flags) { _flags = flags; } /// Return flags set on this port. uint32_t getFlags() const { return _flags; } /// Set the on|off state of a given flag. void setFlag(uint32_t flag, bool value) { _flags = value ? (_flags | flag) : (_flags & ~flag); } /// Return the on|off state of a given flag. bool getFlag(uint32_t flag) const { return ((_flags & flag) != 0); } /// Set the uniform flag this port to true. void setUniform() { _flags |= ShaderPortFlag::UNIFORM; } /// Return the uniform flag on this port. bool isUniform() const { return (_flags & ShaderPortFlag::UNIFORM) != 0; } /// Set the emitted state on this port to true. void setEmitted() { _flags |= ShaderPortFlag::EMITTED; } /// Return the emitted state of this port. bool isEmitted() const { return (_flags & ShaderPortFlag::EMITTED) != 0; } /// Set the bind input state on this port to true. void setBindInput() { _flags |= ShaderPortFlag::BIND_INPUT; } /// Return the emitted state of this port. bool isBindInput() const { return (_flags & ShaderPortFlag::BIND_INPUT) != 0; } /// Set the metadata vector. void setMetadata(ShaderMetadataVecPtr metadata) { _metadata = metadata; } /// Get the metadata vector. ShaderMetadataVecPtr getMetadata() { return _metadata; } /// Get the metadata vector. const ShaderMetadataVecPtr& getMetadata() const { return _metadata; } protected: ShaderNode* _node; const TypeDesc* _type; string _name; string _path; string _semantic; string _variable; ValuePtr _value; string _unit; string _colorspace; string _geomprop; ShaderMetadataVecPtr _metadata; uint32_t _flags; }; /// @class ShaderInput /// An input on a ShaderNode class MX_GENSHADER_API ShaderInput : public ShaderPort { public: ShaderInput(ShaderNode* node, const TypeDesc* type, const string& name); /// Return a connection to an upstream node output, /// or nullptr if not connected. ShaderOutput* getConnection() { return _connection; } /// Return a connection to an upstream node output, /// or nullptr if not connected. const ShaderOutput* getConnection() const { return _connection; } /// Make a connection from the given source output to this input. void makeConnection(ShaderOutput* src); /// Break the connection to this input. void breakConnection(); /// Set optional channels value void setChannels(const string& channels) { _channels = channels; } /// Get optional channels value const string& getChannels() const { return _channels; } /// Return the sibling node connected upstream, /// or nullptr if there is no sibling upstream. ShaderNode* getConnectedSibling() const; protected: ShaderOutput* _connection; string _channels; friend class ShaderOutput; }; /// @class ShaderOutput /// An output on a ShaderNode class MX_GENSHADER_API ShaderOutput : public ShaderPort { public: ShaderOutput(ShaderNode* node, const TypeDesc* type, const string& name); /// Return a set of connections to downstream node inputs, /// empty if not connected. const ShaderInputVec& getConnections() const { return _connections; } /// Make a connection from this output to the given input void makeConnection(ShaderInput* dst); /// Break a connection from this output to the given input void breakConnection(ShaderInput* dst); /// Break all connections from this output void breakConnections(); protected: ShaderInputVec _connections; friend class ShaderInput; }; /// @class ShaderNode /// Class representing a node in the shader generation DAG class MX_GENSHADER_API ShaderNode { public: virtual ~ShaderNode() { } /// Flags for classifying nodes into different categories. class Classification { public: // Node classes static const uint32_t TEXTURE = 1 << 0; /// Any node that outputs floats, colors, vectors, etc. static const uint32_t CLOSURE = 1 << 1; /// Any node that represents light integration static const uint32_t SHADER = 1 << 2; /// Any node that outputs a shader static const uint32_t MATERIAL = 1 << 3; /// Any node that outputs a material // Specific texture node types static const uint32_t FILETEXTURE = 1 << 4; /// A file texture node static const uint32_t CONDITIONAL = 1 << 5; /// A conditional node static const uint32_t CONSTANT = 1 << 6; /// A constant node // Specific closure types static const uint32_t BSDF = 1 << 7; /// A BSDF node static const uint32_t BSDF_R = 1 << 8; /// A reflection BSDF node static const uint32_t BSDF_T = 1 << 9; /// A transmission BSDF node static const uint32_t EDF = 1 << 10; /// A EDF node static const uint32_t VDF = 1 << 11; /// A VDF node static const uint32_t LAYER = 1 << 12; /// A node for vertical layering of other closure nodes static const uint32_t THINFILM = 1 << 13; /// A node for adding thin-film over microfacet BSDF nodes // Specific shader types static const uint32_t SURFACE = 1 << 14; /// A surface shader node static const uint32_t VOLUME = 1 << 15; /// A volume shader node static const uint32_t LIGHT = 1 << 16; /// A light shader node static const uint32_t UNLIT = 1 << 17; /// An unlit surface shader node // Types based on nodegroup static const uint32_t SAMPLE2D = 1 << 18; /// Can be sampled in 2D (uv space) static const uint32_t SAMPLE3D = 1 << 19; /// Can be sampled in 3D (position) static const uint32_t GEOMETRIC = 1 << 20; /// Geometric input static const uint32_t DOT = 1 << 21; /// A dot node }; static const ShaderNodePtr NONE; static const string CONSTANT; static const string DOT; static const string IMAGE; static const string SURFACESHADER; static const string SCATTER_MODE; static const string BSDF_R; static const string BSDF_T; static const string TRANSFORM_POINT; static const string TRANSFORM_VECTOR; static const string TRANSFORM_NORMAL; static const string TEXTURE2D_GROUPNAME; static const string TEXTURE3D_GROUPNAME; static const string PROCEDURAL2D_GROUPNAME; static const string PROCEDURAL3D_GROUPNAME; static const string GEOMETRIC_GROUPNAME; public: /// Constructor. ShaderNode(const ShaderGraph* parent, const string& name); /// Create a new node from a nodedef. static ShaderNodePtr create(const ShaderGraph* parent, const string& name, const NodeDef& nodeDef, GenContext& context); /// Create a new node from a node implementation. static ShaderNodePtr create(const ShaderGraph* parent, const string& name, ShaderNodeImplPtr impl, unsigned int classification = Classification::TEXTURE); /// Return true if this node is a graph. virtual bool isAGraph() const { return false; } /// Return the parent graph that owns this node. /// If this node is a root graph it has no parent /// and nullptr will be returned. const ShaderGraph* getParent() const { return _parent; } /// Set classification bits for this node, /// replacing any previous set bits. void setClassification(uint32_t c) { _classification = c; } /// Get classification bits set for this node. uint32_t getClassification() const { return _classification; } /// Add classification bits to this node. void addClassification(uint32_t c) { _classification |= c; } /// Return true if this node matches the given classification. bool hasClassification(uint32_t c) const { return (_classification & c) == c; } /// Return the name of this node. const string& getName() const { return _name; } /// Return the implementation used for this node. const ShaderNodeImpl& getImplementation() const { return *_impl; } /// Initialize this shader node with all required data /// from the given node and nodedef. void initialize(const Node& node, const NodeDef& nodeDef, GenContext& context); /// Add inputs/outputs ShaderInput* addInput(const string& name, const TypeDesc* type); ShaderOutput* addOutput(const string& name, const TypeDesc* type); /// Get number of inputs/outputs size_t numInputs() const { return _inputOrder.size(); } size_t numOutputs() const { return _outputOrder.size(); } /// Get inputs/outputs by index ShaderInput* getInput(size_t index) { return _inputOrder[index]; } ShaderOutput* getOutput(size_t index = 0) { return _outputOrder[index]; } const ShaderInput* getInput(size_t index) const { return _inputOrder[index]; } const ShaderOutput* getOutput(size_t index = 0) const { return _outputOrder[index]; } /// Get inputs/outputs by name ShaderInput* getInput(const string& name); ShaderOutput* getOutput(const string& name); const ShaderInput* getInput(const string& name) const; const ShaderOutput* getOutput(const string& name) const; /// Get vector of inputs/outputs const vector& getInputs() const { return _inputOrder; } const vector& getOutputs() const { return _outputOrder; } /// Set the metadata vector. void setMetadata(ShaderMetadataVecPtr metadata) { _metadata = metadata; } /// Get the metadata vector. ShaderMetadataVecPtr getMetadata() { return _metadata; } /// Get the metadata vector. const ShaderMetadataVecPtr& getMetadata() const { return _metadata; } /// Returns true if an input is editable by users. /// Editable inputs are allowed to be published as shader uniforms /// and hence must be presentable in a user interface. bool isEditable(const ShaderInput& input) const { return (!_impl || _impl->isEditable(input)); } /// Returns true if a graph input is accessible by users. /// Editable inputs are allowed to be published as shader uniforms /// and hence must be presentable in a user interface. bool isEditable(const ShaderGraphInputSocket& input) const { return (!_impl || _impl->isEditable(input)); } protected: /// Create metadata from the nodedef according to registered metadata. void createMetadata(const NodeDef& nodeDef, GenContext& context); const ShaderGraph* _parent; string _name; uint32_t _classification; std::unordered_map _inputMap; vector _inputOrder; std::unordered_map _outputMap; vector _outputOrder; ShaderNodeImplPtr _impl; ShaderMetadataVecPtr _metadata; friend class ShaderGraph; }; MATERIALX_NAMESPACE_END #endif