This implements bundles and closures which are described in more detail in this blog post: https://code.blender.org/2024/11/geometry-nodes-workshop-october-2024/ tl;dr: * Bundles are containers that allow storing multiple socket values in a single value. Each value in the bundle is identified by a name. Bundles can be nested. * Closures are functions that are created with the Closure Zone and can be evaluated with the Evaluate Closure node. To use the patch, the `Bundle and Closure Nodes` experimental feature has to be enabled. This is necessary, because these features are not fully done yet and still need iterations to improve the workflow before they can be officially released. These iterations are easier to do in `main` than in a separate branch though. That's because this patch is quite large and somewhat prone to merge conflicts. Also other work we want to do, depends on this. This adds the following new nodes: * Combine Bundle: can pack multiple values into one. * Separate Bundle: extracts values from a bundle. * Closure Zone: outputs a closure zone for use in the `Evaluate Closure` node. * Evaluate Closure: evaluates the passed in closure. Things that will be added soon after this lands: * Fields in bundles and closures. The way this is done changes with #134811, so I rather implement this once both are in `main`. * UI features for keeping sockets in sync (right now there are warnings only). One bigger issue is the limited support for lazyness. For example, all inputs of a Combine Bundle node will be evaluated, even if they are not all needed. The same is true for all captured values of a closure. This is a deeper limitation that needs to be resolved at some point. This will likely be done after an initial version of this patch is done. Pull Request: https://projects.blender.org/blender/blender/pulls/128340
191 lines
7.2 KiB
C++
191 lines
7.2 KiB
C++
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#include "DNA_node_types.h"
|
|
|
|
#include "BKE_compute_contexts.hh"
|
|
|
|
#include <ostream>
|
|
|
|
namespace blender::bke {
|
|
|
|
ModifierComputeContext::ModifierComputeContext(const ComputeContext *parent,
|
|
std::string modifier_name)
|
|
: ComputeContext(s_static_type, parent), modifier_name_(std::move(modifier_name))
|
|
{
|
|
hash_.mix_in(s_static_type, strlen(s_static_type));
|
|
hash_.mix_in(modifier_name_.data(), modifier_name_.size());
|
|
}
|
|
|
|
void ModifierComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
stream << "Modifier: " << modifier_name_;
|
|
}
|
|
|
|
GroupNodeComputeContext::GroupNodeComputeContext(
|
|
const ComputeContext *parent,
|
|
const int32_t node_id,
|
|
const std::optional<ComputeContextHash> &cached_hash)
|
|
: ComputeContext(s_static_type, parent), node_id_(node_id)
|
|
{
|
|
if (cached_hash.has_value()) {
|
|
hash_ = *cached_hash;
|
|
}
|
|
else {
|
|
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
|
* necessary. */
|
|
const int type_size = strlen(s_static_type);
|
|
const int buffer_size = type_size + 1 + sizeof(int32_t);
|
|
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
|
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
|
memcpy(buffer, s_static_type, type_size + 1);
|
|
memcpy(buffer + type_size + 1, &node_id_, sizeof(int32_t));
|
|
hash_.mix_in(buffer, buffer_size);
|
|
}
|
|
}
|
|
|
|
GroupNodeComputeContext::GroupNodeComputeContext(const ComputeContext *parent,
|
|
const bNode &node,
|
|
const bNodeTree &caller_tree)
|
|
: GroupNodeComputeContext(parent, node.identifier)
|
|
{
|
|
caller_group_node_ = &node;
|
|
caller_tree_ = &caller_tree;
|
|
}
|
|
|
|
void GroupNodeComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
if (caller_group_node_ != nullptr) {
|
|
stream << "Node: " << caller_group_node_->name;
|
|
return;
|
|
}
|
|
}
|
|
|
|
SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent,
|
|
const int32_t output_node_id)
|
|
: ComputeContext(s_static_type, parent), output_node_id_(output_node_id)
|
|
{
|
|
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
|
* necessary. */
|
|
const int type_size = strlen(s_static_type);
|
|
const int buffer_size = type_size + 1 + sizeof(int32_t);
|
|
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
|
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
|
memcpy(buffer, s_static_type, type_size + 1);
|
|
memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t));
|
|
hash_.mix_in(buffer, buffer_size);
|
|
}
|
|
|
|
SimulationZoneComputeContext::SimulationZoneComputeContext(const ComputeContext *parent,
|
|
const bNode &node)
|
|
: SimulationZoneComputeContext(parent, node.identifier)
|
|
{
|
|
}
|
|
|
|
void SimulationZoneComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
stream << "Simulation Zone ID: " << output_node_id_;
|
|
}
|
|
|
|
RepeatZoneComputeContext::RepeatZoneComputeContext(const ComputeContext *parent,
|
|
const int32_t output_node_id,
|
|
const int iteration)
|
|
: ComputeContext(s_static_type, parent), output_node_id_(output_node_id), iteration_(iteration)
|
|
{
|
|
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
|
* necessary. */
|
|
const int type_size = strlen(s_static_type);
|
|
const int buffer_size = type_size + 1 + sizeof(int32_t) + sizeof(int);
|
|
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
|
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
|
memcpy(buffer, s_static_type, type_size + 1);
|
|
memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t));
|
|
memcpy(buffer + type_size + 1 + sizeof(int32_t), &iteration_, sizeof(int));
|
|
hash_.mix_in(buffer, buffer_size);
|
|
}
|
|
|
|
RepeatZoneComputeContext::RepeatZoneComputeContext(const ComputeContext *parent,
|
|
const bNode &node,
|
|
const int iteration)
|
|
: RepeatZoneComputeContext(parent, node.identifier, iteration)
|
|
{
|
|
}
|
|
|
|
void RepeatZoneComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
stream << "Repeat Zone ID: " << output_node_id_;
|
|
}
|
|
|
|
ForeachGeometryElementZoneComputeContext::ForeachGeometryElementZoneComputeContext(
|
|
const ComputeContext *parent, const int32_t output_node_id, const int index)
|
|
: ComputeContext(s_static_type, parent), output_node_id_(output_node_id), index_(index)
|
|
{
|
|
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
|
* necessary. */
|
|
const int type_size = strlen(s_static_type);
|
|
const int buffer_size = type_size + 1 + sizeof(int32_t) + sizeof(int);
|
|
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
|
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
|
memcpy(buffer, s_static_type, type_size + 1);
|
|
memcpy(buffer + type_size + 1, &output_node_id_, sizeof(int32_t));
|
|
memcpy(buffer + type_size + 1 + sizeof(int32_t), &index_, sizeof(int));
|
|
hash_.mix_in(buffer, buffer_size);
|
|
}
|
|
|
|
ForeachGeometryElementZoneComputeContext::ForeachGeometryElementZoneComputeContext(
|
|
const ComputeContext *parent, const bNode &node, const int index)
|
|
: ForeachGeometryElementZoneComputeContext(parent, node.identifier, index)
|
|
{
|
|
}
|
|
|
|
void ForeachGeometryElementZoneComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
stream << "Foreach Geometry Element Zone ID: " << output_node_id_;
|
|
}
|
|
|
|
EvaluateClosureComputeContext::EvaluateClosureComputeContext(const ComputeContext *parent,
|
|
const int32_t node_id)
|
|
: ComputeContext(s_static_type, parent), node_id_(node_id)
|
|
{
|
|
/* Mix static type and node id into a single buffer so that only a single call to #mix_in is
|
|
* necessary. */
|
|
const int type_size = strlen(s_static_type);
|
|
const int buffer_size = type_size + 1 + sizeof(int32_t);
|
|
DynamicStackBuffer<64, 8> buffer_owner(buffer_size, 8);
|
|
char *buffer = static_cast<char *>(buffer_owner.buffer());
|
|
memcpy(buffer, s_static_type, type_size + 1);
|
|
memcpy(buffer + type_size + 1, &node_id_, sizeof(int32_t));
|
|
hash_.mix_in(buffer, buffer_size);
|
|
}
|
|
|
|
EvaluateClosureComputeContext::EvaluateClosureComputeContext(const ComputeContext *parent,
|
|
const bNode &node)
|
|
: EvaluateClosureComputeContext(parent, node.identifier)
|
|
{
|
|
evaluate_node_ = &node;
|
|
}
|
|
|
|
void EvaluateClosureComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
if (evaluate_node_ != nullptr) {
|
|
stream << "Evaluate Closure: " << evaluate_node_->name;
|
|
return;
|
|
}
|
|
}
|
|
|
|
OperatorComputeContext::OperatorComputeContext() : OperatorComputeContext(nullptr) {}
|
|
|
|
OperatorComputeContext::OperatorComputeContext(const ComputeContext *parent)
|
|
: ComputeContext(s_static_type, parent)
|
|
{
|
|
hash_.mix_in(s_static_type, strlen(s_static_type));
|
|
}
|
|
|
|
void OperatorComputeContext::print_current_in_line(std::ostream &stream) const
|
|
{
|
|
stream << "Operator";
|
|
}
|
|
|
|
} // namespace blender::bke
|