User:KevinDietrich/Cycles Node Definition Language
Cycles Node Definition Language
This is a sketch of a language I designed to define Cycles Nodes.
Rationale
The goals were:
- to cleanup usage of macros, and liberate ourselves from their shortcomings
- to have a system to properly document each socket, with categories and tooltips/documentation for user interfaces
- to more easily add data to SocketType (e.g. min/max information, doing so would require modifying a lot of macros currently)
- to automate data validation e.g. the values of some socket may depend on the values of another one, although we could do this manually, automating this could lead to better code, fewer bugs due to typos
- to support "socket groups", similar to the previous points, we could associate multiple sockets into groups to ensure that they are updated at the same with sensible values
- to properly separate public and private APIs, where the public part is the one define from the metadata, and the private part is whatever Cycles needs internally to work on Nodes during Scene updates
- to improve static initialization of reflection data, and allow for easy access to every NodeType's reflection data without having to include every header files, or creating a bogus Session (which is useful for generating UIs in external software using this metadata)
The idea was to define all of the metadata (sockets, documentation, etc.) of the various Nodes in files and generate C++ code from them at compile time. The generated C++ code would be a single base class for each Node containing just the socket types, from this base class Cycles would derive its own final class with any other members that it desires.
For example, the code for the Mesh Node would have been:
// This class is generated from the language
class MeshNode : public GeometryNode {
// sockets go here
public:
// sockets accessors go here
};
// Then defined by Cycles
class Mesh : public MeshNode {
// members only used internally by Cycles go here
public:
// internal methods go here
};
Only the MeshNode class would be exposed to the Cycles public API. Cycles would be allocating a Mesh, but returning a MeshNode whenever a new Mesh Node is requested, and similarly for any other Node type.
The main issue with this approach, is that base classes, like Geometry, are lost, or rather, it cat get tricky to treat Meshes, Hairs, and Volumes, as a simple pointer to some Geometry. Multiple inheritance could be considered though.
Example Code
// Possible data types for a Cycles socket
// The | is used to indicate that is also possible for a type to be used in an array
// so, int|array means we can have a socket of type int, and one of type array<int>.
SocketDataType {
int|array
uint
bool|array
Node|array
float|array
float2|array
float3|array
Transform|array
}
// Main structure to hold static data representing a socket.
SocketType {
type SocketDataType
name ustring
struct_offset int
default_value *void
enum_values *NodeEnum
node_type *NodeType
flags int
ui_name ustring
modified_flag_bit SocketModifiedFlags
}
// Define the interface of the SocketType class
interface SocketType {
size() -> size_t
is_array() -> bool
size(SocketType) -> size_t @static
max_size() -> size_t @static
type_name(SocketDataType) -> ustring @static
zero_default_value() -> *void @static
is_float3(SocketDataType) -> bool @static
}
// Base class for Nodes
Node {
name ustring
owner *NodeOwner
modified_flags SocketModifiedFlags
}
// Define the interface of the Node class
interface Node {
get(SocketType) -> SocketType.type
set(SocketType, Socket.type)
:doc:
Return whether any of the socket on the Node was modified.
:enddoc:
is_modified() -> bool
is_socket_modified(SocketType) -> bool
}
// Defines the Mesh Node
Mesh : Node {
:socket triangles:
type = float3*array
min = 0
max = 1
ui = "Triangles"
:endsocket:
:socket num_ngons:
min = 0
max = subd_corners.size
ui = "Number of N-Gons"
:endsocket:
:socket used_shaders:
type = Shader*array
ui = "Used Shaders"
:endsocket:
...
}
// Defines the Particle Node (note that this is currently not a Node).
Particle : Node {
:socket position:
type = float3
ui = "Position"
:endsocket:
...
}
// Defines the ParticleSystem Node.
ParticleSystem : Node {
:socket particles:
type = Particle*array
ui = "Particles"
:endsocket:
...
}