Add Chromium-only Blender WebEngine parity work
This commit is contained in:
33
blender-5.2.0/intern/cycles/graph/CMakeLists.txt
Normal file
33
blender-5.2.0/intern/cycles/graph/CMakeLists.txt
Normal 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})
|
||||
910
blender-5.2.0/intern/cycles/graph/node.cpp
Normal file
910
blender-5.2.0/intern/cycles/graph/node.cpp
Normal 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
|
||||
235
blender-5.2.0/intern/cycles/graph/node.h
Normal file
235
blender-5.2.0/intern/cycles/graph/node.h
Normal 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
|
||||
65
blender-5.2.0/intern/cycles/graph/node_enum.h
Normal file
65
blender-5.2.0/intern/cycles/graph/node_enum.h
Normal 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
|
||||
304
blender-5.2.0/intern/cycles/graph/node_type.cpp
Normal file
304
blender-5.2.0/intern/cycles/graph/node_type.cpp
Normal 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
|
||||
441
blender-5.2.0/intern/cycles/graph/node_type.h
Normal file
441
blender-5.2.0/intern/cycles/graph/node_type.h
Normal 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
|
||||
438
blender-5.2.0/intern/cycles/graph/node_xml.cpp
Normal file
438
blender-5.2.0/intern/cycles/graph/node_xml.cpp
Normal 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 */
|
||||
26
blender-5.2.0/intern/cycles/graph/node_xml.h
Normal file
26
blender-5.2.0/intern/cycles/graph/node_xml.h
Normal 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 */
|
||||
Reference in New Issue
Block a user