|
|
|
@@ -29,6 +29,7 @@
|
|
|
|
|
#include <cstddef>
|
|
|
|
|
#include <cstdlib>
|
|
|
|
|
#include <cstring>
|
|
|
|
|
#include <queue>
|
|
|
|
|
|
|
|
|
|
/* Allow using deprecated functionality for .blend file I/O. */
|
|
|
|
|
#define DNA_DEPRECATED_ALLOW
|
|
|
|
@@ -52,9 +53,12 @@
|
|
|
|
|
#include "BLI_map.hh"
|
|
|
|
|
#include "BLI_math.h"
|
|
|
|
|
#include "BLI_path_util.h"
|
|
|
|
|
#include "BLI_set.hh"
|
|
|
|
|
#include "BLI_stack.hh"
|
|
|
|
|
#include "BLI_string.h"
|
|
|
|
|
#include "BLI_string_utils.h"
|
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
#include "BLI_vector_set.hh"
|
|
|
|
|
|
|
|
|
|
#include "BLT_translation.h"
|
|
|
|
|
|
|
|
|
@@ -80,6 +84,7 @@
|
|
|
|
|
#include "NOD_function.h"
|
|
|
|
|
#include "NOD_geometry.h"
|
|
|
|
|
#include "NOD_node_declaration.hh"
|
|
|
|
|
#include "NOD_node_tree_ref.hh"
|
|
|
|
|
#include "NOD_shader.h"
|
|
|
|
|
#include "NOD_socket.h"
|
|
|
|
|
#include "NOD_texture.h"
|
|
|
|
@@ -93,6 +98,14 @@
|
|
|
|
|
|
|
|
|
|
#define NODE_DEFAULT_MAX_WIDTH 700
|
|
|
|
|
|
|
|
|
|
using blender::Array;
|
|
|
|
|
using blender::Set;
|
|
|
|
|
using blender::Span;
|
|
|
|
|
using blender::Stack;
|
|
|
|
|
using blender::Vector;
|
|
|
|
|
using blender::VectorSet;
|
|
|
|
|
using namespace blender::nodes::node_tree_ref_types;
|
|
|
|
|
|
|
|
|
|
/* Fallback types for undefined tree, nodes, sockets */
|
|
|
|
|
static bNodeTreeType NodeTreeTypeUndefined;
|
|
|
|
|
bNodeType NodeTypeUndefined;
|
|
|
|
@@ -647,6 +660,8 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
|
|
|
|
|
ntree->progress = nullptr;
|
|
|
|
|
ntree->execdata = nullptr;
|
|
|
|
|
|
|
|
|
|
ntree->output_field_dependencies = nullptr;
|
|
|
|
|
|
|
|
|
|
BLO_read_data_address(reader, &ntree->adt);
|
|
|
|
|
BKE_animdata_blend_read_data(reader, ntree->adt);
|
|
|
|
|
|
|
|
|
@@ -1015,8 +1030,8 @@ IDTypeInfo IDType_ID_NT = {
|
|
|
|
|
static void node_add_sockets_from_type(bNodeTree *ntree, bNode *node, bNodeType *ntype)
|
|
|
|
|
{
|
|
|
|
|
if (ntype->declare != nullptr) {
|
|
|
|
|
nodeDeclarationEnsure(ntree, node);
|
|
|
|
|
node->declaration->build(*ntree, *node);
|
|
|
|
|
blender::nodes::NodeDeclaration *node_decl = nodeDeclarationEnsure(ntree, node);
|
|
|
|
|
node_decl->build(*ntree, *node);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
bNodeSocketTemplate *sockdef;
|
|
|
|
@@ -3942,18 +3957,19 @@ int nodeSocketLinkLimit(const bNodeSocket *sock)
|
|
|
|
|
* If the node implements a `declare` function, this function makes sure that `node->declaration`
|
|
|
|
|
* is up to date.
|
|
|
|
|
*/
|
|
|
|
|
void nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node)
|
|
|
|
|
NodeDeclarationHandle *nodeDeclarationEnsure(bNodeTree *UNUSED(ntree), bNode *node)
|
|
|
|
|
{
|
|
|
|
|
if (node->typeinfo->declare == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (node->declaration != nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
return node->declaration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
node->declaration = new blender::nodes::NodeDeclaration();
|
|
|
|
|
blender::nodes::NodeDeclarationBuilder builder{*node->declaration};
|
|
|
|
|
node->typeinfo->declare(builder);
|
|
|
|
|
return node->declaration;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ************** Node Clipboard *********** */
|
|
|
|
@@ -4468,6 +4484,296 @@ void ntreeUpdateAllUsers(Main *main, ID *id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool is_field_socket_type(eNodeSocketDatatype type)
|
|
|
|
|
{
|
|
|
|
|
return ELEM(type, SOCK_FLOAT, SOCK_INT, SOCK_BOOLEAN, SOCK_VECTOR, SOCK_RGBA);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool sockets_have_links(blender::Span<const SocketRef *> sockets)
|
|
|
|
|
{
|
|
|
|
|
for (const SocketRef *socket : sockets) {
|
|
|
|
|
if (!socket->directly_linked_links().is_empty()) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
using OutputFieldDependencies = Vector<std::optional<Vector<int>>>;
|
|
|
|
|
|
|
|
|
|
static const std::optional<Vector<int>> *get_group_output_field_dependencies(
|
|
|
|
|
const OutputSocketRef &output_socket)
|
|
|
|
|
{
|
|
|
|
|
const NodeRef &node = output_socket.node();
|
|
|
|
|
BLI_assert(node.is_group_node());
|
|
|
|
|
bNodeTree *group = (bNodeTree *)node.bnode()->id;
|
|
|
|
|
if (group == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (group->output_field_dependencies == nullptr) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
const OutputFieldDependencies *output_field_dependencies = (const OutputFieldDependencies *)
|
|
|
|
|
group->output_field_dependencies;
|
|
|
|
|
if (output_socket.index() >= output_field_dependencies->size()) {
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return &(*output_field_dependencies)[output_socket.index()];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Vector<int> get_linked_field_input_indices(const OutputSocketRef &output_socket)
|
|
|
|
|
{
|
|
|
|
|
Vector<int> indices;
|
|
|
|
|
const NodeRef &node = output_socket.node();
|
|
|
|
|
if (node.is_group_node()) {
|
|
|
|
|
const std::optional<Vector<int>> *optional_dependencies = get_group_output_field_dependencies(
|
|
|
|
|
output_socket);
|
|
|
|
|
if (optional_dependencies && optional_dependencies->has_value()) {
|
|
|
|
|
indices.extend(**optional_dependencies);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
for (const InputSocketRef *input_socket : output_socket.node().inputs()) {
|
|
|
|
|
if (is_field_socket_type((eNodeSocketDatatype)input_socket->typeinfo()->type)) {
|
|
|
|
|
indices.append(input_socket->index());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return indices;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static Vector<const NodeRef *> toposort_nodes(const NodeTreeRef &tree, bool left_to_right = true)
|
|
|
|
|
{
|
|
|
|
|
Vector<const NodeRef *> toposort;
|
|
|
|
|
toposort.reserve(tree.nodes().size());
|
|
|
|
|
Array<bool> node_is_pushed_by_id(tree.nodes().size(), false);
|
|
|
|
|
std::queue<const NodeRef *> nodes_to_check;
|
|
|
|
|
|
|
|
|
|
for (const NodeRef *node : tree.nodes()) {
|
|
|
|
|
if (!sockets_have_links(node->inputs_or_outputs(!left_to_right))) {
|
|
|
|
|
node_is_pushed_by_id[node->id()] = true;
|
|
|
|
|
nodes_to_check.push(node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (!nodes_to_check.empty()) {
|
|
|
|
|
const NodeRef *node = nodes_to_check.front();
|
|
|
|
|
nodes_to_check.pop();
|
|
|
|
|
toposort.append(node);
|
|
|
|
|
|
|
|
|
|
for (const SocketRef *input_socket : node->inputs_or_outputs(left_to_right)) {
|
|
|
|
|
for (const SocketRef *linked_socket : input_socket->directly_linked_sockets()) {
|
|
|
|
|
const NodeRef &linked_node = linked_socket->node();
|
|
|
|
|
const int linked_node_id = linked_node.id();
|
|
|
|
|
if (!node_is_pushed_by_id[linked_node_id]) {
|
|
|
|
|
node_is_pushed_by_id[linked_node_id] = true;
|
|
|
|
|
nodes_to_check.push(&linked_node);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
toposort.as_mutable_span().reverse();
|
|
|
|
|
return toposort;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct SocketFieldState {
|
|
|
|
|
bool is_single = true;
|
|
|
|
|
bool is_field_source = false;
|
|
|
|
|
bool requires_single = false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
static std::optional<Vector<int>> find_dependent_group_input_indices(
|
|
|
|
|
const InputSocketRef &group_output_socket,
|
|
|
|
|
const Span<SocketFieldState> field_state_by_socket_id)
|
|
|
|
|
{
|
|
|
|
|
Set<const InputSocketRef *> handled_sockets;
|
|
|
|
|
Stack<const InputSocketRef *> sockets_to_check;
|
|
|
|
|
|
|
|
|
|
handled_sockets.add(&group_output_socket);
|
|
|
|
|
sockets_to_check.push(&group_output_socket);
|
|
|
|
|
|
|
|
|
|
Set<int> found_input_indices;
|
|
|
|
|
|
|
|
|
|
while (!sockets_to_check.is_empty()) {
|
|
|
|
|
const InputSocketRef *input_socket = sockets_to_check.pop();
|
|
|
|
|
for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) {
|
|
|
|
|
const NodeRef &origin_node = origin_socket->node();
|
|
|
|
|
const SocketFieldState &origin_state = field_state_by_socket_id[origin_socket->id()];
|
|
|
|
|
if (origin_state.is_field_source) {
|
|
|
|
|
if (origin_node.is_group_input_node()) {
|
|
|
|
|
found_input_indices.add(origin_socket->index());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!origin_state.is_single) {
|
|
|
|
|
const Vector<int> input_socket_indices = get_linked_field_input_indices(*origin_socket);
|
|
|
|
|
for (const int input_index : input_socket_indices) {
|
|
|
|
|
const InputSocketRef &origin_input_socket = origin_node.input(input_index);
|
|
|
|
|
if (!field_state_by_socket_id[origin_input_socket.id()].is_single) {
|
|
|
|
|
if (handled_sockets.add(&origin_input_socket)) {
|
|
|
|
|
sockets_to_check.push(&origin_input_socket);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return Vector<int>(found_input_indices.begin(), found_input_indices.end());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void update_socket_shapes_for_fields(bNodeTree &btree)
|
|
|
|
|
{
|
|
|
|
|
using namespace blender;
|
|
|
|
|
using namespace blender::nodes;
|
|
|
|
|
if (btree.type != NTREE_GEOMETRY) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NodeTreeRef tree{&btree};
|
|
|
|
|
Vector<const NodeRef *> toposort_left_to_right = toposort_nodes(tree, true);
|
|
|
|
|
Vector<const NodeRef *> toposort_right_to_left = toposort_nodes(tree, false);
|
|
|
|
|
|
|
|
|
|
Array<SocketFieldState> field_state_by_socket_id(tree.sockets().size());
|
|
|
|
|
|
|
|
|
|
auto check_if_node_is_adaptive = [](const NodeRef &node) {
|
|
|
|
|
const StringRef node_idname = node.idname();
|
|
|
|
|
return !node_idname.startswith("GeometryNode");
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (const NodeRef *node : toposort_right_to_left) {
|
|
|
|
|
NodeDeclaration *node_decl = nodeDeclarationEnsure(&btree, node->bnode());
|
|
|
|
|
|
|
|
|
|
const bool node_is_adaptive = check_if_node_is_adaptive(*node);
|
|
|
|
|
|
|
|
|
|
for (const OutputSocketRef *output_socket : node->outputs()) {
|
|
|
|
|
SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
|
|
|
|
|
for (const InputSocketRef *target_socket : output_socket->directly_linked_sockets()) {
|
|
|
|
|
state.requires_single |= field_state_by_socket_id[target_socket->id()].requires_single;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (state.requires_single) {
|
|
|
|
|
const Vector<int> input_socket_indices = get_linked_field_input_indices(*output_socket);
|
|
|
|
|
for (const int input_index : input_socket_indices) {
|
|
|
|
|
const InputSocketRef &input_socket = node->input(input_index);
|
|
|
|
|
field_state_by_socket_id[input_socket.id()].requires_single = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const InputSocketRef *input_socket : node->inputs()) {
|
|
|
|
|
SocketFieldState &state = field_state_by_socket_id[input_socket->id()];
|
|
|
|
|
if (state.requires_single) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (node_decl != nullptr && !node_is_adaptive) {
|
|
|
|
|
const SocketDeclaration &socket_decl = *node_decl->inputs()[input_socket->index()];
|
|
|
|
|
state.requires_single |= !socket_decl.is_field();
|
|
|
|
|
}
|
|
|
|
|
if (!is_field_socket_type((eNodeSocketDatatype)input_socket->bsocket()->type)) {
|
|
|
|
|
state.requires_single = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const NodeRef *node : tree.nodes_by_type("NodeGroupInput")) {
|
|
|
|
|
for (const OutputSocketRef *output_socket : node->outputs().drop_back(1)) {
|
|
|
|
|
SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
|
|
|
|
|
if (!state.requires_single) {
|
|
|
|
|
state.is_single = false;
|
|
|
|
|
state.is_field_source = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const NodeRef *node : toposort_left_to_right) {
|
|
|
|
|
NodeDeclaration *node_decl = nodeDeclarationEnsure(&btree, node->bnode());
|
|
|
|
|
|
|
|
|
|
for (const InputSocketRef *input_socket : node->inputs()) {
|
|
|
|
|
SocketFieldState &state = field_state_by_socket_id[input_socket->id()];
|
|
|
|
|
if (state.requires_single) {
|
|
|
|
|
state.is_single = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
state.is_single = true;
|
|
|
|
|
for (const OutputSocketRef *origin_socket : input_socket->logically_linked_sockets()) {
|
|
|
|
|
if (!field_state_by_socket_id[origin_socket->id()].is_single) {
|
|
|
|
|
state.is_single = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const OutputSocketRef *output_socket : node->outputs()) {
|
|
|
|
|
SocketFieldState &state = field_state_by_socket_id[output_socket->id()];
|
|
|
|
|
if (node_decl != nullptr) {
|
|
|
|
|
const SocketDeclaration &socket_decl = *node_decl->outputs()[output_socket->index()];
|
|
|
|
|
if (socket_decl.is_field()) {
|
|
|
|
|
state.is_single = false;
|
|
|
|
|
state.is_field_source = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (output_socket->node().is_group_node()) {
|
|
|
|
|
const std::optional<Vector<int>> *optional_dependencies =
|
|
|
|
|
get_group_output_field_dependencies(*output_socket);
|
|
|
|
|
if (optional_dependencies && !optional_dependencies->has_value()) {
|
|
|
|
|
state.is_single = false;
|
|
|
|
|
state.is_field_source = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const Vector<int> input_socket_indices = get_linked_field_input_indices(*output_socket);
|
|
|
|
|
for (const int input_index : input_socket_indices) {
|
|
|
|
|
const InputSocketRef &input_socket = node->input(input_index);
|
|
|
|
|
if (!field_state_by_socket_id[input_socket.id()].is_single) {
|
|
|
|
|
state.is_single = false;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const NodeRef *group_output_node : tree.nodes_by_type("NodeGroupOutput")) {
|
|
|
|
|
if (!(group_output_node->bnode()->flag & NODE_DO_OUTPUT)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
OutputFieldDependencies *output_field_dependencies = new OutputFieldDependencies();
|
|
|
|
|
for (const InputSocketRef *group_output_socket : group_output_node->inputs().drop_back(1)) {
|
|
|
|
|
std::optional<Vector<int>> dependent_input_indices = find_dependent_group_input_indices(
|
|
|
|
|
*group_output_socket, field_state_by_socket_id);
|
|
|
|
|
output_field_dependencies->append(std::move(dependent_input_indices));
|
|
|
|
|
}
|
|
|
|
|
if (btree.output_field_dependencies != nullptr) {
|
|
|
|
|
delete (OutputFieldDependencies *)btree.output_field_dependencies;
|
|
|
|
|
}
|
|
|
|
|
btree.output_field_dependencies = output_field_dependencies;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const InputSocketRef *socket : tree.input_sockets()) {
|
|
|
|
|
bNodeSocket *bsocket = socket->bsocket();
|
|
|
|
|
const SocketFieldState &state = field_state_by_socket_id[socket->id()];
|
|
|
|
|
if (state.requires_single) {
|
|
|
|
|
bsocket->display_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bsocket->display_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const OutputSocketRef *socket : tree.output_sockets()) {
|
|
|
|
|
bNodeSocket *bsocket = socket->bsocket();
|
|
|
|
|
const SocketFieldState &state = field_state_by_socket_id[socket->id()];
|
|
|
|
|
if (state.is_single) {
|
|
|
|
|
bsocket->display_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
bsocket->display_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
|
|
|
|
|
{
|
|
|
|
|
if (!ntree) {
|
|
|
|
@@ -4519,6 +4825,8 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
|
|
|
|
|
/* update the node level from link dependencies */
|
|
|
|
|
ntree_update_node_level(ntree);
|
|
|
|
|
|
|
|
|
|
update_socket_shapes_for_fields(*ntree);
|
|
|
|
|
|
|
|
|
|
/* check link validity */
|
|
|
|
|
ntree_validate_links(ntree);
|
|
|
|
|
}
|
|
|
|
|