Geometry Nodes: Socket structure type and display changes
Implementation of #127106. This is just a visual representation of the field/single/grid status of sockets to make the workflow more intuitive. With a visual representation for volume grid sockets, volume features should be unblocked for further development. The structure type will also be used to distinguish list sockets in the interface. Group input nodes now have a "Structure Type" option instead of the existing "Single Value Only". Usually the auto option should be enough, but in some cases where the inferencing cannot (yet) make a clear determination, it can be helpful to choose a specific type. The new visualization and the group input structure type option are hidden behind a new experimental option for now. Pull Request: https://projects.blender.org/blender/blender/pulls/134811
This commit is contained in:
@@ -2848,6 +2848,7 @@ class USERPREF_PT_experimental_new_features(ExperimentalPanel, Panel):
|
||||
({"property": "use_new_volume_nodes"}, ("blender/blender/issues/103248", "#103248")),
|
||||
({"property": "use_shader_node_previews"}, ("blender/blender/issues/110353", "#110353")),
|
||||
({"property": "use_bundle_and_closure_nodes"}, ("blender/blender/issues/134029", "#134029")),
|
||||
({"property": "use_socket_structure_type"}, ("blender/blender/issues/127106", "#127106")),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
|
||||
/* Blender file format version. */
|
||||
#define BLENDER_FILE_VERSION BLENDER_VERSION
|
||||
#define BLENDER_FILE_SUBVERSION 80
|
||||
#define BLENDER_FILE_SUBVERSION 81
|
||||
|
||||
/* Minimum Blender version that supports reading file written with the current
|
||||
* version. Older Blender versions will test this and cancel loading the file, showing a warning to
|
||||
|
||||
@@ -36,6 +36,7 @@ struct FieldInferencingInterface;
|
||||
struct GeometryNodesEvalDependencies;
|
||||
class NodeDeclaration;
|
||||
struct GeometryNodesLazyFunctionGraphInfo;
|
||||
struct StructureTypeInterface;
|
||||
namespace anonymous_attribute_lifetime {
|
||||
}
|
||||
namespace aal = anonymous_attribute_lifetime;
|
||||
@@ -175,6 +176,7 @@ class bNodeTreeRuntime : NonCopyable, NonMovable {
|
||||
/** Information about usage of anonymous attributes within the group. */
|
||||
std::unique_ptr<node_tree_reference_lifetimes::ReferenceLifetimesInfo> reference_lifetimes_info;
|
||||
std::unique_ptr<nodes::gizmos::TreeGizmoPropagation> gizmo_propagation;
|
||||
std::unique_ptr<nodes::StructureTypeInterface> structure_type_interface;
|
||||
|
||||
/**
|
||||
* A bool for each input socket (indexed by `index_in_all_inputs()`) that indicates whether this
|
||||
@@ -459,6 +461,10 @@ namespace node_field_inferencing {
|
||||
bool update_field_inferencing(const bNodeTree &tree);
|
||||
}
|
||||
|
||||
namespace node_structure_type_inferencing {
|
||||
bool update_structure_type_interface(bNodeTree &tree);
|
||||
}
|
||||
|
||||
} // namespace blender::bke
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
||||
@@ -239,6 +239,7 @@ set(SRC
|
||||
intern/node_tree_field_inferencing.cc
|
||||
intern/node_tree_interface.cc
|
||||
intern/node_tree_reference_lifetimes.cc
|
||||
intern/node_tree_structure_type_inferencing.cc
|
||||
intern/node_tree_update.cc
|
||||
intern/node_tree_zones.cc
|
||||
intern/object.cc
|
||||
|
||||
@@ -203,6 +203,10 @@ static void ntree_copy_data(Main * /*bmain*/,
|
||||
dst_runtime.field_inferencing_interface = std::make_unique<FieldInferencingInterface>(
|
||||
*ntree_src->runtime->field_inferencing_interface);
|
||||
}
|
||||
if (ntree_src->runtime->structure_type_interface) {
|
||||
dst_runtime.structure_type_interface = std::make_unique<nodes::StructureTypeInterface>(
|
||||
*ntree_src->runtime->structure_type_interface);
|
||||
}
|
||||
if (ntree_src->runtime->reference_lifetimes_info) {
|
||||
using namespace node_tree_reference_lifetimes;
|
||||
dst_runtime.reference_lifetimes_info = std::make_unique<ReferenceLifetimesInfo>(
|
||||
|
||||
@@ -525,7 +525,7 @@ static void determine_group_input_states(
|
||||
else if (is_layer_selection_field(*group_input)) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::Implicit;
|
||||
}
|
||||
else if (group_input->flag & NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY) {
|
||||
else if (group_input->structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE) {
|
||||
new_inferencing_interface.inputs[index] = InputSocketFieldType::None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,6 +578,12 @@ void item_write_struct(BlendWriter *writer, bNodeTreeInterfaceItem &item)
|
||||
{
|
||||
switch (NodeTreeInterfaceItemType(item.item_type)) {
|
||||
case NODE_INTERFACE_SOCKET: {
|
||||
/* Forward compatible writing of older single value only flag. To be removed in 5.0. */
|
||||
bNodeTreeInterfaceSocket &socket = get_item_as<bNodeTreeInterfaceSocket>(item);
|
||||
SET_FLAG_FROM_TEST(socket.flag,
|
||||
socket.structure_type == NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE,
|
||||
NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY_LEGACY);
|
||||
|
||||
BLO_write_struct(writer, bNodeTreeInterfaceSocket, &item);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,729 @@
|
||||
/* SPDX-FileCopyrightText: 2025 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include "BLI_array_utils.hh"
|
||||
#include "BLI_stack.hh"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_node_legacy_types.hh"
|
||||
#include "BKE_node_runtime.hh"
|
||||
|
||||
#include "DNA_node_tree_interface_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
|
||||
#include "NOD_node_declaration.hh"
|
||||
|
||||
namespace blender::bke::node_structure_type_inferencing {
|
||||
|
||||
using nodes::StructureType;
|
||||
namespace aal = nodes::anonymous_attribute_lifetime;
|
||||
|
||||
static nodes::StructureTypeInterface calc_node_interface(const bNode &node)
|
||||
{
|
||||
const Span<const bNodeSocket *> input_sockets = node.input_sockets();
|
||||
const Span<const bNodeSocket *> output_sockets = node.output_sockets();
|
||||
|
||||
nodes::StructureTypeInterface node_interface;
|
||||
node_interface.inputs.reinitialize(input_sockets.size());
|
||||
node_interface.outputs.reinitialize(output_sockets.size());
|
||||
|
||||
if (node.is_undefined()) {
|
||||
node_interface.inputs.fill(StructureType::Dynamic);
|
||||
node_interface.outputs.fill(
|
||||
nodes::StructureTypeInterface::OutputDependency{StructureType::Dynamic});
|
||||
return node_interface;
|
||||
}
|
||||
if (node.is_reroute()) {
|
||||
node_interface.inputs.first() = StructureType::Dynamic;
|
||||
node_interface.outputs.first() = {StructureType::Dynamic, {0}};
|
||||
return node_interface;
|
||||
}
|
||||
|
||||
for (const int i : input_sockets.index_range()) {
|
||||
const nodes::SocketDeclaration &decl = *input_sockets[i]->runtime->declaration;
|
||||
node_interface.inputs[i] = decl.structure_type;
|
||||
}
|
||||
|
||||
for (const int output : output_sockets.index_range()) {
|
||||
const nodes::SocketDeclaration &decl = *output_sockets[output]->runtime->declaration;
|
||||
nodes::StructureTypeInterface::OutputDependency &dependency = node_interface.outputs[output];
|
||||
dependency.type = decl.structure_type;
|
||||
if (dependency.type != StructureType::Dynamic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Currently the input sockets that influence the field status of an output are the same as the
|
||||
* sockets that influence its structure type. Reuse that for the propagation of structure type
|
||||
* until there is a more generic format of intra-node dependencies. */
|
||||
switch (decl.output_field_dependency.field_type()) {
|
||||
case nodes::OutputSocketFieldType::None:
|
||||
break;
|
||||
case nodes::OutputSocketFieldType::FieldSource:
|
||||
break;
|
||||
case nodes::OutputSocketFieldType::DependentField:
|
||||
dependency.linked_inputs.reinitialize(input_sockets.size());
|
||||
array_utils::fill_index_range(dependency.linked_inputs.as_mutable_span());
|
||||
break;
|
||||
case nodes::OutputSocketFieldType::PartiallyDependent:
|
||||
dependency.linked_inputs = decl.output_field_dependency.linked_input_indices();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return node_interface;
|
||||
}
|
||||
|
||||
static Array<nodes::StructureTypeInterface> calc_node_interfaces(const bNodeTree &tree)
|
||||
{
|
||||
const Span<const bNode *> nodes = tree.all_nodes();
|
||||
Array<nodes::StructureTypeInterface> interfaces(nodes.size());
|
||||
for (const int i : nodes.index_range()) {
|
||||
interfaces[i] = calc_node_interface(*nodes[i]);
|
||||
}
|
||||
return interfaces;
|
||||
}
|
||||
|
||||
enum class DataRequirement : int8_t { None, Field, Single, Grid, Invalid };
|
||||
|
||||
static DataRequirement merge(const DataRequirement a, const DataRequirement b)
|
||||
{
|
||||
if (a == b) {
|
||||
return a;
|
||||
}
|
||||
if (a == DataRequirement::None) {
|
||||
return b;
|
||||
}
|
||||
if (b == DataRequirement::None) {
|
||||
return a;
|
||||
}
|
||||
if ((a == DataRequirement::Field && b == DataRequirement::Single) ||
|
||||
(a == DataRequirement::Single && b == DataRequirement::Field))
|
||||
{
|
||||
/* Single beats field, becasuse fields can accept single values too. */
|
||||
return DataRequirement::Single;
|
||||
}
|
||||
return DataRequirement::Invalid;
|
||||
}
|
||||
|
||||
static void init_input_requirements(const bNodeTree &tree,
|
||||
MutableSpan<DataRequirement> input_requirements)
|
||||
{
|
||||
const Span<const bNodeSocket *> input_sockets = tree.all_input_sockets();
|
||||
for (const int i : input_sockets.index_range()) {
|
||||
const bNodeSocket &socket = *input_sockets[i];
|
||||
const nodes::SocketDeclaration *declaration = socket.runtime->declaration;
|
||||
if (!declaration) {
|
||||
input_requirements[i] = DataRequirement::None;
|
||||
continue;
|
||||
}
|
||||
switch (declaration->structure_type) {
|
||||
case StructureType::Dynamic: {
|
||||
input_requirements[i] = DataRequirement::None;
|
||||
break;
|
||||
}
|
||||
case StructureType::Single: {
|
||||
input_requirements[i] = DataRequirement::Single;
|
||||
break;
|
||||
}
|
||||
case StructureType::Grid: {
|
||||
input_requirements[i] = DataRequirement::Grid;
|
||||
break;
|
||||
}
|
||||
case StructureType::Field: {
|
||||
input_requirements[i] = DataRequirement::Field;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static DataRequirement calc_output_socket_requirement(
|
||||
const bNodeSocket &output_socket, const Span<DataRequirement> input_requirements)
|
||||
{
|
||||
DataRequirement requirement = DataRequirement::None;
|
||||
if (!output_socket.is_available()) {
|
||||
return requirement;
|
||||
}
|
||||
for (const bNodeSocket *socket : output_socket.directly_linked_sockets()) {
|
||||
if (!socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
requirement = merge(requirement, input_requirements[socket->index_in_all_inputs()]);
|
||||
}
|
||||
return requirement;
|
||||
}
|
||||
|
||||
static void store_group_input_structure_types(const bNodeTree &tree,
|
||||
const Span<DataRequirement> input_requirements,
|
||||
nodes::StructureTypeInterface &derived_interface)
|
||||
{
|
||||
/* Merge usages from all group input nodes. */
|
||||
Array<DataRequirement> interface_requirements(tree.interface_inputs().size(),
|
||||
DataRequirement::None);
|
||||
for (const bNode *node : tree.group_input_nodes()) {
|
||||
const Span<const bNodeSocket *> output_sockets = node->output_sockets();
|
||||
for (const int i : output_sockets.index_range().drop_back(1)) {
|
||||
const bNodeSocket &output = *output_sockets[i];
|
||||
interface_requirements[i] = merge(
|
||||
interface_requirements[i], calc_output_socket_requirement(output, input_requirements));
|
||||
}
|
||||
}
|
||||
|
||||
/* Build derived interface structure types from group input nodes. */
|
||||
for (const int i : tree.interface_inputs().index_range()) {
|
||||
const bNodeTreeInterfaceSocket &io_socket = *tree.interface_inputs()[i];
|
||||
if (io_socket.structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) {
|
||||
derived_interface.inputs[i] = StructureType(io_socket.structure_type);
|
||||
continue;
|
||||
}
|
||||
|
||||
const DataRequirement requirement = interface_requirements[i];
|
||||
switch (requirement) {
|
||||
case DataRequirement::None:
|
||||
derived_interface.inputs[i] = StructureType::Dynamic;
|
||||
break;
|
||||
case DataRequirement::Field:
|
||||
derived_interface.inputs[i] = StructureType::Field;
|
||||
break;
|
||||
case DataRequirement::Single:
|
||||
derived_interface.inputs[i] = StructureType::Single;
|
||||
break;
|
||||
case DataRequirement::Grid:
|
||||
derived_interface.inputs[i] = StructureType::Grid;
|
||||
break;
|
||||
case DataRequirement::Invalid:
|
||||
derived_interface.inputs[i] = StructureType::Dynamic;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class ZoneInOutChange {
|
||||
None = 0,
|
||||
In = (1 << 1),
|
||||
Out = (1 << 2),
|
||||
};
|
||||
ENUM_OPERATORS(ZoneInOutChange, ZoneInOutChange::Out);
|
||||
|
||||
static ZoneInOutChange simulation_zone_requirements_propagate(
|
||||
const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
MutableSpan<DataRequirement> input_requirements)
|
||||
{
|
||||
ZoneInOutChange change = ZoneInOutChange::None;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
/* First input node output is Delta Time which does not appear in the output node outputs. */
|
||||
const bNodeSocket &socket_input = input_node.input_socket(i);
|
||||
const bNodeSocket &socket_output = output_node.output_socket(i);
|
||||
const DataRequirement new_value = merge(
|
||||
input_requirements[socket_input.index_in_all_inputs()],
|
||||
calc_output_socket_requirement(socket_output, input_requirements));
|
||||
if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
|
||||
input_requirements[socket_input.index_in_all_inputs()] = new_value;
|
||||
change |= ZoneInOutChange::In;
|
||||
}
|
||||
if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
|
||||
input_requirements[socket_input.index_in_all_inputs()] = new_value;
|
||||
change |= ZoneInOutChange::In;
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static ZoneInOutChange repeat_zone_requirements_propagate(
|
||||
const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
MutableSpan<DataRequirement> input_requirements)
|
||||
{
|
||||
ZoneInOutChange change = ZoneInOutChange::None;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
const bNodeSocket &socket_input = input_node.input_socket(i + 1);
|
||||
const bNodeSocket &socket_output = output_node.output_socket(i);
|
||||
const DataRequirement new_value = merge(
|
||||
input_requirements[socket_input.index_in_all_inputs()],
|
||||
calc_output_socket_requirement(socket_output, input_requirements));
|
||||
if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
|
||||
input_requirements[socket_input.index_in_all_inputs()] = new_value;
|
||||
change |= ZoneInOutChange::In;
|
||||
}
|
||||
if (input_requirements[socket_input.index_in_all_inputs()] != new_value) {
|
||||
input_requirements[socket_input.index_in_all_inputs()] = new_value;
|
||||
change |= ZoneInOutChange::In;
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static bool propagate_zone_data_requirements(const bNodeTree &tree,
|
||||
const bNode &node,
|
||||
MutableSpan<DataRequirement> input_requirements)
|
||||
{
|
||||
/* Sync field state between zone nodes and schedule another pass if necessary. */
|
||||
switch (node.type_legacy) {
|
||||
case GEO_NODE_SIMULATION_INPUT: {
|
||||
const auto &data = *static_cast<const NodeGeometrySimulationInput *>(node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const ZoneInOutChange change = simulation_zone_requirements_propagate(
|
||||
node, *output_node, input_requirements);
|
||||
if ((change & ZoneInOutChange::Out) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
|
||||
const auto &data = *static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const ZoneInOutChange change = simulation_zone_requirements_propagate(
|
||||
*input_node, node, input_requirements);
|
||||
if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GEO_NODE_REPEAT_INPUT: {
|
||||
const auto &data = *static_cast<const NodeGeometryRepeatInput *>(node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const ZoneInOutChange change = repeat_zone_requirements_propagate(
|
||||
node, *output_node, input_requirements);
|
||||
if ((change & ZoneInOutChange::Out) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GEO_NODE_REPEAT_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeRepeatInput")) {
|
||||
const auto &data = *static_cast<const NodeGeometryRepeatInput *>(input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const ZoneInOutChange change = repeat_zone_requirements_propagate(
|
||||
*input_node, node, input_requirements);
|
||||
if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void propagate_right_to_left(const bNodeTree &tree,
|
||||
const Span<nodes::StructureTypeInterface> node_interfaces,
|
||||
MutableSpan<DataRequirement> input_requirements)
|
||||
{
|
||||
while (true) {
|
||||
bool need_update = false;
|
||||
|
||||
for (const bNode *node : tree.toposort_right_to_left()) {
|
||||
const Span<const bNodeSocket *> input_sockets = node->input_sockets();
|
||||
const Span<const bNodeSocket *> output_sockets = node->output_sockets();
|
||||
const nodes::StructureTypeInterface &node_interface = node_interfaces[node->index()];
|
||||
|
||||
for (const int output : node_interface.outputs.index_range()) {
|
||||
const bNodeSocket &output_socket = *output_sockets[output];
|
||||
DataRequirement ouput_requirement = DataRequirement::None;
|
||||
for (const bNodeSocket *socket : output_socket.directly_linked_sockets()) {
|
||||
if (!socket->is_available()) {
|
||||
continue;
|
||||
}
|
||||
ouput_requirement = merge(ouput_requirement,
|
||||
input_requirements[socket->index_in_all_inputs()]);
|
||||
}
|
||||
|
||||
/* When a data requirement could be provided by multiple node inputs (i.e. only a single
|
||||
* node input involved in a math operation has to be a volume grid for the output to be a
|
||||
* grid), it's better to not propagate the data requirement than incorrectly saying that
|
||||
* all of the inputs have it. */
|
||||
Vector<int, 8> inputs_with_links;
|
||||
for (const int input : node_interface.outputs[output].linked_inputs) {
|
||||
const bNodeSocket &input_socket = *input_sockets[input];
|
||||
if (input_socket.is_directly_linked()) {
|
||||
inputs_with_links.append(input_socket.index_in_all_inputs());
|
||||
}
|
||||
}
|
||||
if (inputs_with_links.size() == 1) {
|
||||
input_requirements[inputs_with_links.first()] = ouput_requirement;
|
||||
}
|
||||
else {
|
||||
for (const int input : inputs_with_links) {
|
||||
input_requirements[input] = DataRequirement::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Find reverse dependencies and resolve conflicts, which may require another pass. */
|
||||
if (propagate_zone_data_requirements(tree, *node, input_requirements)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static StructureType left_to_right_merge(const StructureType a, const StructureType b)
|
||||
{
|
||||
if (a == b) {
|
||||
return a;
|
||||
}
|
||||
if ((a == StructureType::Dynamic && b == StructureType::Single) ||
|
||||
(a == StructureType::Single && b == StructureType::Dynamic))
|
||||
{
|
||||
return StructureType::Dynamic;
|
||||
}
|
||||
if ((a == StructureType::Dynamic && b == StructureType::Field) ||
|
||||
(a == StructureType::Field && b == StructureType::Dynamic))
|
||||
{
|
||||
return StructureType::Field;
|
||||
}
|
||||
if ((a == StructureType::Dynamic && b == StructureType::Grid) ||
|
||||
(a == StructureType::Grid && b == StructureType::Dynamic))
|
||||
{
|
||||
return StructureType::Grid;
|
||||
}
|
||||
if ((a == StructureType::Field && b == StructureType::Grid) ||
|
||||
(a == StructureType::Grid && b == StructureType::Field))
|
||||
{
|
||||
return StructureType::Grid;
|
||||
}
|
||||
if ((a == StructureType::Single && b == StructureType::Field) ||
|
||||
(a == StructureType::Field && b == StructureType::Single))
|
||||
{
|
||||
return StructureType::Field;
|
||||
}
|
||||
if ((a == StructureType::Single && b == StructureType::Grid) ||
|
||||
(a == StructureType::Grid && b == StructureType::Single))
|
||||
{
|
||||
return StructureType::Grid;
|
||||
}
|
||||
/* Invalid combination. */
|
||||
return a;
|
||||
}
|
||||
|
||||
static ZoneInOutChange simulation_zone_status_propagate(const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
MutableSpan<StructureType> structure_types)
|
||||
{
|
||||
ZoneInOutChange change = ZoneInOutChange::None;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
/* First input node output is Delta Time which does not appear in the output node outputs. */
|
||||
const bNodeSocket &input = input_node.output_socket(i + 1);
|
||||
const bNodeSocket &output = output_node.output_socket(i);
|
||||
const StructureType new_value = left_to_right_merge(structure_types[input.index_in_tree()],
|
||||
structure_types[output.index_in_tree()]);
|
||||
if (structure_types[input.index_in_tree()] != new_value) {
|
||||
structure_types[input.index_in_tree()] = new_value;
|
||||
change |= ZoneInOutChange::In;
|
||||
}
|
||||
if (structure_types[output.index_in_tree()] != new_value) {
|
||||
structure_types[output.index_in_tree()] = new_value;
|
||||
change |= ZoneInOutChange::Out;
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static ZoneInOutChange repeat_zone_status_propagate(const bNode &input_node,
|
||||
const bNode &output_node,
|
||||
MutableSpan<StructureType> structure_types)
|
||||
{
|
||||
ZoneInOutChange change = ZoneInOutChange::None;
|
||||
for (const int i : output_node.output_sockets().index_range()) {
|
||||
const bNodeSocket &input = input_node.output_socket(i + 1);
|
||||
const bNodeSocket &output = output_node.output_socket(i);
|
||||
const StructureType new_value = left_to_right_merge(structure_types[input.index_in_tree()],
|
||||
structure_types[output.index_in_tree()]);
|
||||
if (structure_types[input.index_in_tree()] != new_value) {
|
||||
structure_types[input.index_in_tree()] = new_value;
|
||||
change |= ZoneInOutChange::In;
|
||||
}
|
||||
if (structure_types[output.index_in_tree()] != new_value) {
|
||||
structure_types[output.index_in_tree()] = new_value;
|
||||
change |= ZoneInOutChange::Out;
|
||||
}
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
static bool propagate_zone_status(const bNodeTree &tree,
|
||||
const bNode &node,
|
||||
MutableSpan<StructureType> structure_types)
|
||||
{
|
||||
/* Sync field state between zone nodes and schedule another pass if necessary. */
|
||||
switch (node.type_legacy) {
|
||||
case GEO_NODE_SIMULATION_INPUT: {
|
||||
const auto &data = *static_cast<const NodeGeometrySimulationInput *>(node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const ZoneInOutChange change = simulation_zone_status_propagate(
|
||||
node, *output_node, structure_types);
|
||||
if ((change & ZoneInOutChange::Out) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GEO_NODE_SIMULATION_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeSimulationInput")) {
|
||||
const auto &data = *static_cast<const NodeGeometrySimulationInput *>(input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const ZoneInOutChange change = simulation_zone_status_propagate(
|
||||
*input_node, node, structure_types);
|
||||
if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GEO_NODE_REPEAT_INPUT: {
|
||||
const auto &data = *static_cast<const NodeGeometryRepeatInput *>(node.storage);
|
||||
if (const bNode *output_node = tree.node_by_id(data.output_node_id)) {
|
||||
const ZoneInOutChange change = repeat_zone_status_propagate(
|
||||
node, *output_node, structure_types);
|
||||
if ((change & ZoneInOutChange::Out) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
case GEO_NODE_REPEAT_OUTPUT: {
|
||||
for (const bNode *input_node : tree.nodes_by_type("GeometryNodeRepeatInput")) {
|
||||
const auto &data = *static_cast<const NodeGeometryRepeatInput *>(input_node->storage);
|
||||
if (node.identifier == data.output_node_id) {
|
||||
const ZoneInOutChange change = repeat_zone_status_propagate(
|
||||
*input_node, node, structure_types);
|
||||
if ((change & ZoneInOutChange::In) != ZoneInOutChange::None) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static StructureType get_unconnected_input_structure_type(
|
||||
const nodes::SocketDeclaration &declaration)
|
||||
{
|
||||
if (declaration.input_field_type == nodes::InputSocketFieldType::Implicit) {
|
||||
return StructureType::Field;
|
||||
}
|
||||
return StructureType::Single;
|
||||
}
|
||||
|
||||
static void propagate_left_to_right(const bNodeTree &tree,
|
||||
const Span<nodes::StructureTypeInterface> node_interfaces,
|
||||
const Span<StructureType> group_input_structure_types,
|
||||
MutableSpan<StructureType> structure_types)
|
||||
{
|
||||
for (const bNodeSocket *input : tree.all_input_sockets()) {
|
||||
if (input->owner_node().is_undefined()) {
|
||||
continue;
|
||||
}
|
||||
if (!input->is_directly_linked()) {
|
||||
const nodes::SocketDeclaration &declaration = *input->runtime->declaration;
|
||||
structure_types[input->index_in_tree()] = get_unconnected_input_structure_type(declaration);
|
||||
}
|
||||
}
|
||||
while (true) {
|
||||
bool need_update = false;
|
||||
for (const bNode *node : tree.toposort_left_to_right()) {
|
||||
if (node->is_undefined()) {
|
||||
continue;
|
||||
}
|
||||
const Span<const bNodeSocket *> input_sockets = node->input_sockets();
|
||||
const Span<const bNodeSocket *> output_sockets = node->output_sockets();
|
||||
if (node->is_group_input()) {
|
||||
for (const int i : output_sockets.index_range().drop_back(1)) {
|
||||
structure_types[output_sockets[i]->index_in_tree()] = group_input_structure_types[i];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const bNodeSocket *input : input_sockets) {
|
||||
if (!input->is_available()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
std::optional<StructureType> input_type;
|
||||
for (const bNodeLink *link : input->directly_linked_links()) {
|
||||
if (!link->is_used()) {
|
||||
continue;
|
||||
}
|
||||
const StructureType new_type = structure_types[link->fromsock->index_in_tree()];
|
||||
if (input_type) {
|
||||
input_type = left_to_right_merge(*input_type, new_type);
|
||||
}
|
||||
else {
|
||||
input_type = new_type;
|
||||
}
|
||||
}
|
||||
if (input_type) {
|
||||
structure_types[input->index_in_tree()] = *input_type;
|
||||
}
|
||||
}
|
||||
|
||||
const nodes::StructureTypeInterface &node_interface = node_interfaces[node->index()];
|
||||
|
||||
for (const int output_index : node_interface.outputs.index_range()) {
|
||||
const bNodeSocket &output = *output_sockets[output_index];
|
||||
if (!output.is_available()) {
|
||||
continue;
|
||||
}
|
||||
const nodes::SocketDeclaration &declaration = *output.runtime->declaration;
|
||||
|
||||
std::optional<StructureType> output_type;
|
||||
for (const int input_index : node_interface.outputs[output_index].linked_inputs) {
|
||||
const bNodeSocket &input = node->input_socket(input_index);
|
||||
if (!input.is_available()) {
|
||||
continue;
|
||||
}
|
||||
const StructureType new_type = structure_types[input.index_in_tree()];
|
||||
if (output_type) {
|
||||
output_type = left_to_right_merge(*output_type, new_type);
|
||||
}
|
||||
else {
|
||||
output_type = new_type;
|
||||
}
|
||||
}
|
||||
structure_types[output.index_in_tree()] = output_type.value_or(declaration.structure_type);
|
||||
}
|
||||
|
||||
if (propagate_zone_status(tree, *node, structure_types)) {
|
||||
need_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!need_update) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Vector<int> find_dynamic_output_linked_inputs(
|
||||
const bNodeSocket &group_output, const Span<nodes::StructureTypeInterface> interface_by_node)
|
||||
{
|
||||
/* Use a Set instead of an array indexed by socket because we may only look at a few sockets. */
|
||||
Set<const bNodeSocket *> handled_sockets;
|
||||
Stack<const bNodeSocket *> sockets_to_check;
|
||||
|
||||
handled_sockets.add(&group_output);
|
||||
sockets_to_check.push(&group_output);
|
||||
|
||||
Vector<int> group_inputs;
|
||||
|
||||
while (!sockets_to_check.is_empty()) {
|
||||
const bNodeSocket *input_socket = sockets_to_check.pop();
|
||||
if (!input_socket->is_directly_linked()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const bNodeSocket *origin_socket : input_socket->directly_linked_sockets()) {
|
||||
const bNode &origin_node = origin_socket->owner_node();
|
||||
if (origin_node.is_group_input()) {
|
||||
group_inputs.append_non_duplicates(origin_socket->index());
|
||||
continue;
|
||||
}
|
||||
|
||||
const nodes::StructureTypeInterface &node_interface = interface_by_node[origin_node.index()];
|
||||
for (const int input_index : node_interface.outputs[origin_socket->index()].linked_inputs) {
|
||||
const bNodeSocket &input = origin_node.input_socket(input_index);
|
||||
if (!input.is_available()) {
|
||||
continue;
|
||||
}
|
||||
if (handled_sockets.add(&input)) {
|
||||
sockets_to_check.push(&input);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return group_inputs;
|
||||
}
|
||||
|
||||
static void store_group_output_structure_types(
|
||||
const bNodeTree &tree,
|
||||
const Span<nodes::StructureTypeInterface> interface_by_node,
|
||||
const Span<StructureType> structure_types,
|
||||
nodes::StructureTypeInterface &interface)
|
||||
{
|
||||
const bNode *group_output_node = tree.group_output_node();
|
||||
if (!group_output_node) {
|
||||
for (nodes::StructureTypeInterface::OutputDependency &output : interface.outputs) {
|
||||
output.type = StructureType::Dynamic;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const Span<const bNodeTreeInterfaceSocket *> interface_outputs = tree.interface_outputs();
|
||||
const Span<const bNodeSocket *> sockets = group_output_node->input_sockets().drop_back(1);
|
||||
for (const int i : sockets.index_range()) {
|
||||
if (interface_outputs[i]->structure_type != NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO) {
|
||||
interface.outputs[i] = {StructureType(interface_outputs[i]->structure_type), {}};
|
||||
continue;
|
||||
}
|
||||
/* Update derived interface output structure types from output node socket usages. */
|
||||
interface.outputs[i].type = structure_types[sockets[i]->index_in_tree()];
|
||||
if (interface.outputs[i].type == StructureType::Dynamic) {
|
||||
const Vector<int> linked_inputs = find_dynamic_output_linked_inputs(*sockets[i],
|
||||
interface_by_node);
|
||||
interface.outputs[i] = {StructureType::Dynamic, linked_inputs.as_span()};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::unique_ptr<nodes::StructureTypeInterface> calc_structure_type_interface(
|
||||
const bNodeTree &tree)
|
||||
{
|
||||
tree.ensure_topology_cache();
|
||||
tree.ensure_interface_cache();
|
||||
|
||||
auto derived_interface = std::make_unique<nodes::StructureTypeInterface>();
|
||||
derived_interface->inputs.reinitialize(tree.interface_inputs().size());
|
||||
derived_interface->outputs.reinitialize(tree.interface_outputs().size());
|
||||
if (tree.has_available_link_cycle()) {
|
||||
derived_interface->inputs.fill(StructureType::Dynamic);
|
||||
derived_interface->outputs.fill({StructureType::Dynamic, {}});
|
||||
return derived_interface;
|
||||
}
|
||||
|
||||
Array<nodes::StructureTypeInterface> node_interfaces = calc_node_interfaces(tree);
|
||||
|
||||
Array<DataRequirement> data_requirements(tree.all_input_sockets().size());
|
||||
Array<StructureType> structure_types(tree.all_sockets().size(), StructureType::Dynamic);
|
||||
|
||||
init_input_requirements(tree, data_requirements);
|
||||
propagate_right_to_left(tree, node_interfaces, data_requirements);
|
||||
store_group_input_structure_types(tree, data_requirements, *derived_interface);
|
||||
propagate_left_to_right(tree, node_interfaces, derived_interface->inputs, structure_types);
|
||||
store_group_output_structure_types(tree, node_interfaces, structure_types, *derived_interface);
|
||||
|
||||
return derived_interface;
|
||||
}
|
||||
|
||||
bool update_structure_type_interface(bNodeTree &tree)
|
||||
{
|
||||
std::unique_ptr<nodes::StructureTypeInterface> new_interface = calc_structure_type_interface(
|
||||
tree);
|
||||
if (tree.runtime->structure_type_interface &&
|
||||
*tree.runtime->structure_type_interface == *new_interface)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
tree.runtime->structure_type_interface = std::move(new_interface);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace blender::bke::node_structure_type_inferencing
|
||||
@@ -510,6 +510,9 @@ class NodeTreeMainUpdater {
|
||||
if (node_field_inferencing::update_field_inferencing(ntree)) {
|
||||
result.interface_changed = true;
|
||||
}
|
||||
if (node_structure_type_inferencing::update_structure_type_interface(ntree)) {
|
||||
result.interface_changed = true;
|
||||
}
|
||||
this->update_from_field_inference(ntree);
|
||||
if (node_tree_reference_lifetimes::analyse_reference_lifetimes(ntree)) {
|
||||
result.interface_changed = true;
|
||||
@@ -860,21 +863,137 @@ class NodeTreeMainUpdater {
|
||||
}
|
||||
}
|
||||
|
||||
static bool socket_type_always_single(const SocketDeclaration &decl)
|
||||
{
|
||||
switch (decl.socket_type) {
|
||||
case SOCK_OBJECT:
|
||||
case SOCK_IMAGE:
|
||||
case SOCK_GEOMETRY:
|
||||
case SOCK_COLLECTION:
|
||||
case SOCK_TEXTURE:
|
||||
case SOCK_MATERIAL:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static int get_input_socket_shape(const SocketDeclaration &decl,
|
||||
const StructureType structure_type)
|
||||
{
|
||||
if (decl.identifier == "__extend__") {
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
}
|
||||
if (socket_type_always_single(decl)) {
|
||||
return SOCK_DISPLAY_SHAPE_LINE;
|
||||
}
|
||||
switch (structure_type) {
|
||||
case StructureType::Single:
|
||||
return SOCK_DISPLAY_SHAPE_LINE;
|
||||
case StructureType::Dynamic:
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
case StructureType::Field:
|
||||
return SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
case StructureType::Grid:
|
||||
return SOCK_DISPLAY_SHAPE_VOLUME_GRID;
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
}
|
||||
|
||||
static int get_output_socket_shape(const SocketDeclaration &decl,
|
||||
const bke::FieldSocketState field_state,
|
||||
const StructureType structure_type)
|
||||
{
|
||||
if (decl.identifier == "__extend__") {
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
}
|
||||
if (socket_type_always_single(decl)) {
|
||||
return SOCK_DISPLAY_SHAPE_LINE;
|
||||
}
|
||||
switch (structure_type) {
|
||||
case StructureType::Single: {
|
||||
return SOCK_DISPLAY_SHAPE_LINE;
|
||||
}
|
||||
case StructureType::Dynamic: {
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
}
|
||||
case StructureType::Field: {
|
||||
switch (field_state) {
|
||||
case bke::FieldSocketState::RequiresSingle:
|
||||
return SOCK_DISPLAY_SHAPE_LINE;
|
||||
case bke::FieldSocketState::CanBeField:
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
case bke::FieldSocketState::IsField:
|
||||
return SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case StructureType::Grid: {
|
||||
return SOCK_DISPLAY_SHAPE_VOLUME_GRID;
|
||||
}
|
||||
}
|
||||
BLI_assert_unreachable();
|
||||
return SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
}
|
||||
|
||||
void update_socket_shapes(bNodeTree &ntree)
|
||||
{
|
||||
ntree.ensure_topology_cache();
|
||||
const Span<bke::FieldSocketState> field_states = ntree.runtime->field_states;
|
||||
for (bNodeSocket *socket : ntree.all_sockets()) {
|
||||
switch (field_states[socket->index_in_tree()]) {
|
||||
case bke::FieldSocketState::RequiresSingle:
|
||||
socket->display_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
break;
|
||||
case bke::FieldSocketState::CanBeField:
|
||||
socket->display_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT;
|
||||
break;
|
||||
case bke::FieldSocketState::IsField:
|
||||
socket->display_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
break;
|
||||
if (U.experimental.use_socket_structure_type) {
|
||||
const nodes::StructureTypeInterface &node_interface =
|
||||
*ntree.runtime->structure_type_interface;
|
||||
const Span<bke::FieldSocketState> field_states = ntree.runtime->field_states;
|
||||
for (bNode *node : ntree.all_nodes()) {
|
||||
if (node->is_undefined()) {
|
||||
continue;
|
||||
}
|
||||
if (node->is_group_input()) {
|
||||
const Span<bNodeSocket *> sockets = node->output_sockets();
|
||||
for (const int i : node_interface.inputs.index_range()) {
|
||||
sockets[i]->display_shape = get_output_socket_shape(
|
||||
*sockets[i]->runtime->declaration,
|
||||
field_states[sockets[i]->index_in_tree()],
|
||||
node_interface.inputs[i]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (node->is_group_output()) {
|
||||
const Span<bNodeSocket *> sockets = node->input_sockets();
|
||||
for (const int i : node_interface.outputs.index_range()) {
|
||||
sockets[i]->display_shape = get_output_socket_shape(
|
||||
*sockets[i]->runtime->declaration,
|
||||
field_states[sockets[i]->index_in_tree()],
|
||||
node_interface.outputs[i].type);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (bNodeSocket *socket : node->input_sockets()) {
|
||||
socket->display_shape = get_input_socket_shape(
|
||||
*socket->runtime->declaration, socket->runtime->declaration->structure_type);
|
||||
}
|
||||
for (bNodeSocket *socket : node->output_sockets()) {
|
||||
socket->display_shape = get_output_socket_shape(
|
||||
*socket->runtime->declaration,
|
||||
field_states[socket->index_in_tree()],
|
||||
socket->runtime->declaration->structure_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
const Span<bke::FieldSocketState> field_states = ntree.runtime->field_states;
|
||||
for (bNodeSocket *socket : ntree.all_sockets()) {
|
||||
switch (field_states[socket->index_in_tree()]) {
|
||||
case bke::FieldSocketState::RequiresSingle:
|
||||
socket->display_shape = SOCK_DISPLAY_SHAPE_CIRCLE;
|
||||
break;
|
||||
case bke::FieldSocketState::CanBeField:
|
||||
socket->display_shape = SOCK_DISPLAY_SHAPE_DIAMOND_DOT;
|
||||
break;
|
||||
case bke::FieldSocketState::IsField:
|
||||
socket->display_shape = SOCK_DISPLAY_SHAPE_DIAMOND;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5149,6 +5149,25 @@ static void version_convert_sculpt_planar_brushes(Main *bmain)
|
||||
}
|
||||
}
|
||||
|
||||
static void node_interface_single_value_to_structure_type(bNodeTreeInterfaceItem &item)
|
||||
{
|
||||
if (item.item_type == eNodeTreeInterfaceItemType::NODE_INTERFACE_SOCKET) {
|
||||
auto &socket = reinterpret_cast<bNodeTreeInterfaceSocket &>(item);
|
||||
if (socket.flag & NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY_LEGACY) {
|
||||
socket.structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE;
|
||||
}
|
||||
else {
|
||||
socket.structure_type = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO;
|
||||
}
|
||||
}
|
||||
else {
|
||||
auto &panel = reinterpret_cast<bNodeTreeInterfacePanel &>(item);
|
||||
for (bNodeTreeInterfaceItem *item : blender::Span(panel.items_array, panel.items_num)) {
|
||||
node_interface_single_value_to_structure_type(*item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void version_set_default_bone_drawtype(Main *bmain)
|
||||
{
|
||||
LISTBASE_FOREACH (bArmature *, arm, &bmain->armatures) {
|
||||
@@ -6067,6 +6086,14 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain)
|
||||
FOREACH_NODETREE_END;
|
||||
}
|
||||
|
||||
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 81)) {
|
||||
LISTBASE_FOREACH (bNodeTree *, ntree, &bmain->nodetrees) {
|
||||
if (ntree->type == NTREE_GEOMETRY) {
|
||||
node_interface_single_value_to_structure_type(ntree->tree_interface.root_panel.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Always run this versioning (keep at the bottom of the function). Meshes are written with the
|
||||
* legacy format which always needs to be converted to the new format on file load. To be moved
|
||||
* to a subversion check in 5.0. */
|
||||
|
||||
@@ -1476,12 +1476,8 @@ static void std_node_socket_interface_draw(ID *id,
|
||||
uiLayout *sub = &col->column(false);
|
||||
uiLayoutSetActive(sub, !is_layer_selection_field(*interface_socket));
|
||||
sub->prop(&ptr, "hide_in_modifier", DEFAULT_FLAGS, std::nullopt, ICON_NONE);
|
||||
if (nodes::socket_type_supports_fields(type)) {
|
||||
uiLayout *sub_sub = &col->column(false);
|
||||
uiLayoutSetActive(sub_sub,
|
||||
(interface_socket->default_input == NODE_DEFAULT_INPUT_VALUE) &&
|
||||
!is_layer_selection_field(*interface_socket));
|
||||
sub_sub->prop(&ptr, "force_non_field", DEFAULT_FLAGS, std::nullopt, ICON_NONE);
|
||||
if (nodes::socket_type_supports_fields(type) || nodes::socket_type_supports_grids(type)) {
|
||||
sub->prop(&ptr, "structure_type", DEFAULT_FLAGS, std::nullopt, ICON_NONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2167,6 +2167,24 @@ static std::string node_socket_get_tooltip(const SpaceNode *snode,
|
||||
if (std::optional<std::string> info = create_declaration_inspection_string(socket)) {
|
||||
inspection_strings.append(std::move(*info));
|
||||
}
|
||||
if (U.experimental.use_socket_structure_type) {
|
||||
if (socket.runtime->declaration) {
|
||||
switch (socket.runtime->declaration->structure_type) {
|
||||
case nodes::StructureType::Single:
|
||||
inspection_strings.append("(Single Value)");
|
||||
break;
|
||||
case nodes::StructureType::Dynamic:
|
||||
inspection_strings.append("(Dynamic Structure Type)");
|
||||
break;
|
||||
case nodes::StructureType::Field:
|
||||
inspection_strings.append("(Field)");
|
||||
break;
|
||||
case nodes::StructureType::Grid:
|
||||
inspection_strings.append("(Volume Grid)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::stringstream output;
|
||||
for (const std::string &info : inspection_strings) {
|
||||
|
||||
@@ -1180,6 +1180,7 @@ static void node_group_make_insert_selected(const bContext &C,
|
||||
|
||||
if (group.type == NTREE_GEOMETRY) {
|
||||
bke::node_field_inferencing::update_field_inferencing(group);
|
||||
bke::node_structure_type_inferencing::update_structure_type_interface(group);
|
||||
}
|
||||
nodes::update_node_declaration_and_sockets(ntree, *gnode);
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
|
||||
#define SOCK_DISPLAY_SHAPE_CIRCLE_DOT 3
|
||||
#define SOCK_DISPLAY_SHAPE_SQUARE_DOT 4
|
||||
#define SOCK_DISPLAY_SHAPE_DIAMOND_DOT 5
|
||||
#define SOCK_DISPLAY_SHAPE_LINE 6
|
||||
#define SOCK_DISPLAY_SHAPE_VOLUME_GRID 7
|
||||
|
||||
/* Calculates a squared distance field of a square. */
|
||||
float square_sdf(float2 absCo, float2 half_size)
|
||||
@@ -93,6 +95,22 @@ void main()
|
||||
dot_threshold = finalDotRadius;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_LINE: {
|
||||
float square_radius = square_radius - corner_rounding;
|
||||
distance_squared = square_sdf(co, float2(square_radius * 0.75, square_radius * 1.4));
|
||||
alpha_threshold = corner_rounding;
|
||||
break;
|
||||
}
|
||||
case SOCK_DISPLAY_SHAPE_VOLUME_GRID: {
|
||||
float size = 0.7;
|
||||
float2 uv = abs(absUV - size * 0.5) - size * 0.4;
|
||||
float radius_out = length(max(uv, 0.0));
|
||||
float radius_in = max(abs(uv).x, abs(uv).y) * -1.0;
|
||||
float radius = mix(radius_in, radius_out, radius_out > 0);
|
||||
distance_squared = max(-1.0, (radius - size * 0.15));
|
||||
alpha_threshold = -0.2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
float2 alpha_thresholds = calculate_thresholds(alpha_threshold);
|
||||
|
||||
@@ -61,7 +61,8 @@ typedef enum NodeTreeInterfaceSocketFlag {
|
||||
NODE_INTERFACE_SOCKET_HIDE_VALUE = 1 << 2,
|
||||
NODE_INTERFACE_SOCKET_HIDE_IN_MODIFIER = 1 << 3,
|
||||
NODE_INTERFACE_SOCKET_COMPACT = 1 << 4,
|
||||
NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY = 1 << 5,
|
||||
/* To be deprecated when structure types are moved out of experimental. */
|
||||
NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY_LEGACY = 1 << 5,
|
||||
NODE_INTERFACE_SOCKET_LAYER_SELECTION = 1 << 6,
|
||||
/* INSPECT is used by Connect to Output operator to ensure socket that exits from node group. */
|
||||
NODE_INTERFACE_SOCKET_INSPECT = 1 << 7,
|
||||
@@ -72,6 +73,26 @@ typedef enum NodeTreeInterfaceSocketFlag {
|
||||
} NodeTreeInterfaceSocketFlag;
|
||||
ENUM_OPERATORS(NodeTreeInterfaceSocketFlag, NODE_INTERFACE_SOCKET_PANEL_TOGGLE);
|
||||
|
||||
typedef enum NodeSocketInterfaceStructureType {
|
||||
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO = 0,
|
||||
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE = 1,
|
||||
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC = 2,
|
||||
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_FIELD = 3,
|
||||
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_GRID = 4,
|
||||
} NodeSocketInterfaceStructureType;
|
||||
|
||||
// TODO: Move out of DNA.
|
||||
#ifdef __cplusplus
|
||||
namespace blender::nodes {
|
||||
enum class StructureType : int8_t {
|
||||
Single = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE,
|
||||
Dynamic = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC,
|
||||
Field = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_FIELD,
|
||||
Grid = NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_GRID,
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
||||
typedef struct bNodeTreeInterfaceSocket {
|
||||
bNodeTreeInterfaceItem item;
|
||||
|
||||
@@ -96,6 +117,10 @@ typedef struct bNodeTreeInterfaceSocket {
|
||||
|
||||
struct IDProperty *properties;
|
||||
|
||||
/** #NodeSocketInterfaceStructureType. */
|
||||
int8_t structure_type;
|
||||
char _pad[7];
|
||||
|
||||
#ifdef __cplusplus
|
||||
bNodeSocketTypeHandle *socket_typeinfo() const;
|
||||
blender::ColorGeometry4f socket_color() const;
|
||||
|
||||
@@ -321,6 +321,8 @@ typedef enum eNodeSocketDisplayShape {
|
||||
SOCK_DISPLAY_SHAPE_CIRCLE_DOT = 3,
|
||||
SOCK_DISPLAY_SHAPE_SQUARE_DOT = 4,
|
||||
SOCK_DISPLAY_SHAPE_DIAMOND_DOT = 5,
|
||||
SOCK_DISPLAY_SHAPE_LINE = 6,
|
||||
SOCK_DISPLAY_SHAPE_VOLUME_GRID = 7,
|
||||
} eNodeSocketDisplayShape;
|
||||
|
||||
/** Socket side (input/output). */
|
||||
|
||||
@@ -227,7 +227,8 @@ typedef struct UserDef_Experimental {
|
||||
char use_new_volume_nodes;
|
||||
char use_shader_node_previews;
|
||||
char use_bundle_and_closure_nodes;
|
||||
char _pad[5];
|
||||
char use_socket_structure_type;
|
||||
char _pad[4];
|
||||
} UserDef_Experimental;
|
||||
|
||||
#define USER_EXPERIMENTAL_TEST(userdef, member) \
|
||||
|
||||
@@ -26,6 +26,26 @@ static const EnumPropertyItem node_tree_interface_socket_in_out_items[] = {
|
||||
{NODE_INTERFACE_SOCKET_OUTPUT, "OUTPUT", 0, "Output", "Generate a output node socket"},
|
||||
{0, nullptr, 0, nullptr, nullptr}};
|
||||
|
||||
static const EnumPropertyItem node_tree_interface_socket_structure_type_items[] = {
|
||||
{NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO,
|
||||
"AUTO",
|
||||
0,
|
||||
"Auto",
|
||||
"Automatically detect a good structure type based on how the socket is used"},
|
||||
{NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE,
|
||||
"SINGLE",
|
||||
0,
|
||||
"Single",
|
||||
"Socket expects a single value"},
|
||||
{NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC,
|
||||
"DYNAMIC",
|
||||
0,
|
||||
"Dynamic",
|
||||
"Socket can work with different kinds of structures"},
|
||||
{NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_FIELD, "FIELD", 0, "Field", "Socket expects a field"},
|
||||
{NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_GRID, "GRID", 0, "Grid", "Socket expects a grid"},
|
||||
{0, nullptr, 0, nullptr, nullptr}};
|
||||
|
||||
static const EnumPropertyItem node_default_input_items[] = {
|
||||
{NODE_DEFAULT_INPUT_VALUE, "VALUE", 0, "Default Value", "The node socket's default value"},
|
||||
{NODE_DEFAULT_INPUT_INDEX_FIELD, "INDEX", 0, "Index", "The index from the context"},
|
||||
@@ -76,6 +96,7 @@ static const EnumPropertyItem node_default_input_items[] = {
|
||||
# include "BLT_translation.hh"
|
||||
|
||||
# include "NOD_node_declaration.hh"
|
||||
# include "NOD_socket.hh"
|
||||
|
||||
# include "DNA_material_types.h"
|
||||
# include "ED_node.hh"
|
||||
@@ -444,6 +465,80 @@ static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_socket_type_itemf(
|
||||
ntree->typeinfo, rna_NodeTreeInterfaceSocket_socket_type_poll, r_free);
|
||||
}
|
||||
|
||||
/**
|
||||
* Also control the structure type when setting the "Is Single" status. To be removed when the
|
||||
* structure type feature is moved out of experimental.
|
||||
*/
|
||||
static void rna_NodeTreeInterfaceSocket_force_non_field_set(PointerRNA *ptr, const bool value)
|
||||
{
|
||||
bNodeTreeInterfaceSocket *socket = static_cast<bNodeTreeInterfaceSocket *>(ptr->data);
|
||||
SET_FLAG_FROM_TEST(socket->flag, value, NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY_LEGACY);
|
||||
socket->structure_type = value ? NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE :
|
||||
NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_structure_type_itemf(
|
||||
bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free)
|
||||
{
|
||||
const bNodeTree *ntree = reinterpret_cast<const bNodeTree *>(ptr->owner_id);
|
||||
const bNodeTreeInterfaceSocket *socket = static_cast<const bNodeTreeInterfaceSocket *>(
|
||||
ptr->data);
|
||||
if (!ntree) {
|
||||
return rna_enum_dummy_NULL_items;
|
||||
}
|
||||
|
||||
const bool is_geometry_nodes = ntree->type == NTREE_GEOMETRY;
|
||||
|
||||
const eNodeSocketDatatype socket_type = eNodeSocketDatatype(socket->socket_typeinfo()->type);
|
||||
const bool supports_fields = is_geometry_nodes &&
|
||||
blender::nodes::socket_type_supports_fields(socket_type);
|
||||
const bool supports_grids = is_geometry_nodes &&
|
||||
blender::nodes::socket_type_supports_grids(socket_type);
|
||||
|
||||
*r_free = true;
|
||||
EnumPropertyItem *items = nullptr;
|
||||
int items_count = 0;
|
||||
|
||||
for (const EnumPropertyItem *item = node_tree_interface_socket_structure_type_items;
|
||||
item->identifier;
|
||||
item++)
|
||||
{
|
||||
switch (NodeSocketInterfaceStructureType(item->value)) {
|
||||
case NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_SINGLE:
|
||||
case NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_AUTO: {
|
||||
RNA_enum_item_add(&items, &items_count, item);
|
||||
break;
|
||||
}
|
||||
case NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_DYNAMIC: {
|
||||
if (U.experimental.use_socket_structure_type) {
|
||||
if (supports_fields || supports_grids) {
|
||||
RNA_enum_item_add(&items, &items_count, item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_FIELD: {
|
||||
if (U.experimental.use_socket_structure_type) {
|
||||
if (supports_fields) {
|
||||
RNA_enum_item_add(&items, &items_count, item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NODE_INTERFACE_SOCKET_STRUCTURE_TYPE_GRID: {
|
||||
if (U.experimental.use_socket_structure_type) {
|
||||
if (supports_grids) {
|
||||
RNA_enum_item_add(&items, &items_count, item);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
RNA_enum_item_end(&items, &items_count);
|
||||
return items;
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *rna_NodeTreeInterfaceSocket_default_input_itemf(
|
||||
bContext * /*C*/, PointerRNA *ptr, PropertyRNA * /*prop*/, bool *r_free)
|
||||
{
|
||||
@@ -1055,10 +1150,14 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
|
||||
|
||||
prop = RNA_def_property(srna, "force_non_field", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_boolean_sdna(prop, nullptr, "flag", NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY);
|
||||
RNA_def_property_boolean_sdna(
|
||||
prop, nullptr, "flag", NODE_INTERFACE_SOCKET_SINGLE_VALUE_ONLY_LEGACY);
|
||||
RNA_def_property_boolean_funcs(prop, nullptr, "rna_NodeTreeInterfaceSocket_force_non_field_set");
|
||||
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Single Value", "Only allow single value inputs rather than fields");
|
||||
prop,
|
||||
"Single Value",
|
||||
"Only allow single value inputs rather than field.\nDeprecated. Will be remove in 5.0.");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
|
||||
|
||||
prop = RNA_def_property(srna, "is_inspect_output", PROP_BOOLEAN, PROP_NONE);
|
||||
@@ -1109,6 +1208,16 @@ static void rna_def_node_interface_socket(BlenderRNA *brna)
|
||||
"geometry nodes modifier");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
|
||||
|
||||
prop = RNA_def_property(srna, "structure_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, node_tree_interface_socket_structure_type_items);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Structure Type",
|
||||
"What kind of higher order types are expected to flow through this socket");
|
||||
RNA_def_property_enum_funcs(
|
||||
prop, nullptr, nullptr, "rna_NodeTreeInterfaceSocket_structure_type_itemf");
|
||||
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeTreeInterfaceItem_update");
|
||||
|
||||
prop = RNA_def_property(srna, "default_input", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(prop, node_default_input_items);
|
||||
RNA_def_property_ui_text(
|
||||
|
||||
@@ -7574,6 +7574,12 @@ static void rna_def_userdef_experimental(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(
|
||||
prop, "Bundle and Closure Nodes", "Enables bundle and closure nodes in Geometry Nodes");
|
||||
|
||||
prop = RNA_def_property(srna, "use_socket_structure_type", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
"Node Structure Types",
|
||||
"Enables new visualization of socket data compatibility in Geometry Nodes");
|
||||
|
||||
prop = RNA_def_property(srna, "use_extensions_debug", PROP_BOOLEAN, PROP_NONE);
|
||||
RNA_def_property_ui_text(
|
||||
prop,
|
||||
|
||||
@@ -909,6 +909,8 @@ static void check_property_socket_sync(const Object *ob,
|
||||
int geometry_socket_count = 0;
|
||||
|
||||
nmd->node_group->ensure_interface_cache();
|
||||
const Span<nodes::StructureType> input_structure_types =
|
||||
nmd->node_group->runtime->structure_type_interface->inputs;
|
||||
for (const int i : nmd->node_group->interface_inputs().index_range()) {
|
||||
const bNodeTreeInterfaceSocket *socket = nmd->node_group->interface_inputs()[i];
|
||||
const bke::bNodeSocketType *typeinfo = socket->socket_typeinfo();
|
||||
@@ -920,6 +922,9 @@ static void check_property_socket_sync(const Object *ob,
|
||||
if (i == 0 && type == SOCK_GEOMETRY) {
|
||||
continue;
|
||||
}
|
||||
if (input_structure_types[i] == nodes::StructureType::Grid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IDProperty *property = properties.lookup_key_default_as(socket->identifier, nullptr);
|
||||
if (property == nullptr) {
|
||||
|
||||
@@ -64,7 +64,9 @@ bool id_property_type_matches_socket(const bNodeTreeInterfaceSocket &socket,
|
||||
bool use_name_for_ids = false);
|
||||
|
||||
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
|
||||
const bNodeTreeInterfaceSocket &socket, bool use_name_for_ids);
|
||||
const bNodeTreeInterfaceSocket &socket,
|
||||
nodes::StructureType structure_type,
|
||||
bool use_name_for_ids);
|
||||
|
||||
bke::GeometrySet execute_geometry_nodes_on_geometry(const bNodeTree &btree,
|
||||
const PropertiesVectorSet &properties_set,
|
||||
|
||||
@@ -88,6 +88,20 @@ struct FieldInferencingInterface {
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_2(FieldInferencingInterface, inputs, outputs)
|
||||
};
|
||||
|
||||
struct StructureTypeInterface {
|
||||
struct OutputDependency {
|
||||
StructureType type;
|
||||
Array<int> linked_inputs;
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_2(OutputDependency, type, linked_inputs)
|
||||
};
|
||||
|
||||
Array<StructureType> inputs;
|
||||
Array<OutputDependency> outputs;
|
||||
|
||||
BLI_STRUCT_EQUALITY_OPERATORS_2(StructureTypeInterface, inputs, outputs)
|
||||
};
|
||||
|
||||
namespace anonymous_attribute_lifetime {
|
||||
|
||||
/**
|
||||
@@ -212,6 +226,8 @@ class SocketDeclaration : public ItemDeclaration {
|
||||
InputSocketFieldType input_field_type = InputSocketFieldType::None;
|
||||
OutputFieldDependency output_field_dependency;
|
||||
|
||||
StructureType structure_type = StructureType::Single;
|
||||
|
||||
private:
|
||||
CompositorInputRealizationMode compositor_realization_mode_ =
|
||||
CompositorInputRealizationMode::OperationDomain;
|
||||
@@ -422,6 +438,8 @@ class BaseSocketDeclarationBuilder {
|
||||
*/
|
||||
BaseSocketDeclarationBuilder &panel_toggle(bool value = true);
|
||||
|
||||
BaseSocketDeclarationBuilder &structure_type(StructureType structure_type);
|
||||
|
||||
BaseSocketDeclarationBuilder &is_layer_name(bool value = true);
|
||||
|
||||
/** Index in the list of inputs or outputs. */
|
||||
|
||||
@@ -51,8 +51,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
b.add_output(data_type, item.name, output_identifier).field_on_all().align_with_previous();
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Field);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Field)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
|
||||
@@ -80,8 +80,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Dynamic)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
|
||||
@@ -36,19 +36,19 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.min(0.0f)
|
||||
.max(1.0f)
|
||||
.subtype(PROP_FACTOR)
|
||||
.field_on_all()
|
||||
.supports_field()
|
||||
.make_available([](bNode &node) {
|
||||
node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_FACTOR;
|
||||
});
|
||||
auto &length = b.add_input<decl::Float>("Length")
|
||||
.min(0.0f)
|
||||
.subtype(PROP_DISTANCE)
|
||||
.field_on_all()
|
||||
.supports_field()
|
||||
.make_available([](bNode &node) {
|
||||
node_storage(node).mode = GEO_NODE_CURVE_SAMPLE_LENGTH;
|
||||
});
|
||||
auto &index =
|
||||
b.add_input<decl::Int>("Curve Index").field_on_all().make_available([](bNode &node) {
|
||||
b.add_input<decl::Int>("Curve Index").supports_field().make_available([](bNode &node) {
|
||||
node_storage(node).use_all_curves = false;
|
||||
});
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ enum class DistributeMode {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Grid").hide_value();
|
||||
b.add_input<decl::Float>("Grid").hide_value().structure_type(StructureType::Grid);
|
||||
auto &density = b.add_input<decl::Float>("Density")
|
||||
.default_value(1.0f)
|
||||
.min(0.0f)
|
||||
|
||||
@@ -158,8 +158,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
}
|
||||
}
|
||||
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Dynamic)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
|
||||
@@ -33,7 +33,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
return;
|
||||
}
|
||||
|
||||
b.add_output(eNodeSocketDatatype(node->custom1), "Grid");
|
||||
b.add_output(eNodeSocketDatatype(node->custom1), "Grid").structure_type(StructureType::Grid);
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace blender::nodes::node_geo_grid_to_mesh_cc {
|
||||
|
||||
static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
b.add_input<decl::Float>("Grid").hide_value();
|
||||
b.add_input<decl::Float>("Grid").hide_value().structure_type(StructureType::Grid);
|
||||
b.add_input<decl::Float>("Threshold")
|
||||
.default_value(0.1f)
|
||||
.description("Values larger than the threshold are inside the generated mesh");
|
||||
|
||||
@@ -95,7 +95,7 @@ static void node_declare(blender::nodes::NodeDeclarationBuilder &b)
|
||||
output.propagate_all();
|
||||
}
|
||||
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
}
|
||||
|
||||
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
|
||||
|
||||
@@ -29,7 +29,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.max(FLT_MAX)
|
||||
.subtype(PROP_DISTANCE)
|
||||
.description("Width of the gradient inside of the mesh");
|
||||
b.add_output<decl::Float>("Density Grid");
|
||||
b.add_output<decl::Float>("Density Grid").structure_type(StructureType::Grid);
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -25,7 +25,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.min(1)
|
||||
.max(100)
|
||||
.description("Width of the active voxel surface, in voxels");
|
||||
b.add_output<decl::Float>("SDF Grid");
|
||||
b.add_output<decl::Float>("SDF Grid").structure_type(StructureType::Grid);
|
||||
}
|
||||
|
||||
static void node_geo_exec(GeoNodeExecParams params)
|
||||
|
||||
@@ -20,7 +20,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
.subtype(PROP_DISTANCE)
|
||||
.field_on_all();
|
||||
b.add_input<decl::Float>("Voxel Size").default_value(0.3f).min(0.01f).subtype(PROP_DISTANCE);
|
||||
b.add_output<decl::Float>("SDF Grid");
|
||||
b.add_output<decl::Float>("SDF Grid").structure_type(StructureType::Grid);
|
||||
}
|
||||
|
||||
#ifdef WITH_OPENVDB
|
||||
|
||||
@@ -78,8 +78,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
const bNodeTree *tree = b.tree_or_null();
|
||||
if (node && tree) {
|
||||
const NodeGeometryRepeatInput &storage = node_storage(*node);
|
||||
const bNode *output_node = tree->node_by_id(storage.output_node_id);
|
||||
if (output_node) {
|
||||
if (const bNode *output_node = tree->node_by_id(storage.output_node_id)) {
|
||||
const auto &output_storage = *static_cast<const NodeGeometryRepeatOutput *>(
|
||||
output_node->storage);
|
||||
for (const int i : IndexRange(output_storage.items_num)) {
|
||||
@@ -98,8 +97,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Dynamic)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
@@ -177,8 +178,10 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
}
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Dynamic)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
|
||||
@@ -37,7 +37,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
}
|
||||
const eNodeSocketDatatype data_type = eNodeSocketDatatype(node->custom1);
|
||||
|
||||
b.add_input(data_type, "Grid").hide_value();
|
||||
b.add_input(data_type, "Grid").hide_value().structure_type(StructureType::Grid);
|
||||
b.add_input<decl::Vector>("Position").implicit_field(NODE_DEFAULT_INPUT_POSITION_FIELD);
|
||||
|
||||
b.add_output(data_type, "Value").dependent_field({1});
|
||||
|
||||
@@ -28,7 +28,7 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
}
|
||||
const eNodeSocketDatatype data_type = eNodeSocketDatatype(node->custom1);
|
||||
|
||||
b.add_input(data_type, "Grid").hide_value();
|
||||
b.add_input(data_type, "Grid").hide_value().structure_type(StructureType::Grid);
|
||||
b.add_input<decl::Int>("X").supports_field();
|
||||
b.add_input<decl::Int>("Y").supports_field();
|
||||
b.add_input<decl::Int>("Z").supports_field();
|
||||
|
||||
@@ -30,7 +30,8 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
{
|
||||
const bNode *node = b.node_or_null();
|
||||
|
||||
auto &first_grid = b.add_input<decl::Float>("Grid 1").hide_value();
|
||||
auto &first_grid = b.add_input<decl::Float>("Grid 1").hide_value().structure_type(
|
||||
StructureType::Grid);
|
||||
|
||||
if (node) {
|
||||
static const auto make_available = [](bNode &node) {
|
||||
@@ -42,16 +43,20 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
b.add_input<decl::Float>("Grid", "Grid 2")
|
||||
.hide_value()
|
||||
.multi_input()
|
||||
.make_available(make_available);
|
||||
.make_available(make_available)
|
||||
.structure_type(StructureType::Grid);
|
||||
break;
|
||||
case Operation::Difference:
|
||||
b.add_input<decl::Float>("Grid 2").hide_value().multi_input().make_available(
|
||||
make_available);
|
||||
b.add_input<decl::Float>("Grid 2")
|
||||
.hide_value()
|
||||
.multi_input()
|
||||
.make_available(make_available)
|
||||
.structure_type(StructureType::Grid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
b.add_output<decl::Float>("Grid").hide_value();
|
||||
b.add_output<decl::Float>("Grid").hide_value().structure_type(StructureType::Grid);
|
||||
|
||||
if (node) {
|
||||
switch (Operation(node->custom1)) {
|
||||
|
||||
@@ -436,12 +436,15 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
&node_tree->id, SimulationItemsAccessor::item_srna, &item, "name");
|
||||
auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
|
||||
if (socket_type_supports_fields(socket_type)) {
|
||||
input_decl.supports_field();
|
||||
/* If it's below a geometry input it may be a field evaluated on that geometry. */
|
||||
input_decl.supports_field().structure_type(StructureType::Dynamic);
|
||||
output_decl.dependent_field({input_decl.index()});
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Dynamic)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
@@ -796,12 +799,15 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
&tree->id, SimulationItemsAccessor::item_srna, &item, "name");
|
||||
auto &output_decl = b.add_output(socket_type, name, identifier).align_with_previous();
|
||||
if (socket_type_supports_fields(socket_type)) {
|
||||
input_decl.supports_field();
|
||||
/* If it's below a geometry input it may be a field evaluated on that geometry. */
|
||||
input_decl.supports_field().structure_type(StructureType::Dynamic);
|
||||
output_decl.dependent_field({input_decl.index()});
|
||||
}
|
||||
}
|
||||
b.add_input<decl::Extend>("", "__extend__");
|
||||
b.add_output<decl::Extend>("", "__extend__").align_with_previous();
|
||||
b.add_input<decl::Extend>("", "__extend__").structure_type(StructureType::Dynamic);
|
||||
b.add_output<decl::Extend>("", "__extend__")
|
||||
.structure_type(StructureType::Dynamic)
|
||||
.align_with_previous();
|
||||
}
|
||||
|
||||
static void node_init(bNodeTree * /*tree*/, bNode *node)
|
||||
|
||||
@@ -32,7 +32,9 @@ static void node_declare(NodeDeclarationBuilder &b)
|
||||
return;
|
||||
}
|
||||
|
||||
b.add_input(*bke::grid_type_to_socket_type(VolumeGridType(node->custom1)), "Grid").hide_value();
|
||||
b.add_input(*bke::grid_type_to_socket_type(VolumeGridType(node->custom1)), "Grid")
|
||||
.hide_value()
|
||||
.structure_type(StructureType::Grid);
|
||||
}
|
||||
|
||||
static void search_link_ops(GatherLinkSearchOpParams ¶ms)
|
||||
|
||||
@@ -109,8 +109,14 @@ static std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_name_or_va
|
||||
}
|
||||
|
||||
std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_create_from_socket(
|
||||
const bNodeTreeInterfaceSocket &socket, const bool use_name_for_ids)
|
||||
const bNodeTreeInterfaceSocket &socket,
|
||||
const nodes::StructureType structure_type,
|
||||
const bool use_name_for_ids)
|
||||
{
|
||||
if (structure_type == StructureType::Grid) {
|
||||
/* Grids currently aren't exposed as properties. */
|
||||
return nullptr;
|
||||
}
|
||||
const StringRefNull identifier = socket.identifier;
|
||||
const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
|
||||
const eNodeSocketDatatype type = typeinfo ? eNodeSocketDatatype(typeinfo->type) : SOCK_CUSTOM;
|
||||
@@ -970,16 +976,18 @@ void update_input_properties_from_node_tree(const bNodeTree &tree,
|
||||
{
|
||||
tree.ensure_interface_cache();
|
||||
const Span<const bNodeTreeInterfaceSocket *> tree_inputs = tree.interface_inputs();
|
||||
const Span<nodes::StructureType> input_structure_types =
|
||||
tree.runtime->structure_type_interface->inputs;
|
||||
for (const int i : tree_inputs.index_range()) {
|
||||
const bNodeTreeInterfaceSocket &socket = *tree_inputs[i];
|
||||
const StringRefNull socket_identifier = socket.identifier;
|
||||
const bke::bNodeSocketType *typeinfo = socket.socket_typeinfo();
|
||||
const eNodeSocketDatatype socket_type = typeinfo ? eNodeSocketDatatype(typeinfo->type) :
|
||||
SOCK_CUSTOM;
|
||||
IDProperty *new_prop = id_property_create_from_socket(socket, use_name_for_ids).release();
|
||||
IDProperty *new_prop = id_property_create_from_socket(
|
||||
socket, input_structure_types[i], use_name_for_ids)
|
||||
.release();
|
||||
if (new_prop == nullptr) {
|
||||
/* Out of the set of supported input sockets, these sockets aren't added to the modifier. */
|
||||
BLI_assert(ELEM(socket_type, SOCK_GEOMETRY, SOCK_MATRIX, SOCK_BUNDLE, SOCK_CLOSURE));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -254,6 +254,7 @@ get_init_socket_fn(const bNodeTreeInterface &interface, const bNodeTreeInterface
|
||||
static BaseSocketDeclarationBuilder &build_interface_socket_declaration(
|
||||
const bNodeTree &tree,
|
||||
const bNodeTreeInterfaceSocket &io_socket,
|
||||
const std::optional<StructureType> structure_type,
|
||||
const eNodeSocketInOut in_out,
|
||||
DeclarationListBuilder &b)
|
||||
{
|
||||
@@ -389,16 +390,21 @@ static BaseSocketDeclarationBuilder &build_interface_socket_declaration(
|
||||
decl->compact(io_socket.flag & NODE_INTERFACE_SOCKET_COMPACT);
|
||||
decl->panel_toggle(io_socket.flag & NODE_INTERFACE_SOCKET_PANEL_TOGGLE);
|
||||
decl->default_input_type(NodeDefaultInputType(io_socket.default_input));
|
||||
if (structure_type) {
|
||||
decl->structure_type(*structure_type);
|
||||
}
|
||||
if (io_socket.default_input != NODE_DEFAULT_INPUT_VALUE) {
|
||||
decl->hide_value();
|
||||
}
|
||||
return *decl;
|
||||
}
|
||||
|
||||
static void node_group_declare_panel_recursive(DeclarationListBuilder &b,
|
||||
const bNodeTree &group,
|
||||
const bNodeTreeInterfacePanel &io_parent_panel,
|
||||
const bool is_root)
|
||||
static void node_group_declare_panel_recursive(
|
||||
DeclarationListBuilder &b,
|
||||
const bNodeTree &group,
|
||||
const Map<const bNodeTreeInterfaceSocket *, StructureType> &structure_type_by_socket,
|
||||
const bNodeTreeInterfacePanel &io_parent_panel,
|
||||
const bool is_root)
|
||||
{
|
||||
bool layout_added = false;
|
||||
auto add_layout_if_needed = [&]() {
|
||||
@@ -417,7 +423,8 @@ static void node_group_declare_panel_recursive(DeclarationListBuilder &b,
|
||||
if (in_out == SOCK_IN) {
|
||||
add_layout_if_needed();
|
||||
}
|
||||
build_interface_socket_declaration(group, io_socket, in_out, b);
|
||||
build_interface_socket_declaration(
|
||||
group, io_socket, structure_type_by_socket.lookup_try(&io_socket), in_out, b);
|
||||
break;
|
||||
}
|
||||
case NODE_INTERFACE_PANEL: {
|
||||
@@ -426,7 +433,8 @@ static void node_group_declare_panel_recursive(DeclarationListBuilder &b,
|
||||
auto &panel_b = b.add_panel(StringRef(io_panel.name), io_panel.identifier)
|
||||
.description(StringRef(io_panel.description))
|
||||
.default_closed(io_panel.flag & NODE_INTERFACE_PANEL_DEFAULT_CLOSED);
|
||||
node_group_declare_panel_recursive(panel_b, group, io_panel, false);
|
||||
node_group_declare_panel_recursive(
|
||||
panel_b, group, structure_type_by_socket, io_panel, false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -455,7 +463,29 @@ void node_group_declare(NodeDeclarationBuilder &b)
|
||||
/* Allow the node group interface to define the socket order. */
|
||||
r_declaration.use_custom_socket_order = true;
|
||||
|
||||
node_group_declare_panel_recursive(b, *group, group->tree_interface.root_panel, true);
|
||||
group->ensure_interface_cache();
|
||||
|
||||
Map<const bNodeTreeInterfaceSocket *, StructureType> structure_type_by_socket;
|
||||
if (group->type == NTREE_GEOMETRY) {
|
||||
structure_type_by_socket.reserve(group->interface_items().size());
|
||||
|
||||
const Span<const bNodeTreeInterfaceSocket *> inputs = group->interface_inputs();
|
||||
const Span<StructureType> input_structure_types =
|
||||
group->runtime->structure_type_interface->inputs;
|
||||
for (const int i : inputs.index_range()) {
|
||||
structure_type_by_socket.add(inputs[i], input_structure_types[i]);
|
||||
}
|
||||
|
||||
const Span<const bNodeTreeInterfaceSocket *> outputs = group->interface_outputs();
|
||||
const Span<StructureTypeInterface::OutputDependency> output_structure_types =
|
||||
group->runtime->structure_type_interface->outputs;
|
||||
for (const int i : outputs.index_range()) {
|
||||
structure_type_by_socket.add(outputs[i], output_structure_types[i].type);
|
||||
}
|
||||
}
|
||||
|
||||
node_group_declare_panel_recursive(
|
||||
b, *group, structure_type_by_socket, group->tree_interface.root_panel, true);
|
||||
|
||||
if (group->type == NTREE_GEOMETRY) {
|
||||
group->ensure_interface_cache();
|
||||
@@ -528,8 +558,12 @@ static void node_reroute_declare(blender::nodes::NodeDeclarationBuilder &b)
|
||||
|
||||
const blender::StringRefNull socket_idname(
|
||||
static_cast<const NodeReroute *>(node->storage)->type_idname);
|
||||
b.add_input<blender::nodes::decl::Custom>("Input").idname(socket_idname.c_str());
|
||||
b.add_output<blender::nodes::decl::Custom>("Output").idname(socket_idname.c_str());
|
||||
b.add_input<blender::nodes::decl::Custom>("Input")
|
||||
.idname(socket_idname.c_str())
|
||||
.structure_type(blender::nodes::StructureType::Dynamic);
|
||||
b.add_output<blender::nodes::decl::Custom>("Output")
|
||||
.idname(socket_idname.c_str())
|
||||
.structure_type(blender::nodes::StructureType::Dynamic);
|
||||
}
|
||||
|
||||
static void node_reroute_init(bNodeTree * /*ntree*/, bNode *node)
|
||||
@@ -745,7 +779,12 @@ static void group_input_declare(NodeDeclarationBuilder &b)
|
||||
const bNodeTreeInterfaceSocket &socket =
|
||||
node_interface::get_item_as<bNodeTreeInterfaceSocket>(item);
|
||||
if (socket.flag & NODE_INTERFACE_SOCKET_INPUT) {
|
||||
build_interface_socket_declaration(*node_tree, socket, SOCK_OUT, b);
|
||||
/* Trying to use the evaluated structure type for the group output node introduces a
|
||||
* "dependency cycle" between this and the structure type inferencing which uses node
|
||||
* declarations. The compromise is to not use the proper structure type in the group
|
||||
* input/output declarations and instead use a special case for the choice of socket
|
||||
* shapes.*/
|
||||
build_interface_socket_declaration(*node_tree, socket, std::nullopt, SOCK_OUT, b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -770,7 +809,7 @@ static void group_output_declare(NodeDeclarationBuilder &b)
|
||||
const bNodeTreeInterfaceSocket &socket =
|
||||
node_interface::get_item_as<bNodeTreeInterfaceSocket>(item);
|
||||
if (socket.flag & NODE_INTERFACE_SOCKET_OUTPUT) {
|
||||
build_interface_socket_declaration(*node_tree, socket, SOCK_IN, b);
|
||||
build_interface_socket_declaration(*node_tree, socket, std::nullopt, SOCK_IN, b);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -94,6 +94,14 @@ void NodeDeclarationBuilder::build_remaining_anonymous_attribute_relations()
|
||||
void NodeDeclarationBuilder::finalize()
|
||||
{
|
||||
this->build_remaining_anonymous_attribute_relations();
|
||||
if (is_function_node_) {
|
||||
for (SocketDeclaration *socket_decl : declaration_.inputs) {
|
||||
socket_decl->structure_type = StructureType::Dynamic;
|
||||
}
|
||||
for (SocketDeclaration *socket_decl : declaration_.outputs) {
|
||||
socket_decl->structure_type = StructureType::Dynamic;
|
||||
}
|
||||
}
|
||||
#ifndef NDEBUG
|
||||
declaration_.assert_valid();
|
||||
#endif
|
||||
@@ -522,6 +530,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::supports_field()
|
||||
{
|
||||
BLI_assert(this->is_input());
|
||||
decl_base_->input_field_type = InputSocketFieldType::IsSupported;
|
||||
this->structure_type(StructureType::Field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -532,6 +541,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field(
|
||||
this->reference_pass(input_dependencies);
|
||||
decl_base_->output_field_dependency = OutputFieldDependency::ForPartiallyDependentField(
|
||||
std::move(input_dependencies));
|
||||
this->structure_type(StructureType::Dynamic);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -595,6 +605,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on(const Span<
|
||||
relations.available_relations.append(relation);
|
||||
}
|
||||
}
|
||||
this->structure_type(StructureType::Field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -657,6 +668,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_on_all()
|
||||
this->field_source();
|
||||
}
|
||||
field_on_all_ = true;
|
||||
this->structure_type(StructureType::Field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -664,6 +676,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::field_source()
|
||||
{
|
||||
BLI_assert(this->is_output());
|
||||
decl_base_->output_field_dependency = OutputFieldDependency::ForFieldSource();
|
||||
this->structure_type(StructureType::Field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -672,6 +685,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field(
|
||||
{
|
||||
BLI_assert(this->is_input());
|
||||
this->hide_value();
|
||||
this->structure_type(StructureType::Dynamic);
|
||||
decl_base_->input_field_type = InputSocketFieldType::Implicit;
|
||||
decl_base_->default_input_type = default_input_type;
|
||||
return *this;
|
||||
@@ -682,6 +696,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on_al
|
||||
{
|
||||
this->implicit_field(default_input_type);
|
||||
field_on_all_ = true;
|
||||
this->structure_type(StructureType::Field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -690,6 +705,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::implicit_field_on(
|
||||
{
|
||||
this->field_on(input_indices);
|
||||
this->implicit_field(default_input_type);
|
||||
this->structure_type(StructureType::Field);
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -697,6 +713,7 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::dependent_field()
|
||||
{
|
||||
BLI_assert(this->is_output());
|
||||
decl_base_->output_field_dependency = OutputFieldDependency::ForDependentField();
|
||||
this->structure_type(StructureType::Dynamic);
|
||||
this->reference_pass_all();
|
||||
return *this;
|
||||
}
|
||||
@@ -769,6 +786,13 @@ BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::align_with_previous(
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder::structure_type(
|
||||
const StructureType structure_type)
|
||||
{
|
||||
decl_base_->structure_type = structure_type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseSocketDeclarationBuilder &BaseSocketDeclarationBuilder ::socket_name_ptr(
|
||||
const PointerRNA ptr, const StringRef property_name)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user