2023-08-16 00:20:26 +10:00
|
|
|
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
2023-05-31 16:19:06 +02:00
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
#include "NOD_node_declaration.hh"
|
2023-10-04 13:01:45 +02:00
|
|
|
#include "NOD_socket.hh"
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
#include "BKE_node_runtime.hh"
|
2023-06-14 13:56:57 +02:00
|
|
|
#include "BKE_node_tree_anonymous_attributes.hh"
|
|
|
|
|
#include "BKE_node_tree_dot_export.hh"
|
2023-10-20 21:21:40 +02:00
|
|
|
#include "BKE_node_tree_zones.hh"
|
2023-06-14 13:56:57 +02:00
|
|
|
|
|
|
|
|
#include "BLI_bit_group_vector.hh"
|
|
|
|
|
#include "BLI_bit_span_ops.hh"
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
#include "BLI_resource_scope.hh"
|
|
|
|
|
|
2023-10-20 21:21:40 +02:00
|
|
|
#include <iostream>
|
Cleanup: fewer iostreams related includes from BLI/BKE headers
Including <iostream> or similar headers is quite expensive, since it
also pulls in things like <locale> and so on. In many BLI headers,
iostreams are only used to implement some sort of "debug print",
or an operator<< for ostream.
Change some of the commonly used places to instead include <iosfwd>,
which is the standard way of forward-declaring iostreams related
classes, and move the actual debug-print / operator<< implementations
into .cc files.
This is not done for templated classes though (it would be possible
to provide explicit operator<< instantiations somewhere in the
source file, but that would lead to hard-to-figure-out linker error
whenever someone would add a different template type). There, where
possible, I changed from full <iostream> include to only the needed
<ostream> part.
For Span<T>, I just removed print_as_lines since it's not used by
anything. It could be moved into a .cc file using a similar approach
as above if needed.
Doing full blender build changes include counts this way:
- <iostream> 1986 -> 978
- <sstream> 2880 -> 925
It does not affect the total build time much though, mostly because
towards the end of it there's just several CPU cores finishing
compiling OpenVDB related source files.
Pull Request: https://projects.blender.org/blender/blender/pulls/111046
2023-08-11 12:27:56 +03:00
|
|
|
#include <sstream>
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
namespace blender::bke::anonymous_attribute_inferencing {
|
|
|
|
|
namespace aal = nodes::aal;
|
|
|
|
|
using nodes::NodeDeclaration;
|
|
|
|
|
|
2023-01-08 15:17:09 +01:00
|
|
|
static bool socket_is_field(const bNodeSocket &socket)
|
|
|
|
|
{
|
|
|
|
|
return socket.display_shape == SOCK_DISPLAY_SHAPE_DIAMOND;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-05 14:05:30 +01:00
|
|
|
static const aal::RelationsInNode &get_relations_in_node(const bNode &node, ResourceScope &scope)
|
|
|
|
|
{
|
|
|
|
|
if (node.is_group()) {
|
|
|
|
|
if (const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id)) {
|
2023-03-08 15:31:50 +01:00
|
|
|
/* Undefined tree types have no relations. */
|
2024-05-13 16:07:12 +02:00
|
|
|
if (!bke::ntreeIsRegistered(group)) {
|
2023-03-08 15:31:50 +01:00
|
|
|
return scope.construct<aal::RelationsInNode>();
|
|
|
|
|
}
|
2023-06-27 10:07:09 +02:00
|
|
|
/* It's possible that the inferencing failed on the group. */
|
|
|
|
|
if (!group->runtime->anonymous_attribute_inferencing) {
|
|
|
|
|
return scope.construct<aal::RelationsInNode>();
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
return group->runtime->anonymous_attribute_inferencing->tree_relations;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-01-08 15:17:09 +01:00
|
|
|
if (node.is_reroute()) {
|
|
|
|
|
const bNodeSocket &socket = node.input_socket(0);
|
|
|
|
|
if (socket_is_field(socket)) {
|
|
|
|
|
static const aal::RelationsInNode field_relations = []() {
|
|
|
|
|
aal::RelationsInNode relations;
|
|
|
|
|
relations.reference_relations.append({0, 0});
|
|
|
|
|
return relations;
|
|
|
|
|
}();
|
|
|
|
|
return field_relations;
|
|
|
|
|
}
|
|
|
|
|
if (socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
static const aal::RelationsInNode geometry_relations = []() {
|
|
|
|
|
aal::RelationsInNode relations;
|
|
|
|
|
relations.propagate_relations.append({0, 0});
|
|
|
|
|
return relations;
|
|
|
|
|
}();
|
|
|
|
|
return geometry_relations;
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-04-22 20:11:21 +02:00
|
|
|
if (node.is_muted()) {
|
|
|
|
|
aal::RelationsInNode &relations = scope.construct<aal::RelationsInNode>();
|
|
|
|
|
for (const bNodeLink &link : node.internal_links()) {
|
|
|
|
|
const bNodeSocket &input = *link.fromsock;
|
|
|
|
|
const bNodeSocket &output = *link.tosock;
|
|
|
|
|
if (socket_is_field(input) || socket_is_field(output)) {
|
|
|
|
|
relations.reference_relations.append({input.index(), output.index()});
|
|
|
|
|
}
|
|
|
|
|
else if (input.type == SOCK_GEOMETRY) {
|
|
|
|
|
BLI_assert(input.type == output.type);
|
|
|
|
|
relations.propagate_relations.append({input.index(), output.index()});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return relations;
|
|
|
|
|
}
|
2023-12-18 13:01:06 +01:00
|
|
|
if (ELEM(node.type, GEO_NODE_SIMULATION_INPUT, GEO_NODE_SIMULATION_OUTPUT, GEO_NODE_BAKE)) {
|
Geometry Nodes: add simulation support
This adds support for building simulations with geometry nodes. A new
`Simulation Input` and `Simulation Output` node allow maintaining a
simulation state across multiple frames. Together these two nodes form
a `simulation zone` which contains all the nodes that update the simulation
state from one frame to the next.
A new simulation zone can be added via the menu
(`Simulation > Simulation Zone`) or with the node add search.
The simulation state contains a geometry by default. However, it is possible
to add multiple geometry sockets as well as other socket types. Currently,
field inputs are evaluated and stored for the preceding geometry socket in
the order that the sockets are shown. Simulation state items can be added
by linking one of the empty sockets to something else. In the sidebar, there
is a new panel that allows adding, removing and reordering these sockets.
The simulation nodes behave as follows:
* On the first frame, the inputs of the `Simulation Input` node are evaluated
to initialize the simulation state. In later frames these sockets are not
evaluated anymore. The `Delta Time` at the first frame is zero, but the
simulation zone is still evaluated.
* On every next frame, the `Simulation Input` node outputs the simulation
state of the previous frame. Nodes in the simulation zone can edit that
data in arbitrary ways, also taking into account the `Delta Time`. The new
simulation state has to be passed to the `Simulation Output` node where it
is cached and forwarded.
* On a frame that is already cached or baked, the nodes in the simulation
zone are not evaluated, because the `Simulation Output` node can return
the previously cached data directly.
It is not allowed to connect sockets from inside the simulation zone to the
outside without going through the `Simulation Output` node. This is a necessary
restriction to make caching and sub-frame interpolation work. Links can go into
the simulation zone without problems though.
Anonymous attributes are not propagated by the simulation nodes unless they
are explicitly stored in the simulation state. This is unfortunate, but
currently there is no practical and reliable alternative. The core problem
is detecting which anonymous attributes will be required for the simulation
and afterwards. While we can detect this for the current evaluation, we can't
look into the future in time to see what data will be necessary. We intend to
make it easier to explicitly pass data through a simulation in the future,
even if the simulation is in a nested node group.
There is a new `Simulation Nodes` panel in the physics tab in the properties
editor. It allows baking all simulation zones on the selected objects. The
baking options are intentially kept at a minimum for this MVP. More features
for simulation baking as well as baking in general can be expected to be added
separately.
All baked data is stored on disk in a folder next to the .blend file. #106937
describes how baking is implemented in more detail. Volumes can not be baked
yet and materials are lost during baking for now. Packing the baked data into
the .blend file is not yet supported.
The timeline indicates which frames are currently cached, baked or cached but
invalidated by user-changes.
Simulation input and output nodes are internally linked together by their
`bNode.identifier` which stays the same even if the node name changes. They
are generally added and removed together. However, there are still cases where
"dangling" simulation nodes can be created currently. Those generally don't
cause harm, but would be nice to avoid this in more cases in the future.
Co-authored-by: Hans Goudey <h.goudey@me.com>
Co-authored-by: Lukas Tönne <lukas@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/104924
2023-05-03 13:18:51 +02:00
|
|
|
aal::RelationsInNode &relations = scope.construct<aal::RelationsInNode>();
|
|
|
|
|
{
|
|
|
|
|
/* Add eval relations. */
|
|
|
|
|
int last_geometry_index = -1;
|
|
|
|
|
for (const int i : node.input_sockets().index_range()) {
|
|
|
|
|
const bNodeSocket &socket = node.input_socket(i);
|
|
|
|
|
if (socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
last_geometry_index = i;
|
|
|
|
|
}
|
|
|
|
|
else if (socket_is_field(socket)) {
|
|
|
|
|
if (last_geometry_index != -1) {
|
|
|
|
|
relations.eval_relations.append({i, last_geometry_index});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
/* Add available relations. */
|
|
|
|
|
int last_geometry_index = -1;
|
|
|
|
|
for (const int i : node.output_sockets().index_range()) {
|
|
|
|
|
const bNodeSocket &socket = node.output_socket(i);
|
|
|
|
|
if (socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
last_geometry_index = i;
|
|
|
|
|
}
|
|
|
|
|
else if (socket_is_field(socket)) {
|
|
|
|
|
if (last_geometry_index == -1) {
|
|
|
|
|
relations.available_on_none.append(i);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
relations.available_relations.append({i, last_geometry_index});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return relations;
|
|
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
if (ELEM(node.type, GEO_NODE_REPEAT_INPUT, GEO_NODE_REPEAT_OUTPUT)) {
|
|
|
|
|
aal::RelationsInNode &relations = scope.construct<aal::RelationsInNode>();
|
2024-01-14 14:03:55 +01:00
|
|
|
/* TODO: Use smaller set of eval and available relations. For now this makes the pessimistic
|
|
|
|
|
* assumption that every field may belong to any geometry. In many cases it should be possible
|
|
|
|
|
* to reduce this set a bit with static analysis. */
|
2023-07-11 22:36:10 +02:00
|
|
|
for (const bNodeSocket *socket : node.output_sockets()) {
|
|
|
|
|
if (socket->type == SOCK_GEOMETRY) {
|
|
|
|
|
for (const bNodeSocket *other_output : node.output_sockets()) {
|
|
|
|
|
if (socket_is_field(*other_output)) {
|
|
|
|
|
relations.available_relations.append({other_output->index(), socket->index()});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const bNodeSocket *socket : node.input_sockets()) {
|
|
|
|
|
if (socket->type == SOCK_GEOMETRY) {
|
|
|
|
|
for (const bNodeSocket *other_input : node.input_sockets()) {
|
|
|
|
|
if (socket_is_field(*other_input)) {
|
|
|
|
|
relations.eval_relations.append({other_input->index(), socket->index()});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-14 14:03:55 +01:00
|
|
|
const int items_num = node.output_sockets().size() - 1;
|
|
|
|
|
for (const int i : IndexRange(items_num)) {
|
|
|
|
|
const int input_index = (node.type == GEO_NODE_REPEAT_INPUT) ? i + 1 : i;
|
|
|
|
|
const int output_index = i;
|
|
|
|
|
const bNodeSocket &input_socket = node.input_socket(input_index);
|
|
|
|
|
if (input_socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
relations.propagate_relations.append({input_index, output_index});
|
|
|
|
|
}
|
|
|
|
|
else if (socket_is_field(input_socket)) {
|
|
|
|
|
relations.reference_relations.append({input_index, output_index});
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-07-11 22:36:10 +02:00
|
|
|
return relations;
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
if (const NodeDeclaration *node_decl = node.declaration()) {
|
|
|
|
|
if (const aal::RelationsInNode *relations = node_decl->anonymous_attribute_relations()) {
|
|
|
|
|
return *relations;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return scope.construct<aal::RelationsInNode>();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Array<const aal::RelationsInNode *> get_relations_by_node(const bNodeTree &tree,
|
|
|
|
|
ResourceScope &scope)
|
|
|
|
|
{
|
|
|
|
|
const Span<const bNode *> nodes = tree.all_nodes();
|
|
|
|
|
Array<const aal::RelationsInNode *> relations_by_node(nodes.size());
|
|
|
|
|
for (const int i : nodes.index_range()) {
|
|
|
|
|
relations_by_node[i] = &get_relations_in_node(*nodes[i], scope);
|
|
|
|
|
}
|
|
|
|
|
return relations_by_node;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
class bNodeTreeToDotOptionsForAnonymousAttributeInferencing : public bNodeTreeToDotOptions {
|
|
|
|
|
private:
|
|
|
|
|
const AnonymousAttributeInferencingResult &result_;
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
public:
|
|
|
|
|
bNodeTreeToDotOptionsForAnonymousAttributeInferencing(
|
|
|
|
|
const AnonymousAttributeInferencingResult &result)
|
|
|
|
|
: result_(result)
|
|
|
|
|
{
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2024-01-19 12:08:40 -05:00
|
|
|
std::string socket_name(const bNodeSocket &socket) const override
|
2023-06-14 13:56:57 +02:00
|
|
|
{
|
|
|
|
|
if (socket.type == SOCK_GEOMETRY) {
|
|
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << socket.identifier << " [";
|
|
|
|
|
bits::foreach_1_index(result_.required_fields_by_geometry_socket[socket.index_in_tree()],
|
|
|
|
|
[&](const int i) { ss << i << ","; });
|
2023-06-29 12:32:12 +02:00
|
|
|
ss << "] [";
|
|
|
|
|
bits::foreach_1_index(
|
|
|
|
|
result_.propagate_to_output_by_geometry_socket[socket.index_in_tree()],
|
|
|
|
|
[&](const int i) { ss << result_.propagated_output_geometry_indices[i] << ","; });
|
2023-06-14 13:56:57 +02:00
|
|
|
ss << "]";
|
|
|
|
|
return ss.str();
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2024-01-19 12:08:40 -05:00
|
|
|
if (nodes::socket_type_supports_fields(eNodeSocketDatatype(socket.type))) {
|
2023-06-14 13:56:57 +02:00
|
|
|
std::stringstream ss;
|
|
|
|
|
ss << socket.identifier << " [";
|
|
|
|
|
bits::foreach_1_index(result_.propagated_fields_by_socket[socket.index_in_tree()],
|
|
|
|
|
[&](const int i) { ss << i << ","; });
|
|
|
|
|
ss << "]";
|
|
|
|
|
return ss.str();
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
return socket.identifier;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
};
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-10-23 21:26:57 +02:00
|
|
|
static bool or_into_each_other_masked(MutableBoundedBitSpan a,
|
|
|
|
|
MutableBoundedBitSpan b,
|
|
|
|
|
const BoundedBitSpan mask)
|
|
|
|
|
{
|
|
|
|
|
if (bits::spans_equal_masked(a, b, mask)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
bits::inplace_or_masked(a, mask, b);
|
|
|
|
|
bits::inplace_or_masked(b, mask, a);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-20 21:21:40 +02:00
|
|
|
static bool or_into_each_other(MutableBoundedBitSpan a, MutableBoundedBitSpan b)
|
|
|
|
|
{
|
|
|
|
|
if (bits::spans_equal(a, b)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
a |= b;
|
|
|
|
|
b |= a;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-23 21:26:57 +02:00
|
|
|
static bool or_into_each_other_masked(BitGroupVector<> &vec,
|
|
|
|
|
const int64_t a,
|
|
|
|
|
const int64_t b,
|
|
|
|
|
const BoundedBitSpan mask)
|
|
|
|
|
{
|
|
|
|
|
return or_into_each_other_masked(vec[a], vec[b], mask);
|
|
|
|
|
}
|
|
|
|
|
|
2023-10-20 21:21:40 +02:00
|
|
|
static bool or_into_each_other(BitGroupVector<> &vec, const int64_t a, const int64_t b)
|
|
|
|
|
{
|
|
|
|
|
return or_into_each_other(vec[a], vec[b]);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-27 10:55:39 +11:00
|
|
|
static AnonymousAttributeInferencingResult analyze_anonymous_attribute_usages(
|
2023-06-14 13:56:57 +02:00
|
|
|
const bNodeTree &tree)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(!tree.has_available_link_cycle());
|
2023-09-14 14:13:07 +02:00
|
|
|
tree.ensure_interface_cache();
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
ResourceScope scope;
|
|
|
|
|
const Array<const aal::RelationsInNode *> relations_by_node = get_relations_by_node(tree, scope);
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-10-20 21:21:40 +02:00
|
|
|
/* Repeat zones need some special behavior because they can propagate anonymous attributes from
|
|
|
|
|
* right to left (from the repeat output to the repeat input node). */
|
|
|
|
|
const bNodeTreeZones *zones = tree.zones();
|
|
|
|
|
Vector<const bNodeTreeZone *> repeat_zones_to_consider;
|
|
|
|
|
if (zones) {
|
|
|
|
|
for (const std::unique_ptr<bNodeTreeZone> &zone : zones->zones) {
|
|
|
|
|
if (ELEM(nullptr, zone->input_node, zone->output_node)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (zone->output_node->type != GEO_NODE_REPEAT_OUTPUT) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
repeat_zones_to_consider.append(zone.get());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
Vector<FieldSource> all_field_sources;
|
|
|
|
|
Vector<GeometrySource> all_geometry_sources;
|
|
|
|
|
|
|
|
|
|
/* Find input field and geometry sources. */
|
|
|
|
|
for (const int i : tree.interface_inputs().index_range()) {
|
2023-08-30 12:37:21 +02:00
|
|
|
const bNodeTreeInterfaceSocket &interface_socket = *tree.interface_inputs()[i];
|
2024-05-13 16:07:12 +02:00
|
|
|
const bNodeSocketType *typeinfo = bke::nodeSocketTypeFind(interface_socket.socket_type);
|
2023-08-30 12:37:21 +02:00
|
|
|
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
|
|
|
|
|
if (type == SOCK_GEOMETRY) {
|
2023-06-14 13:56:57 +02:00
|
|
|
all_geometry_sources.append_and_get_index({InputGeometrySource{i}});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-10-04 13:01:45 +02:00
|
|
|
else if (nodes::socket_type_supports_fields(type)) {
|
2023-06-14 13:56:57 +02:00
|
|
|
all_field_sources.append_and_get_index({InputFieldSource{i}});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
for (const int geometry_source_index : all_geometry_sources.index_range()) {
|
|
|
|
|
for (const int field_source_index : all_field_sources.index_range()) {
|
|
|
|
|
all_geometry_sources[geometry_source_index].field_sources.append(field_source_index);
|
|
|
|
|
all_field_sources[field_source_index].geometry_sources.append(geometry_source_index);
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
/* Find socket field and geometry sources. */
|
|
|
|
|
Map<const bNodeSocket *, int> field_source_by_socket;
|
|
|
|
|
Map<const bNodeSocket *, int> geometry_source_by_socket;
|
|
|
|
|
for (const bNode *node : tree.all_nodes()) {
|
|
|
|
|
const aal::RelationsInNode &relations = *relations_by_node[node->index()];
|
|
|
|
|
for (const aal::AvailableRelation &relation : relations.available_relations) {
|
|
|
|
|
const bNodeSocket &geometry_socket = node->output_socket(relation.geometry_output);
|
|
|
|
|
const bNodeSocket &field_socket = node->output_socket(relation.field_output);
|
|
|
|
|
if (!field_socket.is_available()) {
|
|
|
|
|
continue;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
if (!field_socket.is_directly_linked()) {
|
|
|
|
|
continue;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
const int field_source_index = field_source_by_socket.lookup_or_add_cb(&field_socket, [&]() {
|
|
|
|
|
return all_field_sources.append_and_get_index({SocketFieldSource{&field_socket}});
|
|
|
|
|
});
|
|
|
|
|
const int geometry_source_index = geometry_source_by_socket.lookup_or_add_cb(
|
|
|
|
|
&geometry_socket, [&]() {
|
|
|
|
|
return all_geometry_sources.append_and_get_index(
|
|
|
|
|
{SocketGeometrySource{&geometry_socket}});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
all_field_sources[field_source_index].geometry_sources.append(geometry_source_index);
|
|
|
|
|
all_geometry_sources[geometry_source_index].field_sources.append(field_source_index);
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
const int sockets_num = tree.all_sockets().size();
|
|
|
|
|
BitGroupVector<> propagated_fields_by_socket(sockets_num, all_field_sources.size(), false);
|
|
|
|
|
BitGroupVector<> propagated_geometries_by_socket(
|
|
|
|
|
sockets_num, all_geometry_sources.size(), false);
|
|
|
|
|
BitGroupVector<> available_fields_by_geometry_socket(
|
|
|
|
|
sockets_num, all_field_sources.size(), false);
|
|
|
|
|
|
|
|
|
|
/* Insert field and geometry sources into the maps for the first inferencing pass. */
|
|
|
|
|
for (const int field_source_index : all_field_sources.index_range()) {
|
|
|
|
|
const FieldSource &field_source = all_field_sources[field_source_index];
|
|
|
|
|
if (const auto *input_field = std::get_if<InputFieldSource>(&field_source.data)) {
|
|
|
|
|
for (const bNode *node : tree.group_input_nodes()) {
|
|
|
|
|
const bNodeSocket &socket = node->output_socket(input_field->input_index);
|
|
|
|
|
propagated_fields_by_socket[socket.index_in_tree()][field_source_index].set();
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
2023-06-14 13:56:57 +02:00
|
|
|
const auto &socket_field = std::get<SocketFieldSource>(field_source.data);
|
|
|
|
|
propagated_fields_by_socket[socket_field.socket->index_in_tree()][field_source_index].set();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const int geometry_source_index : all_geometry_sources.index_range()) {
|
|
|
|
|
const GeometrySource &geometry_source = all_geometry_sources[geometry_source_index];
|
|
|
|
|
if (const auto *input_geometry = std::get_if<InputGeometrySource>(&geometry_source.data)) {
|
|
|
|
|
for (const bNode *node : tree.group_input_nodes()) {
|
|
|
|
|
const bNodeSocket &socket = node->output_socket(input_geometry->input_index);
|
|
|
|
|
const int socket_i = socket.index_in_tree();
|
|
|
|
|
propagated_geometries_by_socket[socket_i][geometry_source_index].set();
|
|
|
|
|
for (const int field_source_index : geometry_source.field_sources) {
|
|
|
|
|
available_fields_by_geometry_socket[socket_i][field_source_index].set();
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
else {
|
|
|
|
|
const auto &socket_geometry = std::get<SocketGeometrySource>(geometry_source.data);
|
|
|
|
|
const int socket_i = socket_geometry.socket->index_in_tree();
|
|
|
|
|
propagated_geometries_by_socket[socket_i][geometry_source_index].set();
|
|
|
|
|
for (const int field_source_index : geometry_source.field_sources) {
|
|
|
|
|
available_fields_by_geometry_socket[socket_i][field_source_index].set();
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
/* Inferencing pass from left to right to figure out where fields and geometries may be
|
|
|
|
|
* propagated to. */
|
2023-10-20 21:21:40 +02:00
|
|
|
auto pass_left_to_right = [&]() {
|
|
|
|
|
for (const bNode *node : tree.toposort_left_to_right()) {
|
|
|
|
|
for (const bNodeSocket *socket : node->input_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const int dst_index = socket->index_in_tree();
|
|
|
|
|
for (const bNodeLink *link : socket->directly_linked_links()) {
|
|
|
|
|
if (link->is_used()) {
|
|
|
|
|
const int src_index = link->fromsock->index_in_tree();
|
|
|
|
|
propagated_fields_by_socket[dst_index] |= propagated_fields_by_socket[src_index];
|
|
|
|
|
propagated_geometries_by_socket[dst_index] |=
|
|
|
|
|
propagated_geometries_by_socket[src_index];
|
|
|
|
|
available_fields_by_geometry_socket[dst_index] |=
|
|
|
|
|
available_fields_by_geometry_socket[src_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2024-01-14 14:03:55 +01:00
|
|
|
switch (node->type) {
|
|
|
|
|
default: {
|
|
|
|
|
const aal::RelationsInNode &relations = *relations_by_node[node->index()];
|
|
|
|
|
for (const aal::ReferenceRelation &relation : relations.reference_relations) {
|
|
|
|
|
const bNodeSocket &from_socket = node->input_socket(relation.from_field_input);
|
|
|
|
|
const bNodeSocket &to_socket = node->output_socket(relation.to_field_output);
|
|
|
|
|
if (!from_socket.is_available() || !to_socket.is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const int src_index = from_socket.index_in_tree();
|
|
|
|
|
const int dst_index = to_socket.index_in_tree();
|
2023-10-23 21:26:57 +02:00
|
|
|
propagated_fields_by_socket[dst_index] |= propagated_fields_by_socket[src_index];
|
2024-01-14 14:03:55 +01:00
|
|
|
}
|
|
|
|
|
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
|
|
|
|
|
const bNodeSocket &from_socket = node->input_socket(relation.from_geometry_input);
|
|
|
|
|
const bNodeSocket &to_socket = node->output_socket(relation.to_geometry_output);
|
|
|
|
|
if (!from_socket.is_available() || !to_socket.is_available()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
const int src_index = from_socket.index_in_tree();
|
|
|
|
|
const int dst_index = to_socket.index_in_tree();
|
2023-10-23 21:26:57 +02:00
|
|
|
propagated_geometries_by_socket[dst_index] |=
|
|
|
|
|
propagated_geometries_by_socket[src_index];
|
|
|
|
|
available_fields_by_geometry_socket[dst_index] |=
|
|
|
|
|
available_fields_by_geometry_socket[src_index];
|
|
|
|
|
}
|
2024-01-14 14:03:55 +01:00
|
|
|
break;
|
2023-10-23 21:26:57 +02:00
|
|
|
}
|
2024-01-14 14:03:55 +01:00
|
|
|
/* The repeat output node needs special handling for two reasons:
|
|
|
|
|
* - It propagates data directly from the zone input in case the iteration count is zero.
|
|
|
|
|
* - Fields coming out of the repeat zone are wrapped by a new #FieldSource, because the
|
|
|
|
|
* intermediate fields from within the zone are not available afterwards. */
|
|
|
|
|
case GEO_NODE_REPEAT_OUTPUT: {
|
|
|
|
|
if (zones == nullptr) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
/* If the amount of iterations is zero, the data is directly forwarded from the Repeat
|
|
|
|
|
* Input to the Repeat Output node. Therefor, all anonymous attributes may be propagated
|
|
|
|
|
* as well. */
|
|
|
|
|
const bNodeTreeZone *zone = zones->get_zone_by_node(node->identifier);
|
|
|
|
|
const int items_num = node->output_sockets().size() - 1;
|
|
|
|
|
if (const bNode *input_node = zone->input_node) {
|
|
|
|
|
for (const int i : IndexRange(items_num)) {
|
|
|
|
|
const int src_index = input_node->input_socket(i + 1).index_in_tree();
|
|
|
|
|
const int dst_index = node->output_socket(i).index_in_tree();
|
|
|
|
|
propagated_fields_by_socket[dst_index] |= propagated_fields_by_socket[src_index];
|
|
|
|
|
propagated_geometries_by_socket[dst_index] |=
|
|
|
|
|
propagated_geometries_by_socket[src_index];
|
|
|
|
|
available_fields_by_geometry_socket[dst_index] |=
|
|
|
|
|
available_fields_by_geometry_socket[src_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto can_propagate_field_source_out_of_zone = [&](const int field_source_index) {
|
|
|
|
|
const FieldSource &field_source = all_field_sources[field_source_index];
|
|
|
|
|
if (const auto *socket_field_source = std::get_if<SocketFieldSource>(
|
|
|
|
|
&field_source.data))
|
|
|
|
|
{
|
|
|
|
|
const bNode &field_source_node = socket_field_source->socket->owner_node();
|
|
|
|
|
if (zone->contains_node_recursively(field_source_node)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
auto can_propagated_geometry_source_out_of_zone = [&](const int geometry_source_index) {
|
|
|
|
|
const GeometrySource &geometry_source = all_geometry_sources[geometry_source_index];
|
|
|
|
|
if (const auto *socket_geometry_source = std::get_if<SocketGeometrySource>(
|
|
|
|
|
&geometry_source.data))
|
|
|
|
|
{
|
|
|
|
|
const bNode &geometry_source_node = socket_geometry_source->socket->owner_node();
|
|
|
|
|
if (zone->contains_node_recursively(geometry_source_node)) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/* Propagate fields that have not been created inside of the repeat zones. Field sources
|
|
|
|
|
* from inside the repeat zone become new field sources on the outside. */
|
|
|
|
|
for (const int i : IndexRange(items_num)) {
|
|
|
|
|
const int src_index = node->input_socket(i).index_in_tree();
|
|
|
|
|
const int dst_index = node->output_socket(i).index_in_tree();
|
|
|
|
|
bits::foreach_1_index(
|
|
|
|
|
propagated_fields_by_socket[src_index], [&](const int field_source_index) {
|
|
|
|
|
if (can_propagate_field_source_out_of_zone(field_source_index)) {
|
|
|
|
|
propagated_fields_by_socket[dst_index][field_source_index].set();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
bits::foreach_1_index(
|
|
|
|
|
available_fields_by_geometry_socket[src_index], [&](const int field_source_index) {
|
|
|
|
|
if (can_propagate_field_source_out_of_zone(field_source_index)) {
|
|
|
|
|
available_fields_by_geometry_socket[dst_index][field_source_index].set();
|
2024-01-09 10:45:50 +01:00
|
|
|
}
|
2024-01-14 14:03:55 +01:00
|
|
|
});
|
|
|
|
|
bits::foreach_1_index(
|
|
|
|
|
propagated_geometries_by_socket[src_index], [&](const int geometry_source_index) {
|
|
|
|
|
if (can_propagated_geometry_source_out_of_zone(geometry_source_index)) {
|
|
|
|
|
propagated_geometries_by_socket[dst_index][geometry_source_index].set();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
break;
|
2024-01-09 10:45:50 +01:00
|
|
|
}
|
2023-10-23 21:26:57 +02:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
}
|
2023-10-20 21:21:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
pass_left_to_right();
|
|
|
|
|
|
|
|
|
|
/* Repeat zones may need multiple inference passes. That's because anonymous attributes
|
|
|
|
|
* propagated to a repeat output node also come out of the corresponding repeat input node. */
|
|
|
|
|
bool changed = false;
|
|
|
|
|
for (const bNodeTreeZone *zone : repeat_zones_to_consider) {
|
|
|
|
|
const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
|
|
|
|
zone->output_node->storage);
|
2023-10-23 21:26:57 +02:00
|
|
|
/* Only field and geometry sources that come before the repeat zone, can be propagated from
|
|
|
|
|
* the repeat output to the repeat input node. Otherwise, a socket can depend on the field
|
|
|
|
|
* source that only comes later in the tree, which leads to a cyclic dependency. */
|
|
|
|
|
BitVector<> input_propagated_fields(all_field_sources.size(), false);
|
|
|
|
|
BitVector<> input_propagated_geometries(all_geometry_sources.size(), false);
|
|
|
|
|
for (const bNodeSocket *socket : zone->input_node->input_sockets()) {
|
|
|
|
|
const int src = socket->index_in_tree();
|
|
|
|
|
input_propagated_fields |= propagated_fields_by_socket[src];
|
|
|
|
|
input_propagated_geometries |= propagated_geometries_by_socket[src];
|
|
|
|
|
}
|
|
|
|
|
for (const bNodeLink *link : zone->border_links) {
|
|
|
|
|
const int src = link->fromsock->index_in_tree();
|
|
|
|
|
input_propagated_fields |= propagated_fields_by_socket[src];
|
|
|
|
|
input_propagated_geometries |= propagated_geometries_by_socket[src];
|
|
|
|
|
}
|
2023-10-20 21:21:40 +02:00
|
|
|
for (const int i : IndexRange(storage.items_num)) {
|
|
|
|
|
const bNodeSocket &body_input_socket = zone->input_node->output_socket(i);
|
|
|
|
|
const bNodeSocket &body_output_socket = zone->output_node->input_socket(i);
|
|
|
|
|
const int in_index = body_input_socket.index_in_tree();
|
|
|
|
|
const int out_index = body_output_socket.index_in_tree();
|
|
|
|
|
|
2023-10-23 21:26:57 +02:00
|
|
|
changed |= or_into_each_other_masked(
|
|
|
|
|
propagated_fields_by_socket, in_index, out_index, input_propagated_fields);
|
|
|
|
|
changed |= or_into_each_other_masked(
|
|
|
|
|
propagated_geometries_by_socket, in_index, out_index, input_propagated_geometries);
|
|
|
|
|
changed |= or_into_each_other_masked(
|
|
|
|
|
available_fields_by_geometry_socket, in_index, out_index, input_propagated_fields);
|
2023-06-14 13:56:57 +02:00
|
|
|
}
|
2023-10-20 21:21:40 +02:00
|
|
|
}
|
|
|
|
|
if (!changed) {
|
|
|
|
|
break;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
BitGroupVector<> required_fields_by_geometry_socket(
|
|
|
|
|
sockets_num, all_field_sources.size(), false);
|
|
|
|
|
VectorSet<int> propagated_output_geometry_indices;
|
|
|
|
|
aal::RelationsInNode tree_relations;
|
|
|
|
|
|
2023-10-20 21:21:40 +02:00
|
|
|
/* Create #PropagateRelation, #AvailableRelation and #ReferenceRelation for the tree based on
|
|
|
|
|
* the propagated data from above. */
|
2023-06-14 13:56:57 +02:00
|
|
|
if (const bNode *group_output_node = tree.group_output_node()) {
|
|
|
|
|
for (const bNodeSocket *socket : group_output_node->input_sockets().drop_back(1)) {
|
|
|
|
|
if (socket->type == SOCK_GEOMETRY) {
|
|
|
|
|
const BoundedBitSpan propagated_geometries =
|
|
|
|
|
propagated_geometries_by_socket[socket->index_in_tree()];
|
|
|
|
|
bits::foreach_1_index(propagated_geometries, [&](const int geometry_source_index) {
|
|
|
|
|
const GeometrySource &geometry_source = all_geometry_sources[geometry_source_index];
|
|
|
|
|
if (const auto *input_geometry = std::get_if<InputGeometrySource>(&geometry_source.data))
|
|
|
|
|
{
|
|
|
|
|
tree_relations.propagate_relations.append(
|
|
|
|
|
aal::PropagateRelation{input_geometry->input_index, socket->index()});
|
|
|
|
|
propagated_output_geometry_indices.add(socket->index());
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
else {
|
|
|
|
|
[[maybe_unused]] const auto &socket_geometry = std::get<SocketGeometrySource>(
|
|
|
|
|
geometry_source.data);
|
|
|
|
|
for (const int field_source_index : geometry_source.field_sources) {
|
|
|
|
|
for (const bNodeSocket *other_socket :
|
2024-01-02 18:12:54 +01:00
|
|
|
group_output_node->input_sockets().drop_back(1))
|
|
|
|
|
{
|
2023-10-04 13:01:45 +02:00
|
|
|
if (!nodes::socket_type_supports_fields(eNodeSocketDatatype(other_socket->type))) {
|
2023-06-14 13:56:57 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (propagated_fields_by_socket[other_socket->index_in_tree()][field_source_index]
|
2024-01-02 18:12:54 +01:00
|
|
|
.test())
|
|
|
|
|
{
|
2023-06-14 13:56:57 +02:00
|
|
|
tree_relations.available_relations.append(
|
|
|
|
|
aal::AvailableRelation{other_socket->index(), socket->index()});
|
|
|
|
|
required_fields_by_geometry_socket[socket->index_in_tree()][field_source_index]
|
|
|
|
|
.set();
|
|
|
|
|
}
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-10-04 13:01:45 +02:00
|
|
|
else if (nodes::socket_type_supports_fields(eNodeSocketDatatype(socket->type))) {
|
2023-06-14 13:56:57 +02:00
|
|
|
const BoundedBitSpan propagated_fields =
|
|
|
|
|
propagated_fields_by_socket[socket->index_in_tree()];
|
|
|
|
|
bits::foreach_1_index(propagated_fields, [&](const int field_source_index) {
|
|
|
|
|
const FieldSource &field_source = all_field_sources[field_source_index];
|
|
|
|
|
if (const auto *input_field = std::get_if<InputFieldSource>(&field_source.data)) {
|
|
|
|
|
tree_relations.reference_relations.append(
|
|
|
|
|
aal::ReferenceRelation{input_field->input_index, socket->index()});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
/* Initialize map for second inferencing pass. */
|
|
|
|
|
BitGroupVector<> propagate_to_output_by_geometry_socket(
|
|
|
|
|
sockets_num, propagated_output_geometry_indices.size(), false);
|
|
|
|
|
for (const aal::PropagateRelation &relation : tree_relations.propagate_relations) {
|
|
|
|
|
const bNodeSocket &socket = tree.group_output_node()->input_socket(
|
|
|
|
|
relation.to_geometry_output);
|
|
|
|
|
propagate_to_output_by_geometry_socket[socket.index_in_tree()]
|
|
|
|
|
[propagated_output_geometry_indices.index_of(
|
|
|
|
|
relation.to_geometry_output)]
|
|
|
|
|
.set();
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
/* Inferencing pass from right to left to determine which anonymous attributes have to be
|
|
|
|
|
* propagated to which geometry sockets. */
|
2023-10-20 21:21:40 +02:00
|
|
|
auto pass_right_to_left = [&]() {
|
|
|
|
|
for (const bNode *node : tree.toposort_right_to_left()) {
|
|
|
|
|
for (const bNodeSocket *socket : node->output_sockets()) {
|
|
|
|
|
if (!socket->is_available()) {
|
|
|
|
|
continue;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-10-20 21:21:40 +02:00
|
|
|
const int dst_index = socket->index_in_tree();
|
|
|
|
|
for (const bNodeLink *link : socket->directly_linked_links()) {
|
|
|
|
|
if (link->is_used()) {
|
|
|
|
|
const int src_index = link->tosock->index_in_tree();
|
|
|
|
|
required_fields_by_geometry_socket[dst_index] |=
|
|
|
|
|
required_fields_by_geometry_socket[src_index];
|
|
|
|
|
propagate_to_output_by_geometry_socket[dst_index] |=
|
|
|
|
|
propagate_to_output_by_geometry_socket[src_index];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const aal::RelationsInNode &relations = *relations_by_node[node->index()];
|
|
|
|
|
for (const aal::PropagateRelation &relation : relations.propagate_relations) {
|
|
|
|
|
const bNodeSocket &output_socket = node->output_socket(relation.to_geometry_output);
|
|
|
|
|
const bNodeSocket &input_socket = node->input_socket(relation.from_geometry_input);
|
|
|
|
|
const int src_index = output_socket.index_in_tree();
|
|
|
|
|
const int dst_index = input_socket.index_in_tree();
|
|
|
|
|
required_fields_by_geometry_socket[dst_index] |=
|
|
|
|
|
required_fields_by_geometry_socket[src_index];
|
|
|
|
|
propagate_to_output_by_geometry_socket[dst_index] |=
|
|
|
|
|
propagate_to_output_by_geometry_socket[src_index];
|
|
|
|
|
}
|
|
|
|
|
for (const aal::EvalRelation &relation : relations.eval_relations) {
|
|
|
|
|
const bNodeSocket &geometry_socket = node->input_socket(relation.geometry_input);
|
|
|
|
|
const bNodeSocket &field_socket = node->input_socket(relation.field_input);
|
|
|
|
|
required_fields_by_geometry_socket[geometry_socket.index_in_tree()] |=
|
|
|
|
|
propagated_fields_by_socket[field_socket.index_in_tree()];
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
2023-10-20 21:21:40 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
while (true) {
|
|
|
|
|
pass_right_to_left();
|
|
|
|
|
|
|
|
|
|
/* Data required by a repeat input node will also be required by the repeat output node,
|
|
|
|
|
* because that's where the data comes from after the first iteration. */
|
|
|
|
|
bool changed = false;
|
|
|
|
|
for (const bNodeTreeZone *zone : repeat_zones_to_consider) {
|
|
|
|
|
const auto &storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
|
|
|
|
zone->output_node->storage);
|
|
|
|
|
for (const int i : IndexRange(storage.items_num)) {
|
|
|
|
|
const bNodeSocket &body_input_socket = zone->input_node->output_socket(i);
|
|
|
|
|
const bNodeSocket &body_output_socket = zone->output_node->input_socket(i);
|
|
|
|
|
const int in_index = body_input_socket.index_in_tree();
|
|
|
|
|
const int out_index = body_output_socket.index_in_tree();
|
|
|
|
|
|
|
|
|
|
changed |= or_into_each_other(required_fields_by_geometry_socket, in_index, out_index);
|
|
|
|
|
changed |= or_into_each_other(propagate_to_output_by_geometry_socket, in_index, out_index);
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
}
|
2023-10-20 21:21:40 +02:00
|
|
|
if (!changed) {
|
|
|
|
|
break;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
/* Make sure that only available fields are also required. */
|
|
|
|
|
required_fields_by_geometry_socket.all_bits() &= available_fields_by_geometry_socket.all_bits();
|
2023-01-05 14:05:30 +01:00
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
/* Create #EvalRelation for the tree. */
|
2023-08-30 12:37:21 +02:00
|
|
|
tree.ensure_topology_cache();
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
for (const int interface_i : tree.interface_inputs().index_range()) {
|
2023-08-30 12:37:21 +02:00
|
|
|
const bNodeTreeInterfaceSocket &interface_socket = *tree.interface_inputs()[interface_i];
|
|
|
|
|
const bNodeSocketType *typeinfo = interface_socket.socket_typeinfo();
|
|
|
|
|
eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
|
|
|
|
|
if (socket_type != SOCK_GEOMETRY) {
|
2023-01-05 14:05:30 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
BitVector<> required_fields(all_field_sources.size(), false);
|
|
|
|
|
for (const bNode *node : tree.group_input_nodes()) {
|
|
|
|
|
const bNodeSocket &geometry_socket = node->output_socket(interface_i);
|
|
|
|
|
required_fields |= required_fields_by_geometry_socket[geometry_socket.index_in_tree()];
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
bits::foreach_1_index(required_fields, [&](const int field_source_index) {
|
|
|
|
|
const FieldSource &field_source = all_field_sources[field_source_index];
|
|
|
|
|
if (const auto *input_field = std::get_if<InputFieldSource>(&field_source.data)) {
|
|
|
|
|
tree_relations.eval_relations.append(
|
|
|
|
|
aal::EvalRelation{input_field->input_index, interface_i});
|
|
|
|
|
}
|
|
|
|
|
});
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
2023-06-14 13:56:57 +02:00
|
|
|
|
|
|
|
|
AnonymousAttributeInferencingResult result{std::move(all_field_sources),
|
|
|
|
|
std::move(all_geometry_sources),
|
|
|
|
|
std::move(propagated_fields_by_socket),
|
|
|
|
|
std::move(propagated_geometries_by_socket),
|
|
|
|
|
std::move(available_fields_by_geometry_socket),
|
|
|
|
|
std::move(required_fields_by_geometry_socket),
|
|
|
|
|
std::move(propagated_output_geometry_indices),
|
|
|
|
|
std::move(propagate_to_output_by_geometry_socket),
|
|
|
|
|
std::move(tree_relations)};
|
|
|
|
|
|
|
|
|
|
/* Print analysis result for debugging purposes. */
|
|
|
|
|
#if 0
|
|
|
|
|
bNodeTreeToDotOptionsForAnonymousAttributeInferencing options{result};
|
|
|
|
|
std::cout << "\n\n" << node_tree_to_dot(tree, options) << "\n\n";
|
|
|
|
|
#endif
|
|
|
|
|
return result;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool update_anonymous_attribute_relations(bNodeTree &tree)
|
|
|
|
|
{
|
|
|
|
|
tree.ensure_topology_cache();
|
|
|
|
|
|
2023-06-14 13:56:57 +02:00
|
|
|
if (tree.has_available_link_cycle()) {
|
2024-01-19 12:08:40 -05:00
|
|
|
const bool changed = bool(tree.runtime->anonymous_attribute_inferencing);
|
2023-06-14 13:56:57 +02:00
|
|
|
tree.runtime->anonymous_attribute_inferencing.reset();
|
|
|
|
|
return changed;
|
2023-01-05 14:05:30 +01:00
|
|
|
}
|
|
|
|
|
|
2023-11-27 10:55:39 +11:00
|
|
|
AnonymousAttributeInferencingResult result = analyze_anonymous_attribute_usages(tree);
|
2023-06-14 13:56:57 +02:00
|
|
|
|
|
|
|
|
const bool group_interface_changed =
|
|
|
|
|
!tree.runtime->anonymous_attribute_inferencing ||
|
|
|
|
|
tree.runtime->anonymous_attribute_inferencing->tree_relations != result.tree_relations;
|
|
|
|
|
|
|
|
|
|
tree.runtime->anonymous_attribute_inferencing =
|
|
|
|
|
std::make_unique<AnonymousAttributeInferencingResult>(std::move(result));
|
2023-01-05 14:05:30 +01:00
|
|
|
|
|
|
|
|
return group_interface_changed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace blender::bke::anonymous_attribute_inferencing
|