/* * Copyright 2011-2016 Blender Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "graph/node.h" #include "graph/node_type.h" #include "util/util_foreach.h" #include "util/util_md5.h" #include "util/util_param.h" #include "util/util_transform.h" CCL_NAMESPACE_BEGIN /* Node Type */ NodeOwner::~NodeOwner() { } 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 */ foreach (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, int value) { assert((input.type == SocketType::INT || input.type == SocketType::ENUM)); set_if_different(input, value); } void Node::set(const SocketType &input, uint value) { assert(input.type == SocketType::UINT); set_if_different(input, value); } void Node::set(const SocketType &input, float value) { assert(input.type == SocketType::FLOAT); set_if_different(input, value); } void Node::set(const SocketType &input, float2 value) { assert(input.type == SocketType::POINT2); set_if_different(input, value); } void Node::set(const SocketType &input, 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 &value) { assert(input.type == SocketType::BOOLEAN_ARRAY); set_if_different(input, value); } void Node::set(const SocketType &input, array &value) { assert(input.type == SocketType::INT_ARRAY); set_if_different(input, value); } void Node::set(const SocketType &input, array &value) { assert(input.type == SocketType::FLOAT_ARRAY); set_if_different(input, value); } void Node::set(const SocketType &input, array &value) { assert(input.type == SocketType::POINT2_ARRAY); set_if_different(input, value); } void Node::set(const SocketType &input, array &value) { assert(is_socket_array_float3(input)); set_if_different(input, value); } void Node::set(const SocketType &input, array &value) { assert(input.type == SocketType::STRING_ARRAY); set_if_different(input, value); } void Node::set(const SocketType &input, array &value) { assert(input.type == SocketType::TRANSFORM_ARRAY); set_if_different(input, value); } void Node::set(const SocketType &input, array &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(this, input); } int Node::get_int(const SocketType &input) const { assert(input.type == SocketType::INT || input.type == SocketType::ENUM); return get_socket_value(this, input); } uint Node::get_uint(const SocketType &input) const { assert(input.type == SocketType::UINT); return get_socket_value(this, input); } float Node::get_float(const SocketType &input) const { assert(input.type == SocketType::FLOAT); return get_socket_value(this, input); } float2 Node::get_float2(const SocketType &input) const { assert(input.type == SocketType::POINT2); return get_socket_value(this, input); } float3 Node::get_float3(const SocketType &input) const { assert(is_socket_float3(input)); return get_socket_value(this, input); } ustring Node::get_string(const SocketType &input) const { if (input.type == SocketType::STRING) { return get_socket_value(this, input); } else if (input.type == SocketType::ENUM) { const NodeEnum &enm = *input.enum_values; int intvalue = get_socket_value(this, input); return (enm.exists(intvalue)) ? enm[intvalue] : ustring(); } else { assert(0); return ustring(); } } Transform Node::get_transform(const SocketType &input) const { assert(input.type == SocketType::TRANSFORM); return get_socket_value(this, input); } Node *Node::get_node(const SocketType &input) const { assert(input.type == SocketType::NODE); return get_socket_value(this, input); } /* get array values */ const array &Node::get_bool_array(const SocketType &input) const { assert(input.type == SocketType::BOOLEAN_ARRAY); return get_socket_value>(this, input); } const array &Node::get_int_array(const SocketType &input) const { assert(input.type == SocketType::INT_ARRAY); return get_socket_value>(this, input); } const array &Node::get_float_array(const SocketType &input) const { assert(input.type == SocketType::FLOAT_ARRAY); return get_socket_value>(this, input); } const array &Node::get_float2_array(const SocketType &input) const { assert(input.type == SocketType::POINT2_ARRAY); return get_socket_value>(this, input); } const array &Node::get_float3_array(const SocketType &input) const { assert(is_socket_array_float3(input)); return get_socket_value>(this, input); } const array &Node::get_string_array(const SocketType &input) const { assert(input.type == SocketType::STRING_ARRAY); return get_socket_value>(this, input); } const array &Node::get_transform_array(const SocketType &input) const { assert(input.type == SocketType::TRANSFORM_ARRAY); return get_socket_value>(this, input); } const array &Node::get_node_array(const SocketType &input) const { assert(input.type == SocketType::NODE_ARRAY); return get_socket_value>(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(this, input); return memcmp(dst, src, input.size()) == 0; } void Node::set_default_value(const SocketType &socket) { const void *src = socket.default_value; void *dst = ((char *)this) + socket.struct_offset; if (socket.size() > 0) { memcpy(dst, src, socket.size()); } } template static void copy_array(const Node *node, const SocketType &socket, const Node *other, const SocketType &other_socket) { const array *src = (const array *)(((char *)other) + other_socket.struct_offset); array *dst = (array *)(((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(this, socket, &other, other_socket); break; case SocketType::FLOAT_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::INT_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::COLOR_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::VECTOR_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::POINT_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::NORMAL_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::POINT2_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::STRING_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::TRANSFORM_ARRAY: copy_array(this, socket, &other, other_socket); break; case SocketType::NODE_ARRAY: { copy_array(this, socket, &other, other_socket); array &node_array = get_socket_value>(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.size()); if (socket.type == SocketType::NODE) { Node *node = get_socket_value(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>(&other, socket)); break; case SocketType::FLOAT_ARRAY: set(socket, get_socket_value>(&other, socket)); break; case SocketType::INT_ARRAY: set(socket, get_socket_value>(&other, socket)); break; case SocketType::COLOR_ARRAY: case SocketType::VECTOR_ARRAY: case SocketType::POINT_ARRAY: case SocketType::NORMAL_ARRAY: set(socket, get_socket_value>(&other, socket)); break; case SocketType::POINT2_ARRAY: set(socket, get_socket_value>(&other, socket)); break; case SocketType::STRING_ARRAY: set(socket, get_socket_value>(&other, socket)); break; case SocketType::TRANSFORM_ARRAY: set(socket, get_socket_value>(&other, socket)); break; case SocketType::NODE_ARRAY: set(socket, get_socket_value>(&other, socket)); break; default: assert(0); break; } } else { switch (socket.type) { case SocketType::BOOLEAN: set(socket, get_socket_value(&other, socket)); break; case SocketType::FLOAT: set(socket, get_socket_value(&other, socket)); break; case SocketType::INT: set(socket, get_socket_value(&other, socket)); break; case SocketType::UINT: set(socket, get_socket_value(&other, socket)); break; case SocketType::COLOR: case SocketType::VECTOR: case SocketType::POINT: case SocketType::NORMAL: set(socket, get_socket_value(&other, socket)); break; case SocketType::POINT2: set(socket, get_socket_value(&other, socket)); break; case SocketType::STRING: set(socket, get_socket_value(&other, socket)); break; case SocketType::ENUM: set(socket, get_socket_value(&other, socket)); break; case SocketType::TRANSFORM: set(socket, get_socket_value(&other, socket)); break; case SocketType::NODE: set(socket, get_socket_value(&other, socket)); break; default: assert(0); break; } } } template static bool is_array_equal(const Node *node, const Node *other, const SocketType &socket) { const array *a = (const array *)(((char *)node) + socket.struct_offset); const array *b = (const array *)(((char *)other) + socket.struct_offset); return *a == *b; } template 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(this, &other, socket); case SocketType::FLOAT: return is_value_equal(this, &other, socket); case SocketType::INT: return is_value_equal(this, &other, socket); case SocketType::UINT: return is_value_equal(this, &other, socket); case SocketType::COLOR: return is_value_equal(this, &other, socket); case SocketType::VECTOR: return is_value_equal(this, &other, socket); case SocketType::POINT: return is_value_equal(this, &other, socket); case SocketType::NORMAL: return is_value_equal(this, &other, socket); case SocketType::POINT2: return is_value_equal(this, &other, socket); case SocketType::CLOSURE: return true; case SocketType::STRING: return is_value_equal(this, &other, socket); case SocketType::ENUM: return is_value_equal(this, &other, socket); case SocketType::TRANSFORM: return is_value_equal(this, &other, socket); case SocketType::NODE: return is_value_equal(this, &other, socket); case SocketType::BOOLEAN_ARRAY: return is_array_equal(this, &other, socket); case SocketType::FLOAT_ARRAY: return is_array_equal(this, &other, socket); case SocketType::INT_ARRAY: return is_array_equal(this, &other, socket); case SocketType::COLOR_ARRAY: return is_array_equal(this, &other, socket); case SocketType::VECTOR_ARRAY: return is_array_equal(this, &other, socket); case SocketType::POINT_ARRAY: return is_array_equal(this, &other, socket); case SocketType::NORMAL_ARRAY: return is_array_equal(this, &other, socket); case SocketType::POINT2_ARRAY: return is_array_equal(this, &other, socket); case SocketType::STRING_ARRAY: return is_array_equal(this, &other, socket); case SocketType::TRANSFORM_ARRAY: return is_array_equal(this, &other, socket); case SocketType::NODE_ARRAY: return is_array_equal(this, &other, socket); case SocketType::UNDEFINED: return true; } return true; } /* equals */ bool Node::equals(const Node &other) const { assert(type == other.type); foreach (const SocketType &socket, type->inputs) { if (!equals_value(other, socket)) return false; } return true; } /* Hash */ namespace { template void value_hash(const Node *node, const SocketType &socket, MD5Hash &md5) { md5.append(((uint8_t *)node) + socket.struct_offset, socket.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 void array_hash(const Node *node, const SocketType &socket, MD5Hash &md5) { const array &a = *(const array *)(((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) { /* Don't compare 4th element used for padding. */ const array &a = *(const array *)(((char *)node) + socket.struct_offset); for (size_t i = 0; i < a.size(); i++) { md5.append((uint8_t *)&a[i], sizeof(float) * 3); } } } // namespace void Node::hash(MD5Hash &md5) { md5.append(type->name.string()); foreach (const SocketType &socket, type->inputs) { md5.append(socket.name.string()); switch (socket.type) { case SocketType::BOOLEAN: value_hash(this, socket, md5); break; case SocketType::FLOAT: value_hash(this, socket, md5); break; case SocketType::INT: value_hash(this, socket, md5); break; case SocketType::UINT: value_hash(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(this, socket, md5); break; case SocketType::CLOSURE: break; case SocketType::STRING: value_hash(this, socket, md5); break; case SocketType::ENUM: value_hash(this, socket, md5); break; case SocketType::TRANSFORM: value_hash(this, socket, md5); break; case SocketType::NODE: value_hash(this, socket, md5); break; case SocketType::BOOLEAN_ARRAY: array_hash(this, socket, md5); break; case SocketType::FLOAT_ARRAY: array_hash(this, socket, md5); break; case SocketType::INT_ARRAY: array_hash(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(this, socket, md5); break; case SocketType::STRING_ARRAY: array_hash(this, socket, md5); break; case SocketType::TRANSFORM_ARRAY: array_hash(this, socket, md5); break; case SocketType::NODE_ARRAY: array_hash(this, socket, md5); break; case SocketType::UNDEFINED: break; } } } namespace { template size_t array_size_in_bytes(const Node *node, const SocketType &socket) { const array &a = *(const array *)(((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; foreach (const SocketType &socket, type->inputs) { switch (socket.type) { case SocketType::BOOLEAN: case SocketType::FLOAT: case SocketType::INT: case SocketType::UINT: 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.size(); break; case SocketType::BOOLEAN_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::FLOAT_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::INT_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::COLOR_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::VECTOR_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::POINT_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::NORMAL_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::POINT2_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::STRING_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::TRANSFORM_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::NODE_ARRAY: total_size += array_size_in_bytes(this, socket); break; case SocketType::UNDEFINED: 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() { foreach (const SocketType &socket, type->inputs) { if (socket.type == SocketType::NODE) { Node *node = get_socket_value(this, socket); if (node) { node->dereference(); } } else if (socket.type == SocketType::NODE_ARRAY) { const array &nodes = get_socket_value>(this, socket); for (Node *node : nodes) { node->dereference(); } } } } bool Node::socket_is_modified(const SocketType &input) const { return (socket_modified & input.modified_flag_bit) != 0; } bool Node::is_modified() { return socket_modified != 0; } void Node::tag_modified() { socket_modified = ~0ull; } void Node::clear_modified() { socket_modified = 0; } template void Node::set_if_different(const SocketType &input, T value) { if (get_socket_value(this, input) == value) { return; } get_socket_value(this, input) = value; socket_modified |= input.modified_flag_bit; } void Node::set_if_different(const SocketType &input, Node *value) { if (get_socket_value(this, input) == value) { return; } Node *old_node = get_socket_value(this, input); if (old_node) { old_node->dereference(); } if (value) { value->reference(); } get_socket_value(this, input) = value; socket_modified |= input.modified_flag_bit; } template void Node::set_if_different(const SocketType &input, array &value) { if (!socket_is_modified(input)) { if (get_socket_value>(this, input) == value) { return; } } get_socket_value>(this, input).steal_data(value); socket_modified |= input.modified_flag_bit; } void Node::set_if_different(const SocketType &input, array &value) { if (!socket_is_modified(input)) { if (get_socket_value>(this, input) == value) { return; } } array &old_nodes = get_socket_value>(this, input); for (Node *old_node : old_nodes) { old_node->dereference(); } for (Node *new_node : value) { new_node->reference(); } get_socket_value>(this, input).steal_data(value); socket_modified |= input.modified_flag_bit; } void Node::print_modified_sockets() const { printf("Node : %s\n", name.c_str()); for (auto &socket : type->inputs) { if (socket_is_modified(socket)) { printf("-- socket modified : %s\n", socket.name.c_str()); } } } CCL_NAMESPACE_END