Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# SPDX-FileCopyrightText: 2011-2022 Blender Foundation
#
# SPDX-License-Identifier: Apache-2.0
set(INC
..
)
set(INC_SYS
)
set(SRC
node.cpp
node_type.cpp
node_xml.cpp
)
set(SRC_HEADERS
node.h
node_enum.h
node_type.h
node_xml.h
)
set(LIB
PUBLIC cycles_util
PUBLIC bf::dependencies::optional::pugixml
)
include_directories(${INC})
include_directories(SYSTEM ${INC_SYS})
cycles_add_library(cycles_graph "${LIB}" ${SRC} ${SRC_HEADERS})

View File

@@ -0,0 +1,910 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "graph/node.h"
#include "graph/node_type.h"
#include "util/md5.h"
#include "util/param.h"
#include "util/transform.h"
CCL_NAMESPACE_BEGIN
/* Node Type */
NodeOwner::~NodeOwner() = default;
Node::Node(const NodeType *type_, ustring name_) : name(name_), type(type_)
{
assert(type);
owner = nullptr;
tag_modified();
/* assign non-empty name, convenient for debugging */
if (name.empty()) {
name = type->name;
}
/* initialize default values */
for (const SocketType &socket : type->inputs) {
set_default_value(socket);
}
}
Node::~Node() {}
#ifndef NDEBUG
static bool is_socket_float3(const SocketType &socket)
{
return socket.type == SocketType::COLOR || socket.type == SocketType::POINT ||
socket.type == SocketType::VECTOR || socket.type == SocketType::NORMAL;
}
static bool is_socket_array_float3(const SocketType &socket)
{
return socket.type == SocketType::COLOR_ARRAY || socket.type == SocketType::POINT_ARRAY ||
socket.type == SocketType::VECTOR_ARRAY || socket.type == SocketType::NORMAL_ARRAY;
}
#endif
/* set values */
void Node::set(const SocketType &input, bool value)
{
assert(input.type == SocketType::BOOLEAN);
set_if_different(input, value);
}
void Node::set(const SocketType &input, const int value)
{
assert((input.type == SocketType::INT || input.type == SocketType::ENUM));
set_if_different(input, value);
}
void Node::set(const SocketType &input, const uint value)
{
assert(input.type == SocketType::UINT);
set_if_different(input, value);
}
void Node::set(const SocketType &input, const uint64_t value)
{
assert(input.type == SocketType::UINT64);
set_if_different(input, value);
}
void Node::set(const SocketType &input, const float value)
{
assert(input.type == SocketType::FLOAT);
set_if_different(input, value);
}
void Node::set(const SocketType &input, const float2 value)
{
assert(input.type == SocketType::POINT2);
set_if_different(input, value);
}
void Node::set(const SocketType &input, const float3 value)
{
assert(is_socket_float3(input));
set_if_different(input, value);
}
void Node::set(const SocketType &input, const char *value)
{
set(input, ustring(value));
}
void Node::set(const SocketType &input, ustring value)
{
if (input.type == SocketType::STRING) {
set_if_different(input, value);
}
else if (input.type == SocketType::ENUM) {
const NodeEnum &enm = *input.enum_values;
if (enm.exists(value)) {
set_if_different(input, enm[value]);
}
else {
assert(0);
}
}
else {
assert(0);
}
}
void Node::set(const SocketType &input, const Transform &value)
{
assert(input.type == SocketType::TRANSFORM);
set_if_different(input, value);
}
void Node::set(const SocketType &input, Node *value)
{
assert(input.type == SocketType::NODE);
set_if_different(input, value);
}
/* set array values */
void Node::set(const SocketType &input, array<bool> &value)
{
assert(input.type == SocketType::BOOLEAN_ARRAY);
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<int> &value)
{
assert(input.type == SocketType::INT_ARRAY);
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<float> &value)
{
assert(input.type == SocketType::FLOAT_ARRAY);
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<float2> &value)
{
assert(input.type == SocketType::POINT2_ARRAY);
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<packed_float3> &value)
{
assert(is_socket_array_float3(input));
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<ustring> &value)
{
assert(input.type == SocketType::STRING_ARRAY);
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<Transform> &value)
{
assert(input.type == SocketType::TRANSFORM_ARRAY);
set_if_different(input, value);
}
void Node::set(const SocketType &input, array<Node *> &value)
{
assert(input.type == SocketType::NODE_ARRAY);
set_if_different(input, value);
}
/* get values */
bool Node::get_bool(const SocketType &input) const
{
assert(input.type == SocketType::BOOLEAN);
return get_socket_value<bool>(this, input);
}
int Node::get_int(const SocketType &input) const
{
assert(input.type == SocketType::INT || input.type == SocketType::ENUM);
return get_socket_value<int>(this, input);
}
uint Node::get_uint(const SocketType &input) const
{
assert(input.type == SocketType::UINT);
return get_socket_value<uint>(this, input);
}
uint64_t Node::get_uint64(const SocketType &input) const
{
assert(input.type == SocketType::UINT64);
return get_socket_value<uint64_t>(this, input);
}
float Node::get_float(const SocketType &input) const
{
assert(input.type == SocketType::FLOAT);
return get_socket_value<float>(this, input);
}
float2 Node::get_float2(const SocketType &input) const
{
assert(input.type == SocketType::POINT2);
return get_socket_value<float2>(this, input);
}
float3 Node::get_float3(const SocketType &input) const
{
assert(is_socket_float3(input));
return get_socket_value<float3>(this, input);
}
ustring Node::get_string(const SocketType &input) const
{
if (input.type == SocketType::STRING) {
return get_socket_value<ustring>(this, input);
}
if (input.type == SocketType::ENUM) {
const NodeEnum &enm = *input.enum_values;
const int intvalue = get_socket_value<int>(this, input);
return (enm.exists(intvalue)) ? enm[intvalue] : ustring();
}
assert(0);
return ustring();
}
Transform Node::get_transform(const SocketType &input) const
{
assert(input.type == SocketType::TRANSFORM);
return get_socket_value<Transform>(this, input);
}
Node *Node::get_node(const SocketType &input) const
{
assert(input.type == SocketType::NODE);
return get_socket_value<Node *>(this, input);
}
/* get array values */
const array<bool> &Node::get_bool_array(const SocketType &input) const
{
assert(input.type == SocketType::BOOLEAN_ARRAY);
return get_socket_value<array<bool>>(this, input);
}
const array<int> &Node::get_int_array(const SocketType &input) const
{
assert(input.type == SocketType::INT_ARRAY);
return get_socket_value<array<int>>(this, input);
}
const array<float> &Node::get_float_array(const SocketType &input) const
{
assert(input.type == SocketType::FLOAT_ARRAY);
return get_socket_value<array<float>>(this, input);
}
const array<float2> &Node::get_float2_array(const SocketType &input) const
{
assert(input.type == SocketType::POINT2_ARRAY);
return get_socket_value<array<float2>>(this, input);
}
const array<packed_float3> &Node::get_float3_array(const SocketType &input) const
{
assert(is_socket_array_float3(input));
return get_socket_value<array<packed_float3>>(this, input);
}
const array<ustring> &Node::get_string_array(const SocketType &input) const
{
assert(input.type == SocketType::STRING_ARRAY);
return get_socket_value<array<ustring>>(this, input);
}
const array<Transform> &Node::get_transform_array(const SocketType &input) const
{
assert(input.type == SocketType::TRANSFORM_ARRAY);
return get_socket_value<array<Transform>>(this, input);
}
const array<Node *> &Node::get_node_array(const SocketType &input) const
{
assert(input.type == SocketType::NODE_ARRAY);
return get_socket_value<array<Node *>>(this, input);
}
/* generic value operations */
bool Node::has_default_value(const SocketType &input) const
{
const void *src = input.default_value;
void *dst = &get_socket_value<char>(this, input);
return memcmp(dst, src, input.packed_size()) == 0;
}
void Node::set_default_value(const SocketType &input)
{
const void *src = input.default_value;
void *dst = ((char *)this) + input.struct_offset;
if (input.packed_size() > 0) {
memcpy(dst, src, input.packed_size());
}
}
template<typename T>
static void copy_array(const Node *node,
const SocketType &socket,
const Node *other,
const SocketType &other_socket)
{
const array<T> *src = (const array<T> *)(((char *)other) + other_socket.struct_offset);
array<T> *dst = (array<T> *)(((char *)node) + socket.struct_offset);
*dst = *src;
}
void Node::copy_value(const SocketType &socket, const Node &other, const SocketType &other_socket)
{
assert(socket.type == other_socket.type);
if (socket.is_array()) {
switch (socket.type) {
case SocketType::BOOLEAN_ARRAY:
copy_array<bool>(this, socket, &other, other_socket);
break;
case SocketType::FLOAT_ARRAY:
copy_array<float>(this, socket, &other, other_socket);
break;
case SocketType::INT_ARRAY:
copy_array<int>(this, socket, &other, other_socket);
break;
case SocketType::COLOR_ARRAY:
copy_array<packed_float3>(this, socket, &other, other_socket);
break;
case SocketType::VECTOR_ARRAY:
copy_array<packed_float3>(this, socket, &other, other_socket);
break;
case SocketType::POINT_ARRAY:
copy_array<packed_float3>(this, socket, &other, other_socket);
break;
case SocketType::NORMAL_ARRAY:
copy_array<packed_float3>(this, socket, &other, other_socket);
break;
case SocketType::POINT2_ARRAY:
copy_array<float2>(this, socket, &other, other_socket);
break;
case SocketType::STRING_ARRAY:
copy_array<ustring>(this, socket, &other, other_socket);
break;
case SocketType::TRANSFORM_ARRAY:
copy_array<Transform>(this, socket, &other, other_socket);
break;
case SocketType::NODE_ARRAY: {
copy_array<void *>(this, socket, &other, other_socket);
const array<Node *> &node_array = get_socket_value<array<Node *>>(this, socket);
for (Node *node : node_array) {
node->reference();
}
break;
}
default:
assert(0);
break;
}
}
else {
const void *src = ((char *)&other) + other_socket.struct_offset;
void *dst = ((char *)this) + socket.struct_offset;
memcpy(dst, src, socket.storage_size());
if (socket.type == SocketType::NODE) {
Node *node = get_socket_value<Node *>(this, socket);
if (node) {
node->reference();
}
}
}
}
void Node::set_value(const SocketType &socket, const Node &other, const SocketType &other_socket)
{
assert(socket.type == other_socket.type);
(void)other_socket;
if (socket.is_array()) {
switch (socket.type) {
case SocketType::BOOLEAN_ARRAY:
set(socket, get_socket_value<array<bool>>(&other, socket));
break;
case SocketType::FLOAT_ARRAY:
set(socket, get_socket_value<array<float>>(&other, socket));
break;
case SocketType::INT_ARRAY:
set(socket, get_socket_value<array<int>>(&other, socket));
break;
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY:
set(socket, get_socket_value<array<packed_float3>>(&other, socket));
break;
case SocketType::POINT2_ARRAY:
set(socket, get_socket_value<array<float2>>(&other, socket));
break;
case SocketType::STRING_ARRAY:
set(socket, get_socket_value<array<ustring>>(&other, socket));
break;
case SocketType::TRANSFORM_ARRAY:
set(socket, get_socket_value<array<Transform>>(&other, socket));
break;
case SocketType::NODE_ARRAY:
set(socket, get_socket_value<array<Node *>>(&other, socket));
break;
default:
assert(0);
break;
}
}
else {
switch (socket.type) {
case SocketType::BOOLEAN:
set(socket, get_socket_value<bool>(&other, socket));
break;
case SocketType::FLOAT:
set(socket, get_socket_value<float>(&other, socket));
break;
case SocketType::INT:
set(socket, get_socket_value<int>(&other, socket));
break;
case SocketType::UINT:
set(socket, get_socket_value<uint>(&other, socket));
break;
case SocketType::UINT64:
set(socket, get_socket_value<uint64_t>(&other, socket));
break;
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
set(socket, get_socket_value<float3>(&other, socket));
break;
case SocketType::POINT2:
set(socket, get_socket_value<float2>(&other, socket));
break;
case SocketType::STRING:
set(socket, get_socket_value<ustring>(&other, socket));
break;
case SocketType::ENUM:
set(socket, get_socket_value<int>(&other, socket));
break;
case SocketType::TRANSFORM:
set(socket, get_socket_value<Transform>(&other, socket));
break;
case SocketType::NODE:
set(socket, get_socket_value<Node *>(&other, socket));
break;
default:
assert(0);
break;
}
}
}
template<typename T>
static bool is_array_equal(const Node *node, const Node *other, const SocketType &socket)
{
const array<T> *a = (const array<T> *)(((char *)node) + socket.struct_offset);
const array<T> *b = (const array<T> *)(((char *)other) + socket.struct_offset);
return *a == *b;
}
template<typename T>
static bool is_value_equal(const Node *node, const Node *other, const SocketType &socket)
{
const T *a = (const T *)(((char *)node) + socket.struct_offset);
const T *b = (const T *)(((char *)other) + socket.struct_offset);
return *a == *b;
}
bool Node::equals_value(const Node &other, const SocketType &socket) const
{
switch (socket.type) {
case SocketType::BOOLEAN:
return is_value_equal<bool>(this, &other, socket);
case SocketType::FLOAT:
return is_value_equal<float>(this, &other, socket);
case SocketType::INT:
return is_value_equal<int>(this, &other, socket);
case SocketType::UINT:
return is_value_equal<uint>(this, &other, socket);
case SocketType::UINT64:
return is_value_equal<uint64_t>(this, &other, socket);
case SocketType::COLOR:
return is_value_equal<float3>(this, &other, socket);
case SocketType::VECTOR:
return is_value_equal<float3>(this, &other, socket);
case SocketType::POINT:
return is_value_equal<float3>(this, &other, socket);
case SocketType::NORMAL:
return is_value_equal<float3>(this, &other, socket);
case SocketType::POINT2:
return is_value_equal<float2>(this, &other, socket);
case SocketType::CLOSURE:
return true;
case SocketType::STRING:
return is_value_equal<ustring>(this, &other, socket);
case SocketType::ENUM:
return is_value_equal<int>(this, &other, socket);
case SocketType::TRANSFORM:
return is_value_equal<Transform>(this, &other, socket);
case SocketType::NODE:
return is_value_equal<void *>(this, &other, socket);
case SocketType::BOOLEAN_ARRAY:
return is_array_equal<bool>(this, &other, socket);
case SocketType::FLOAT_ARRAY:
return is_array_equal<float>(this, &other, socket);
case SocketType::INT_ARRAY:
return is_array_equal<int>(this, &other, socket);
case SocketType::COLOR_ARRAY:
return is_array_equal<packed_float3>(this, &other, socket);
case SocketType::VECTOR_ARRAY:
return is_array_equal<packed_float3>(this, &other, socket);
case SocketType::POINT_ARRAY:
return is_array_equal<packed_float3>(this, &other, socket);
case SocketType::NORMAL_ARRAY:
return is_array_equal<packed_float3>(this, &other, socket);
case SocketType::POINT2_ARRAY:
return is_array_equal<float2>(this, &other, socket);
case SocketType::STRING_ARRAY:
return is_array_equal<ustring>(this, &other, socket);
case SocketType::TRANSFORM_ARRAY:
return is_array_equal<Transform>(this, &other, socket);
case SocketType::NODE_ARRAY:
return is_array_equal<void *>(this, &other, socket);
case SocketType::UNDEFINED:
case SocketType::NUM_TYPES:
return true;
}
return true;
}
/* equals */
bool Node::equals(const Node &other) const
{
assert(type == other.type);
for (const SocketType &socket : type->inputs) {
if (!equals_value(other, socket)) {
return false;
}
}
return true;
}
/* Hash */
namespace {
template<typename T> void value_hash(const Node *node, const SocketType &socket, MD5Hash &md5)
{
md5.append(((uint8_t *)node) + socket.struct_offset, socket.packed_size());
}
void float3_hash(const Node *node, const SocketType &socket, MD5Hash &md5)
{
/* Don't compare 4th element used for padding. */
md5.append(((uint8_t *)node) + socket.struct_offset, sizeof(float) * 3);
}
template<typename T> void array_hash(const Node *node, const SocketType &socket, MD5Hash &md5)
{
const array<T> &a = *(const array<T> *)(((char *)node) + socket.struct_offset);
for (size_t i = 0; i < a.size(); i++) {
md5.append((uint8_t *)&a[i], sizeof(T));
}
}
void float3_array_hash(const Node *node, const SocketType &socket, MD5Hash &md5)
{
const array<packed_float3> &a = *(const array<packed_float3> *)(((char *)node) +
socket.struct_offset);
for (size_t i = 0; i < a.size(); i++) {
md5.append((uint8_t *)&a[i], sizeof(packed_float3));
}
}
} // namespace
void Node::hash(MD5Hash &md5)
{
md5.append(type->name.string());
for (const SocketType &socket : type->inputs) {
md5.append(socket.name.string());
switch (socket.type) {
case SocketType::BOOLEAN:
value_hash<bool>(this, socket, md5);
break;
case SocketType::FLOAT:
value_hash<float>(this, socket, md5);
break;
case SocketType::INT:
value_hash<int>(this, socket, md5);
break;
case SocketType::UINT:
value_hash<uint>(this, socket, md5);
break;
case SocketType::UINT64:
value_hash<uint64_t>(this, socket, md5);
break;
case SocketType::COLOR:
float3_hash(this, socket, md5);
break;
case SocketType::VECTOR:
float3_hash(this, socket, md5);
break;
case SocketType::POINT:
float3_hash(this, socket, md5);
break;
case SocketType::NORMAL:
float3_hash(this, socket, md5);
break;
case SocketType::POINT2:
value_hash<float2>(this, socket, md5);
break;
case SocketType::CLOSURE:
break;
case SocketType::STRING:
value_hash<ustring>(this, socket, md5);
break;
case SocketType::ENUM:
value_hash<int>(this, socket, md5);
break;
case SocketType::TRANSFORM:
value_hash<Transform>(this, socket, md5);
break;
case SocketType::NODE:
value_hash<void *>(this, socket, md5);
break;
case SocketType::BOOLEAN_ARRAY:
array_hash<bool>(this, socket, md5);
break;
case SocketType::FLOAT_ARRAY:
array_hash<float>(this, socket, md5);
break;
case SocketType::INT_ARRAY:
array_hash<int>(this, socket, md5);
break;
case SocketType::COLOR_ARRAY:
float3_array_hash(this, socket, md5);
break;
case SocketType::VECTOR_ARRAY:
float3_array_hash(this, socket, md5);
break;
case SocketType::POINT_ARRAY:
float3_array_hash(this, socket, md5);
break;
case SocketType::NORMAL_ARRAY:
float3_array_hash(this, socket, md5);
break;
case SocketType::POINT2_ARRAY:
array_hash<float2>(this, socket, md5);
break;
case SocketType::STRING_ARRAY:
array_hash<ustring>(this, socket, md5);
break;
case SocketType::TRANSFORM_ARRAY:
array_hash<Transform>(this, socket, md5);
break;
case SocketType::NODE_ARRAY:
array_hash<void *>(this, socket, md5);
break;
case SocketType::UNDEFINED:
case SocketType::NUM_TYPES:
break;
}
}
}
namespace {
template<typename T> size_t array_size_in_bytes(const Node *node, const SocketType &socket)
{
const array<T> &a = *(const array<T> *)(((char *)node) + socket.struct_offset);
return a.size() * sizeof(T);
}
} // namespace
size_t Node::get_total_size_in_bytes() const
{
size_t total_size = 0;
for (const SocketType &socket : type->inputs) {
switch (socket.type) {
case SocketType::BOOLEAN:
case SocketType::FLOAT:
case SocketType::INT:
case SocketType::UINT:
case SocketType::UINT64:
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL:
case SocketType::POINT2:
case SocketType::CLOSURE:
case SocketType::STRING:
case SocketType::ENUM:
case SocketType::TRANSFORM:
case SocketType::NODE:
total_size += socket.storage_size();
break;
case SocketType::BOOLEAN_ARRAY:
total_size += array_size_in_bytes<bool>(this, socket);
break;
case SocketType::FLOAT_ARRAY:
total_size += array_size_in_bytes<float>(this, socket);
break;
case SocketType::INT_ARRAY:
total_size += array_size_in_bytes<int>(this, socket);
break;
case SocketType::COLOR_ARRAY:
total_size += array_size_in_bytes<float3>(this, socket);
break;
case SocketType::VECTOR_ARRAY:
total_size += array_size_in_bytes<float3>(this, socket);
break;
case SocketType::POINT_ARRAY:
total_size += array_size_in_bytes<float3>(this, socket);
break;
case SocketType::NORMAL_ARRAY:
total_size += array_size_in_bytes<float3>(this, socket);
break;
case SocketType::POINT2_ARRAY:
total_size += array_size_in_bytes<float2>(this, socket);
break;
case SocketType::STRING_ARRAY:
total_size += array_size_in_bytes<ustring>(this, socket);
break;
case SocketType::TRANSFORM_ARRAY:
total_size += array_size_in_bytes<Transform>(this, socket);
break;
case SocketType::NODE_ARRAY:
total_size += array_size_in_bytes<void *>(this, socket);
break;
case SocketType::UNDEFINED:
case SocketType::NUM_TYPES:
break;
}
}
return total_size;
}
bool Node::is_a(const NodeType *type_)
{
for (const NodeType *base = type; base; base = base->base) {
if (base == type_) {
return true;
}
}
return false;
}
const NodeOwner *Node::get_owner() const
{
return owner;
}
void Node::set_owner(const NodeOwner *owner_)
{
assert(owner_);
owner = owner_;
}
void Node::dereference_all_used_nodes()
{
for (const SocketType &socket : type->inputs) {
if (socket.type == SocketType::NODE) {
Node *node = get_socket_value<Node *>(this, socket);
if (node) {
node->dereference();
}
}
else if (socket.type == SocketType::NODE_ARRAY) {
const array<Node *> &nodes = get_socket_value<array<Node *>>(this, socket);
for (Node *node : nodes) {
node->dereference();
}
}
}
}
bool Node::socket_is_modified(const SocketType &input) const
{
return socket_modified.test(input.modified_flag_bit);
}
bool Node::is_modified() const
{
return socket_modified.any();
}
void Node::tag_modified()
{
socket_modified.set();
}
void Node::clear_modified()
{
socket_modified.reset();
}
template<typename T> void Node::set_if_different(const SocketType &input, T value)
{
if (get_socket_value<T>(this, input) == value) {
return;
}
get_socket_value<T>(this, input) = value;
socket_modified.set(input.modified_flag_bit);
}
void Node::set_if_different(const SocketType &input, Node *value)
{
if (get_socket_value<Node *>(this, input) == value) {
return;
}
Node *old_node = get_socket_value<Node *>(this, input);
if (old_node) {
old_node->dereference();
}
if (value) {
value->reference();
}
get_socket_value<Node *>(this, input) = value;
socket_modified.set(input.modified_flag_bit);
}
template<typename T> void Node::set_if_different(const SocketType &input, array<T> &value)
{
if (!socket_is_modified(input)) {
if (get_socket_value<array<T>>(this, input) == value) {
return;
}
}
get_socket_value<array<T>>(this, input).steal_data(value);
socket_modified.set(input.modified_flag_bit);
}
void Node::set_if_different(const SocketType &input, array<Node *> &value)
{
if (!socket_is_modified(input)) {
if (get_socket_value<array<Node *>>(this, input) == value) {
return;
}
}
const array<Node *> &old_nodes = get_socket_value<array<Node *>>(this, input);
for (Node *old_node : old_nodes) {
old_node->dereference();
}
for (Node *new_node : value) {
new_node->reference();
}
get_socket_value<array<Node *>>(this, input).steal_data(value);
socket_modified.set(input.modified_flag_bit);
}
void Node::print_modified_sockets() const
{
printf("Node : %s\n", name.c_str());
for (const auto &socket : type->inputs) {
if (socket_is_modified(socket)) {
printf("-- socket modified : %s\n", socket.name.c_str());
}
}
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,235 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include <type_traits>
#include "graph/node_type.h"
#include "util/array.h"
#include "util/param.h"
#include "util/types.h"
CCL_NAMESPACE_BEGIN
class MD5Hash;
struct Node;
struct NodeType;
struct Transform;
/* NOTE: in the following macros we use "type const &" instead of "const type &"
* to avoid issues when pasting a pointer type. */
#define NODE_SOCKET_API_BASE_METHODS(type_, name, string_name) \
const SocketType *get_##name##_socket() const \
{ \
/* Explicitly cast to base class to use `Node::type` even if the derived class defines \
* `type`. */ \
const Node *self_node = this; \
static const SocketType *socket = self_node->type->find_input(ustring(string_name)); \
return socket; \
} \
bool name##_is_modified() const \
{ \
const SocketType *socket = get_##name##_socket(); \
return socket_is_modified(*socket); \
} \
void tag_##name##_modified() \
{ \
const SocketType *socket = get_##name##_socket(); \
socket_modified.set(socket->modified_flag_bit); \
} \
type_ const &get_##name() const \
{ \
const SocketType *socket = get_##name##_socket(); \
return get_socket_value<type_>(this, *socket); \
}
#define NODE_SOCKET_API_BASE(type_, name, string_name) \
protected: \
type_ name; \
\
public: \
NODE_SOCKET_API_BASE_METHODS(type_, name, string_name)
#define NODE_SOCKET_API(type_, name) \
NODE_SOCKET_API_BASE(type_, name, #name) \
void set_##name(type_ value) \
{ \
const SocketType *socket = get_##name##_socket(); \
this->set(*socket, value); \
}
#define NODE_SOCKET_API_ARRAY(type_, name) \
NODE_SOCKET_API_BASE(type_, name, #name) \
void set_##name(type_ &value) \
{ \
const SocketType *socket = get_##name##_socket(); \
this->set(*socket, value); \
} \
type_ &get_##name() \
{ \
const SocketType *socket = get_##name##_socket(); \
return get_socket_value<type_>(this, *socket); \
}
#define NODE_SOCKET_API_STRUCT_MEMBER(type_, name, member) \
NODE_SOCKET_API_BASE_METHODS(type_, name##_##member, #name "." #member) \
void set_##name##_##member(type_ value) \
{ \
const SocketType *socket = get_##name##_##member##_socket(); \
this->set(*socket, value); \
}
/* Node */
struct NodeOwner {
virtual ~NodeOwner();
};
struct Node {
explicit Node(const NodeType *type, ustring name = ustring());
virtual ~Node() = 0;
/* set values */
void set(const SocketType &input, bool value);
void set(const SocketType &input, const int value);
void set(const SocketType &input, const uint value);
void set(const SocketType &input, const uint64_t value);
void set(const SocketType &input, const float value);
void set(const SocketType &input, const float2 value);
void set(const SocketType &input, const float3 value);
void set(const SocketType &input, const char *value);
void set(const SocketType &input, ustring value);
void set(const SocketType &input, const Transform &value);
void set(const SocketType &input, Node *value);
/* Implicitly cast enums and enum classes to integer, which matches an internal way of how
* enumerator values are stored and accessed in a generic API. */
template<class ValueType, std::enable_if_t<std::is_enum_v<ValueType>, bool> = true>
void set(const SocketType &input, const ValueType &value)
{
static_assert(sizeof(ValueType) <= sizeof(int), "Enumerator type should fit int");
set(input, static_cast<int>(value));
}
/* set array values. the memory from the input array will taken over
* by the node and the input array will be empty after return */
void set(const SocketType &input, array<bool> &value);
void set(const SocketType &input, array<int> &value);
void set(const SocketType &input, array<float> &value);
void set(const SocketType &input, array<float2> &value);
void set(const SocketType &input, array<packed_float3> &value);
void set(const SocketType &input, array<ustring> &value);
void set(const SocketType &input, array<Transform> &value);
void set(const SocketType &input, array<Node *> &value);
/* get values */
bool get_bool(const SocketType &input) const;
int get_int(const SocketType &input) const;
uint get_uint(const SocketType &input) const;
uint64_t get_uint64(const SocketType &input) const;
float get_float(const SocketType &input) const;
float2 get_float2(const SocketType &input) const;
float3 get_float3(const SocketType &input) const;
ustring get_string(const SocketType &input) const;
Transform get_transform(const SocketType &input) const;
Node *get_node(const SocketType &input) const;
/* get array values */
const array<bool> &get_bool_array(const SocketType &input) const;
const array<int> &get_int_array(const SocketType &input) const;
const array<float> &get_float_array(const SocketType &input) const;
const array<float2> &get_float2_array(const SocketType &input) const;
const array<packed_float3> &get_float3_array(const SocketType &input) const;
const array<ustring> &get_string_array(const SocketType &input) const;
const array<Transform> &get_transform_array(const SocketType &input) const;
const array<Node *> &get_node_array(const SocketType &input) const;
/* generic values operations */
bool has_default_value(const SocketType &input) const;
void set_default_value(const SocketType &input);
bool equals_value(const Node &other, const SocketType &socket) const;
void copy_value(const SocketType &socket, const Node &other, const SocketType &other_socket);
void set_value(const SocketType &socket, const Node &other, const SocketType &other_socket);
/* equals */
bool equals(const Node &other) const;
/* compute hash of node and its socket values */
void hash(MD5Hash &md5);
/* Get total size of this node. */
size_t get_total_size_in_bytes() const;
/* Type testing, taking into account base classes. */
bool is_a(const NodeType *type);
bool socket_is_modified(const SocketType &input) const;
bool is_modified() const;
void tag_modified();
void clear_modified();
void print_modified_sockets() const;
ustring name;
const NodeType *type;
const NodeOwner *get_owner() const;
void set_owner(const NodeOwner *owner_);
int reference_count() const
{
return ref_count;
}
void reference()
{
ref_count += 1;
}
void dereference()
{
ref_count -= 1;
}
/* Set the reference count to zero. This should only be called when we know for sure that the
* Node is not used by anyone else. For now, this is only the case when "deleting" shaders, as
* they are never actually deleted. */
void clear_reference_count()
{
ref_count = 0;
}
protected:
const NodeOwner *owner;
int ref_count{0};
template<typename T> static T &get_socket_value(const Node *node, const SocketType &socket)
{
return (T &)*(((char *)node) + socket.struct_offset);
}
SocketModifiedFlags socket_modified;
template<typename T> void set_if_different(const SocketType &input, T value);
/* Explicit overload for Node sockets so we can handle reference counting. The old Node is
* dereferenced, and the new one is referenced. */
void set_if_different(const SocketType &input, Node *value);
template<typename T> void set_if_different(const SocketType &input, array<T> &value);
/* Explicit overload for Node sockets so we can handle reference counting. The old Nodes are
* dereferenced, and the new ones are referenced. */
void set_if_different(const SocketType &input, array<Node *> &value);
/* Call this function in derived classes' destructors to ensure that used Nodes are dereferenced
* properly. */
void dereference_all_used_nodes();
};
CCL_NAMESPACE_END

View File

@@ -0,0 +1,65 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include "util/map.h"
#include "util/param.h"
CCL_NAMESPACE_BEGIN
/* Enum
*
* Utility class for enum values. */
struct NodeEnum {
bool empty() const
{
return left.empty();
}
void insert(const char *x, const int y)
{
const ustring ustr_x(x);
left[ustr_x] = y;
right[y] = ustr_x;
}
bool exists(ustring x) const
{
return left.contains(x);
}
bool exists(const int y) const
{
return right.contains(y);
}
int operator[](const char *x) const
{
return left.find(ustring(x))->second;
}
int operator[](ustring x) const
{
return left.find(x)->second;
}
ustring operator[](int y) const
{
return right.find(y)->second;
}
unordered_map<ustring, int>::const_iterator begin() const
{
return left.begin();
}
unordered_map<ustring, int>::const_iterator end() const
{
return left.end();
}
private:
unordered_map<ustring, int> left;
unordered_map<int, ustring> right;
};
CCL_NAMESPACE_END

View File

@@ -0,0 +1,304 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#include "graph/node_type.h"
#include "util/log.h"
#include "util/transform.h"
#include "util/types_float3.h"
CCL_NAMESPACE_BEGIN
/* Node Socket Type */
size_t SocketType::storage_size() const
{
return size(type, false);
}
size_t SocketType::packed_size() const
{
return size(type, true);
}
bool SocketType::is_array() const
{
return (type >= BOOLEAN_ARRAY);
}
size_t SocketType::size(Type type, bool packed)
{
switch (type) {
case UNDEFINED:
case NUM_TYPES:
return 0;
case BOOLEAN:
return sizeof(bool);
case FLOAT:
return sizeof(float);
case INT:
return sizeof(int);
case UINT:
return sizeof(uint);
case UINT64:
return sizeof(uint64_t);
case COLOR:
case VECTOR:
case POINT:
case NORMAL:
return (packed) ? sizeof(packed_float3) : sizeof(float3);
case POINT2:
return sizeof(float2);
case CLOSURE:
return 0;
case STRING:
return sizeof(ustring);
case ENUM:
return sizeof(int);
case TRANSFORM:
return sizeof(Transform);
case NODE:
return sizeof(void *);
case BOOLEAN_ARRAY:
return sizeof(array<bool>);
case FLOAT_ARRAY:
return sizeof(array<float>);
case INT_ARRAY:
return sizeof(array<int>);
case COLOR_ARRAY:
return sizeof(array<packed_float3>);
case VECTOR_ARRAY:
return sizeof(array<packed_float3>);
case POINT_ARRAY:
return sizeof(array<packed_float3>);
case NORMAL_ARRAY:
return sizeof(array<packed_float3>);
case POINT2_ARRAY:
return sizeof(array<float2>);
case STRING_ARRAY:
return sizeof(array<ustring>);
case TRANSFORM_ARRAY:
return sizeof(array<Transform>);
case NODE_ARRAY:
return sizeof(array<void *>);
}
assert(0);
return 0;
}
size_t SocketType::max_size()
{
return sizeof(Transform);
}
void *SocketType::zero_default_value()
{
static Transform zero_transform = transform_zero();
return &zero_transform;
}
ustring SocketType::type_name(Type type)
{
static const ustring names[] = {ustring("undefined"),
ustring("boolean"), ustring("float"),
ustring("int"), ustring("uint"),
ustring("uint64"), ustring("color"),
ustring("vector"), ustring("point"),
ustring("normal"), ustring("point2"),
ustring("closure"), ustring("string"),
ustring("enum"), ustring("transform"),
ustring("node"),
ustring("array_boolean"), ustring("array_float"),
ustring("array_int"), ustring("array_color"),
ustring("array_vector"), ustring("array_point"),
ustring("array_normal"), ustring("array_point2"),
ustring("array_string"), ustring("array_transform"),
ustring("array_node")};
constexpr size_t num_names = sizeof(names) / sizeof(*names);
static_assert(num_names == NUM_TYPES);
return names[(int)type];
}
bool SocketType::is_float3(Type type)
{
return (type == COLOR || type == VECTOR || type == POINT || type == NORMAL);
}
/* Node Type */
NodeType::NodeType(Type type, const NodeType *base) : type(type), base(base)
{
if (base) {
/* Inherit sockets. */
inputs = base->inputs;
outputs = base->outputs;
}
}
NodeType::~NodeType() = default;
void NodeType::register_input(ustring name,
ustring ui_name,
SocketType::Type type,
const int struct_offset,
const void *default_value,
const NodeEnum *enum_values,
const NodeType *node_type,
const int flags,
const int extra_flags)
{
SocketType socket;
socket.name = name;
socket.ui_name = ui_name;
socket.type = type;
socket.struct_offset = struct_offset;
socket.default_value = default_value;
socket.enum_values = enum_values;
socket.node_type = node_type;
socket.flags = flags | extra_flags;
assert(inputs.size() < SocketModifiedFlags().size());
socket.modified_flag_bit = inputs.size();
inputs.push_back(socket);
}
void NodeType::register_output(ustring name, ustring ui_name, SocketType::Type type)
{
SocketType socket;
socket.name = name;
socket.ui_name = ui_name;
socket.type = type;
socket.struct_offset = 0;
socket.default_value = nullptr;
socket.enum_values = nullptr;
socket.node_type = nullptr;
socket.flags = SocketType::LINKABLE;
outputs.push_back(socket);
}
const SocketType *NodeType::find_input(ustring name) const
{
for (const SocketType &socket : inputs) {
if (socket.name == name) {
return &socket;
}
}
return nullptr;
}
const SocketType *NodeType::find_output(ustring name) const
{
for (const SocketType &socket : outputs) {
if (socket.name == name) {
return &socket;
}
}
return nullptr;
}
/* Node Type Registry
*
* We want to be able to find and enumerate types without the need for a centralized
* function for all node types. Due to static initialization order issues and guarded
* allocators, there are some subtle implementation choices.
*
* Only the init functions are gathered on static initialization, as registering the
* node type itself could otherwise happen in the wrong order for base and derived
* types, and Blender's guarded allocator may not have been initialized yet.
*
* The initialization functions are stored in std::vector without guarded allocation
* as that may not been properly initialized yet. */
static thread_mutex &types_mutex()
{
static thread_mutex types_mutex_;
return types_mutex_;
}
static unordered_map<ustring, NodeType> &types()
{
static unordered_map<ustring, NodeType> _types;
return _types;
}
static thread_mutex &types_on_init_mutex()
{
static thread_mutex types_on_init_mutex_;
return types_on_init_mutex_;
}
static std::vector<const NodeType *(*)()> &types_on_init()
{
static std::vector<const NodeType *(*)()> _types_on_init;
return _types_on_init;
}
static void ensure_types_initialized()
{
thread_scoped_lock lock(types_on_init_mutex());
for (const auto &init_func : types_on_init()) {
init_func();
}
types_on_init().clear();
}
bool NodeType::register_on_init(const NodeType *(*init_func)())
{
thread_scoped_lock lock(types_on_init_mutex());
types_on_init().push_back(init_func);
return true;
}
NodeType *NodeType::add(const char *name_, CreateFunc create_, Type type_, const NodeType *base_)
{
const ustring name(name_);
/* Types can be lazily registered from multiple threads. */
thread_scoped_lock lock(types_mutex());
if (types().contains(name)) {
LOG_ERROR << "Node type " << name_ << " registered twice";
assert(0);
return nullptr;
}
types()[name] = NodeType(type_, base_);
NodeType *type = &types()[name];
type->name = name;
type->create = create_;
return type;
}
const NodeType *NodeType::find(ustring name)
{
ensure_types_initialized();
thread_scoped_lock lock(types_mutex());
const unordered_map<ustring, NodeType>::iterator it = types().find(name);
return (it == types().end()) ? nullptr : &it->second;
}
vector<ustring> NodeType::type_names()
{
ensure_types_initialized();
thread_scoped_lock lock(types_mutex());
vector<ustring> names;
for (const auto &pair : types()) {
names.push_back(pair.first);
}
return names;
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,441 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#include <bitset>
#include "graph/node_enum.h"
#include "util/array.h" // IWYU pragma: keep
#include "util/map.h"
#include "util/param.h"
#include "util/thread.h"
#include "util/types.h"
#include "util/unique_ptr.h"
#include "util/vector.h"
CCL_NAMESPACE_BEGIN
struct Node;
struct NodeType;
using SocketModifiedFlags = std::bitset<128>;
/* Socket Type */
struct SocketType {
enum Type {
UNDEFINED = 0,
BOOLEAN,
FLOAT,
INT,
UINT,
UINT64,
COLOR,
VECTOR,
POINT,
NORMAL,
POINT2,
CLOSURE,
STRING,
ENUM,
TRANSFORM,
NODE,
BOOLEAN_ARRAY,
FLOAT_ARRAY,
INT_ARRAY,
COLOR_ARRAY,
VECTOR_ARRAY,
POINT_ARRAY,
NORMAL_ARRAY,
POINT2_ARRAY,
STRING_ARRAY,
TRANSFORM_ARRAY,
NODE_ARRAY,
NUM_TYPES,
};
enum Flags {
LINKABLE = (1 << 0),
ANIMATABLE = (1 << 1),
SVM_INTERNAL = (1 << 2),
OSL_INTERNAL = (1 << 3),
INTERNAL = (1 << 2) | (1 << 3),
LINK_TEXTURE_GENERATED = (1 << 4),
LINK_TEXTURE_NORMAL = (1 << 5),
LINK_TEXTURE_UV = (1 << 6),
LINK_TEXTURE_INCOMING = (1 << 7),
LINK_INCOMING = (1 << 8),
LINK_NORMAL = (1 << 9),
LINK_POSITION = (1 << 10),
LINK_TANGENT = (1 << 11),
LINK_OSL_INITIALIZER = (1 << 12),
DEFAULT_LINK_MASK = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7) | (1 << 8) | (1 << 9) |
(1 << 10) | (1 << 11) | (1 << 12)
};
ustring name;
Type type = UNDEFINED;
int struct_offset = -1;
const void *default_value = nullptr;
const NodeEnum *enum_values = nullptr;
const NodeType *node_type = nullptr;
int flags = 0;
ustring ui_name;
uint8_t modified_flag_bit = 0;
size_t storage_size() const;
size_t packed_size() const;
bool is_array() const;
static size_t size(Type type, bool packed);
static size_t max_size();
static ustring type_name(Type type);
static void *zero_default_value();
static bool is_float3(Type type);
};
/* Node Type */
struct NodeType {
enum Type { NONE, SHADER };
explicit NodeType(Type type = NONE, const NodeType *base = nullptr);
~NodeType();
void register_input(ustring name,
ustring ui_name,
SocketType::Type type,
const int struct_offset,
const void *default_value,
const NodeEnum *enum_values = nullptr,
const NodeType *node_type = nullptr,
int flags = 0,
int extra_flags = 0);
void register_output(ustring name, ustring ui_name, SocketType::Type type);
const SocketType *find_input(ustring name) const;
const SocketType *find_output(ustring name) const;
using CreateFunc = unique_ptr<Node> (*)(const NodeType *);
ustring name;
Type type;
const NodeType *base;
vector<SocketType, std::allocator<SocketType>> inputs;
vector<SocketType, std::allocator<SocketType>> outputs;
CreateFunc create;
static NodeType *add(const char *name,
CreateFunc create,
Type type = NONE,
const NodeType *base = nullptr);
static const NodeType *find(ustring name);
static vector<ustring> type_names();
static bool register_on_init(const NodeType *(*init_func)());
};
/* Node Definition Macros
*
* Node we use accessor to get node types to ensure correct static
* initialization order. */
#define NODE_DECLARE \
static const NodeType *get_node_type(); \
template<typename T> static const NodeType *register_type(); \
static unique_ptr<Node> create(const NodeType *type); \
static const NodeType *node_type_; \
static thread_mutex node_type_mutex_;
#define NODE_DEFINE(structname) \
const NodeType *structname::node_type_ = nullptr; \
thread_mutex structname::node_type_mutex_; \
static bool structname##_register_on_init = NodeType::register_on_init( \
structname::get_node_type); \
unique_ptr<Node> structname::create(const NodeType *) \
{ \
return make_unique<structname>(); \
} \
const NodeType *structname::get_node_type() \
{ \
if (node_type_ == nullptr) { \
thread_scoped_lock lock(node_type_mutex_); \
if (node_type_ == nullptr) { \
node_type_ = structname::register_type<structname>(); \
} \
} \
return node_type_; \
} \
template<typename T> const NodeType *structname::register_type()
#define NODE_ABSTRACT_DECLARE \
template<typename T> static const NodeType *register_base_type(); \
static const NodeType *get_node_base_type();
#define NODE_ABSTRACT_DEFINE(structname) \
const NodeType *structname::get_node_base_type() \
{ \
/* Base types constructed in this getter to ensure correct initialization \
* order. Regular types are not so they are auto-registered for XML parsing. */ \
static const NodeType *node_base_type = register_base_type<structname>(); \
return node_base_type; \
} \
template<typename T> const NodeType *structname::register_base_type()
/* Sock Definition Macros */
#define SOCKET_OFFSETOF(T, name) offsetof(T, name)
#define SOCKET_SIZEOF(T, name) (sizeof(T::name))
#define SOCKET_DEFINE(name, ui_name, default_value, datatype, TYPE, flags, ...) \
{ \
static datatype defval = default_value; \
static_assert(std::is_same_v<decltype(T::name), datatype>); \
type->register_input(ustring(#name), \
ustring(ui_name), \
TYPE, \
SOCKET_OFFSETOF(T, name), \
&defval, \
nullptr, \
nullptr, \
flags, \
##__VA_ARGS__); \
}
#define SOCKET_BOOLEAN(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, bool, SocketType::BOOLEAN, 0, ##__VA_ARGS__)
#define SOCKET_INT(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, int, SocketType::INT, 0, ##__VA_ARGS__)
#define SOCKET_UINT(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, uint, SocketType::UINT, 0, ##__VA_ARGS__)
#define SOCKET_UINT64(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, uint64_t, SocketType::UINT64, 0, ##__VA_ARGS__)
#define SOCKET_FLOAT(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, float, SocketType::FLOAT, 0, ##__VA_ARGS__)
#define SOCKET_COLOR(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, float3, SocketType::COLOR, 0, ##__VA_ARGS__)
#define SOCKET_VECTOR(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, float3, SocketType::VECTOR, 0, ##__VA_ARGS__)
#define SOCKET_POINT(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, float3, SocketType::POINT, 0, ##__VA_ARGS__)
#define SOCKET_NORMAL(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, float3, SocketType::NORMAL, 0, ##__VA_ARGS__)
#define SOCKET_POINT2(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, float2, SocketType::POINT2, 0, ##__VA_ARGS__)
#define SOCKET_STRING(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, ustring, SocketType::STRING, 0, ##__VA_ARGS__)
#define SOCKET_TRANSFORM(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, Transform, SocketType::TRANSFORM, 0, ##__VA_ARGS__)
#define SOCKET_ENUM(name, ui_name, values, default_value, ...) \
{ \
static int defval = default_value; \
assert(SOCKET_SIZEOF(T, name) == sizeof(int)); \
type->register_input(ustring(#name), \
ustring(ui_name), \
SocketType::ENUM, \
SOCKET_OFFSETOF(T, name), \
&defval, \
&values, \
nullptr, \
##__VA_ARGS__); \
}
#define SOCKET_NODE(name, ui_name, node_type, ...) \
{ \
static Node *defval = nullptr; \
assert(SOCKET_SIZEOF(T, name) == sizeof(Node *)); \
type->register_input(ustring(#name), \
ustring(ui_name), \
SocketType::NODE, \
SOCKET_OFFSETOF(T, name), \
(const void *)&defval, \
nullptr, \
node_type, \
##__VA_ARGS__); \
}
#define SOCKET_BOOLEAN_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE( \
name, ui_name, default_value, array<bool>, SocketType::BOOLEAN_ARRAY, 0, ##__VA_ARGS__)
#define SOCKET_INT_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, ui_name, default_value, array<int>, SocketType::INT_ARRAY, 0, ##__VA_ARGS__)
#define SOCKET_FLOAT_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE( \
name, ui_name, default_value, array<float>, SocketType::FLOAT_ARRAY, 0, ##__VA_ARGS__)
#define SOCKET_COLOR_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
array<packed_float3>, \
SocketType::COLOR_ARRAY, \
0, \
##__VA_ARGS__)
#define SOCKET_VECTOR_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
array<packed_float3>, \
SocketType::VECTOR_ARRAY, \
0, \
##__VA_ARGS__)
#define SOCKET_POINT_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
array<packed_float3>, \
SocketType::POINT_ARRAY, \
0, \
##__VA_ARGS__)
#define SOCKET_NORMAL_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
array<packed_float3>, \
SocketType::NORMAL_ARRAY, \
0, \
##__VA_ARGS__)
#define SOCKET_POINT2_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE( \
name, ui_name, default_value, array<float2>, SocketType::POINT2_ARRAY, 0, ##__VA_ARGS__)
#define SOCKET_STRING_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE( \
name, ui_name, default_value, array<ustring>, SocketType::STRING_ARRAY, 0, ##__VA_ARGS__)
#define SOCKET_TRANSFORM_ARRAY(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
array<Transform>, \
SocketType::TRANSFORM_ARRAY, \
0, \
##__VA_ARGS__)
#define SOCKET_NODE_ARRAY(name, ui_name, node_type, ...) \
{ \
static array<Node *> defval = {}; \
assert(SOCKET_SIZEOF(T, name) == sizeof(array<Node *>)); \
type->register_input(ustring(#name), \
ustring(ui_name), \
SocketType::NODE_ARRAY, \
SOCKET_OFFSETOF(T, name), \
&defval, \
nullptr, \
node_type, \
##__VA_ARGS__); \
}
#define SOCKET_IN_BOOLEAN(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
bool, \
SocketType::BOOLEAN, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_INT(name, ui_name, default_value, ...) \
SOCKET_DEFINE( \
name, ui_name, default_value, int, SocketType::INT, SocketType::LINKABLE, ##__VA_ARGS__)
#define SOCKET_IN_FLOAT(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
float, \
SocketType::FLOAT, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_COLOR(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
float3, \
SocketType::COLOR, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_VECTOR(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
float3, \
SocketType::VECTOR, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_POINT(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
float3, \
SocketType::POINT, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_NORMAL(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
float3, \
SocketType::NORMAL, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_STRING(name, ui_name, default_value, ...) \
SOCKET_DEFINE(name, \
ui_name, \
default_value, \
ustring, \
SocketType::STRING, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_IN_CLOSURE(name, ui_name, ...) \
type->register_input(ustring(#name), \
ustring(ui_name), \
SocketType::CLOSURE, \
0, \
nullptr, \
nullptr, \
nullptr, \
SocketType::LINKABLE, \
##__VA_ARGS__)
#define SOCKET_OUT_BOOLEAN(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::BOOLEAN); \
}
#define SOCKET_OUT_INT(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::INT); \
}
#define SOCKET_OUT_FLOAT(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::FLOAT); \
}
#define SOCKET_OUT_COLOR(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::COLOR); \
}
#define SOCKET_OUT_VECTOR(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::VECTOR); \
}
#define SOCKET_OUT_POINT(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::POINT); \
}
#define SOCKET_OUT_NORMAL(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::NORMAL); \
}
#define SOCKET_OUT_CLOSURE(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::CLOSURE); \
}
#define SOCKET_OUT_STRING(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::STRING); \
}
#define SOCKET_OUT_ENUM(name, ui_name) \
{ \
type->register_output(ustring(#name), ustring(ui_name), SocketType::ENUM); \
}
CCL_NAMESPACE_END

View File

@@ -0,0 +1,438 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#ifdef WITH_PUGIXML
# include "graph/node_xml.h"
# include "graph/node.h"
# include "util/log.h"
# include "util/string.h"
# include "util/transform.h"
CCL_NAMESPACE_BEGIN
static bool xml_read_boolean(const char *value)
{
return string_iequals(value, "true") || (atoi(value) != 0);
}
static const char *xml_write_boolean(bool value)
{
return (value) ? "true" : "false";
}
template<int VECTOR_SIZE, typename T>
static void xml_read_float_array(T &value, xml_attribute attr)
{
vector<string> tokens;
string_split(tokens, attr.value());
if (tokens.size() % VECTOR_SIZE != 0) {
return;
}
value.resize(tokens.size() / VECTOR_SIZE);
for (size_t i = 0; i < value.size(); i++) {
float *value_float = (float *)&value[i];
for (size_t j = 0; j < VECTOR_SIZE; j++) {
value_float[j] = (float)atof(tokens[i * VECTOR_SIZE + j].c_str());
}
}
}
void xml_read_node(XMLReader &reader, Node *node, const xml_node xml_node)
{
const xml_attribute name_attr = xml_node.attribute("name");
if (name_attr) {
node->name = ustring(name_attr.value());
}
for (const SocketType &socket : node->type->inputs) {
if (socket.type == SocketType::CLOSURE || socket.type == SocketType::UNDEFINED) {
continue;
}
if (socket.flags & SocketType::INTERNAL) {
continue;
}
const xml_attribute attr = xml_node.attribute(socket.name.c_str());
if (!attr) {
continue;
}
switch (socket.type) {
case SocketType::BOOLEAN: {
node->set(socket, xml_read_boolean(attr.value()));
break;
}
case SocketType::BOOLEAN_ARRAY: {
vector<string> tokens;
string_split(tokens, attr.value());
array<bool> value;
value.resize(tokens.size());
for (size_t i = 0; i < value.size(); i++) {
value[i] = xml_read_boolean(tokens[i].c_str());
}
node->set(socket, value);
break;
}
case SocketType::FLOAT: {
node->set(socket, (float)atof(attr.value()));
break;
}
case SocketType::FLOAT_ARRAY: {
array<float> value;
xml_read_float_array<1>(value, attr);
node->set(socket, value);
break;
}
case SocketType::INT: {
node->set(socket, atoi(attr.value()));
break;
}
case SocketType::UINT: {
node->set(socket, (uint)atoi(attr.value()));
break;
}
case SocketType::UINT64: {
node->set(socket, (uint64_t)strtoull(attr.value(), nullptr, 10));
break;
}
case SocketType::INT_ARRAY: {
vector<string> tokens;
string_split(tokens, attr.value());
array<int> value;
value.resize(tokens.size());
for (size_t i = 0; i < value.size(); i++) {
value[i] = atoi(attr.value());
}
node->set(socket, value);
break;
}
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL: {
array<float3> value;
xml_read_float_array<3>(value, attr);
if (value.size() == 1) {
node->set(socket, value[0]);
}
break;
}
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY: {
array<packed_float3> value;
xml_read_float_array<3>(value, attr);
node->set(socket, value);
break;
}
case SocketType::POINT2: {
array<float2> value;
xml_read_float_array<2>(value, attr);
if (value.size() == 1) {
node->set(socket, value[0]);
}
break;
}
case SocketType::POINT2_ARRAY: {
array<float2> value;
xml_read_float_array<2>(value, attr);
node->set(socket, value);
break;
}
case SocketType::STRING: {
node->set(socket, attr.value());
break;
}
case SocketType::ENUM: {
const ustring value(attr.value());
if (socket.enum_values->exists(value)) {
node->set(socket, value);
}
else {
LOG_ERROR << "Unknown value \"" << value.c_str() << "\" for attribute \""
<< socket.name.c_str() << "\"";
}
break;
}
case SocketType::STRING_ARRAY: {
vector<string> tokens;
string_split(tokens, attr.value());
array<ustring> value;
value.resize(tokens.size());
for (size_t i = 0; i < value.size(); i++) {
value[i] = ustring(tokens[i]);
}
node->set(socket, value);
break;
}
case SocketType::TRANSFORM: {
array<Transform> value;
xml_read_float_array<12>(value, attr);
if (value.size() == 1) {
node->set(socket, value[0]);
}
break;
}
case SocketType::TRANSFORM_ARRAY: {
array<Transform> value;
xml_read_float_array<12>(value, attr);
node->set(socket, value);
break;
}
case SocketType::NODE: {
const ustring value(attr.value());
const map<ustring, Node *>::iterator it = reader.node_map.find(value);
if (it != reader.node_map.end()) {
Node *value_node = it->second;
if (value_node->is_a(socket.node_type)) {
node->set(socket, it->second);
}
}
break;
}
case SocketType::NODE_ARRAY: {
vector<string> tokens;
string_split(tokens, attr.value());
array<Node *> value;
value.resize(tokens.size());
for (size_t i = 0; i < value.size(); i++) {
const map<ustring, Node *>::iterator it = reader.node_map.find(ustring(tokens[i]));
if (it != reader.node_map.end()) {
Node *value_node = it->second;
value[i] = (value_node->is_a(socket.node_type)) ? value_node : nullptr;
}
else {
value[i] = nullptr;
}
}
node->set(socket, value);
break;
}
case SocketType::CLOSURE:
case SocketType::UNDEFINED:
case SocketType::NUM_TYPES:
break;
}
}
if (!node->name.empty()) {
reader.node_map[node->name] = node;
}
}
xml_node xml_write_node(Node *node, xml_node xml_root)
{
xml_node xml_node = xml_root.append_child(node->type->name.c_str());
xml_node.append_attribute("name") = node->name.c_str();
for (const SocketType &socket : node->type->inputs) {
if (socket.type == SocketType::CLOSURE || socket.type == SocketType::UNDEFINED) {
continue;
}
if (socket.flags & SocketType::INTERNAL) {
continue;
}
if (node->has_default_value(socket)) {
continue;
}
xml_attribute attr = xml_node.append_attribute(socket.name.c_str());
switch (socket.type) {
case SocketType::BOOLEAN: {
attr = xml_write_boolean(node->get_bool(socket));
break;
}
case SocketType::BOOLEAN_ARRAY: {
std::stringstream ss;
const array<bool> &value = node->get_bool_array(socket);
for (size_t i = 0; i < value.size(); i++) {
ss << xml_write_boolean(value[i]);
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::FLOAT: {
attr = (double)node->get_float(socket);
break;
}
case SocketType::FLOAT_ARRAY: {
std::stringstream ss;
const array<float> &value = node->get_float_array(socket);
for (size_t i = 0; i < value.size(); i++) {
ss << value[i];
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::INT: {
attr = node->get_int(socket);
break;
}
case SocketType::UINT: {
attr = node->get_uint(socket);
break;
}
case SocketType::UINT64: {
attr = node->get_uint64(socket);
break;
}
case SocketType::INT_ARRAY: {
std::stringstream ss;
const array<int> &value = node->get_int_array(socket);
for (size_t i = 0; i < value.size(); i++) {
ss << value[i];
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::COLOR:
case SocketType::VECTOR:
case SocketType::POINT:
case SocketType::NORMAL: {
const float3 value = node->get_float3(socket);
attr =
string_printf("%g %g %g", (double)value.x, (double)value.y, (double)value.z).c_str();
break;
}
case SocketType::COLOR_ARRAY:
case SocketType::VECTOR_ARRAY:
case SocketType::POINT_ARRAY:
case SocketType::NORMAL_ARRAY: {
std::stringstream ss;
const array<packed_float3> &value = node->get_float3_array(socket);
for (size_t i = 0; i < value.size(); i++) {
ss << string_printf(
"%g %g %g", (double)value[i].x, (double)value[i].y, (double)value[i].z);
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::POINT2: {
const float2 value = node->get_float2(socket);
attr = string_printf("%g %g", (double)value.x, (double)value.y).c_str();
break;
}
case SocketType::POINT2_ARRAY: {
std::stringstream ss;
const array<float2> &value = node->get_float2_array(socket);
for (size_t i = 0; i < value.size(); i++) {
ss << string_printf("%g %g", (double)value[i].x, (double)value[i].y);
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::STRING:
case SocketType::ENUM: {
attr = node->get_string(socket).c_str();
break;
}
case SocketType::STRING_ARRAY: {
std::stringstream ss;
const array<ustring> &value = node->get_string_array(socket);
for (size_t i = 0; i < value.size(); i++) {
ss << value[i];
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::TRANSFORM: {
Transform tfm = node->get_transform(socket);
std::stringstream ss;
for (int i = 0; i < 3; i++) {
ss << string_printf("%g %g %g %g ",
(double)tfm[i][0],
(double)tfm[i][1],
(double)tfm[i][2],
(double)tfm[i][3]);
}
ss << string_printf("%g %g %g %g", 0.0, 0.0, 0.0, 1.0);
attr = ss.str().c_str();
break;
}
case SocketType::TRANSFORM_ARRAY: {
std::stringstream ss;
const array<Transform> &value = node->get_transform_array(socket);
for (size_t j = 0; j < value.size(); j++) {
const Transform &tfm = value[j];
for (int i = 0; i < 3; i++) {
ss << string_printf("%g %g %g %g ",
(double)tfm[i][0],
(double)tfm[i][1],
(double)tfm[i][2],
(double)tfm[i][3]);
}
ss << string_printf("%g %g %g %g", 0.0, 0.0, 0.0, 1.0);
if (j != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::NODE: {
Node *value = node->get_node(socket);
if (value) {
attr = value->name.c_str();
}
break;
}
case SocketType::NODE_ARRAY: {
std::stringstream ss;
const array<Node *> &value = node->get_node_array(socket);
for (size_t i = 0; i < value.size(); i++) {
if (value[i]) {
ss << value[i]->name.c_str();
}
if (i != value.size() - 1) {
ss << " ";
}
}
attr = ss.str().c_str();
break;
}
case SocketType::CLOSURE:
case SocketType::UNDEFINED:
case SocketType::NUM_TYPES:
break;
}
}
return xml_node;
}
CCL_NAMESPACE_END
#endif /* WITH_PUGIXML */

View File

@@ -0,0 +1,26 @@
/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
*
* SPDX-License-Identifier: Apache-2.0 */
#pragma once
#ifdef WITH_PUGIXML
# include "util/map.h"
# include "util/param.h"
# include "util/xml.h"
CCL_NAMESPACE_BEGIN
struct Node;
struct XMLReader {
map<ustring, Node *> node_map;
};
void xml_read_node(XMLReader &reader, Node *node, const xml_node xml_node);
xml_node xml_write_node(Node *node, const xml_node xml_root);
CCL_NAMESPACE_END
#endif /* WITH_PUGIXML */