WIP: Closures and deferred evaluation for geometry nodes #107842

Draft
Lukas Tönne wants to merge 35 commits from LukasTonne/blender:geometry-nodes-closures into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
34 changed files with 2422 additions and 38 deletions

View File

@ -9,6 +9,7 @@ from bpy.types import (
from bpy.props import (
BoolProperty,
CollectionProperty,
EnumProperty,
FloatVectorProperty,
StringProperty,
)
@ -243,6 +244,102 @@ class NODE_OT_tree_path_parent(Operator):
return {'FINISHED'}
class NodeFunctionSignatureOperator():
# Dictionary of node types with methods to get the signature
signature_nodes = {
'GeometryNodeEvaluateFunction' : lambda node: node.signature,
}
param_type: EnumProperty(
name = "Parameter Type",
items = [
('INPUT', "Input", ""),
('OUTPUT', "Output", ""),
]
)
def params_get(self, signature):
return signature.inputs if self.param_type == 'INPUT' else signature.outputs
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname not in cls.signature_nodes:
return False
return True
class NODE_OT_function_parameter_add(NodeFunctionSignatureOperator, Operator):
'''Add a parameter to the function signature'''
bl_idname = "node.function_parameter_add"
bl_label = "Add Parameter"
bl_options = {'REGISTER', 'UNDO'}
default_socket_type = 'FLOAT'
def execute(self, context):
node = context.active_node
signature = self.signature_nodes[node.bl_idname](node)
params = self.params_get(signature)
# Remember index to move the item
dst_index = min(params.active_index + 1, len(params))
# Empty name so it is based on the type only
params.new(self.default_socket_type, "")
params.move(len(params) - 1, dst_index)
params.active_index = dst_index
return {'FINISHED'}
class NODE_OT_function_parameter_remove(NodeFunctionSignatureOperator, Operator):
'''Remove a parameter from the function signature'''
bl_idname = "node.function_parameter_remove"
bl_label = "Remove Parameter"
bl_options = {'REGISTER', 'UNDO'}
def execute(self, context):
node = context.active_node
signature = self.signature_nodes[node.bl_idname](node)
params = self.params_get(signature)
if params.active:
params.remove(params.active)
params.active_index = min(params.active_index, len(params) - 1)
return {'FINISHED'}
class NODE_OT_function_parameter_move(NodeFunctionSignatureOperator, Operator):
'''Move a parameter item up or down in the signature'''
bl_idname = "node.function_parameter_move"
bl_label = "Move Parameter"
bl_options = {'REGISTER', 'UNDO'}
direction: EnumProperty(
name="Direction",
items=[('UP', "Up", ""), ('DOWN', "Down", "")],
default = 'UP',
)
def execute(self, context):
node = context.active_node
signature = self.signature_nodes[node.bl_idname](node)
params = self.params_get(signature)
if self.direction == 'UP' and params.active_index > 0:
params.move(params.active_index, params.active_index - 1)
params.active_index -= 1
elif self.direction == 'DOWN' and params.active_index < len(params) - 1:
params.move(params.active_index, params.active_index + 1)
params.active_index += 1
return {'FINISHED'}
classes = (
NodeSetting,
@ -250,4 +347,7 @@ classes = (
NODE_OT_add_simulation_zone,
NODE_OT_collapse_hide_unused_toggle,
NODE_OT_tree_path_parent,
NODE_OT_function_parameter_add,
NODE_OT_function_parameter_remove,
NODE_OT_function_parameter_move,
)

View File

@ -629,6 +629,16 @@ class NODE_MT_category_GEO_GROUP(Menu):
node_add_menu.draw_assets_for_catalog(layout, self.bl_label)
class NODE_MT_category_GEO_FUNCTION(Menu):
bl_idname = "NODE_MT_category_GEO_FUNCTION"
bl_label = "Function"
def draw(self, context):
layout = self.layout
node_add_menu.add_node_type(layout, "GeometryNodeBindFunction")
node_add_menu.add_node_type(layout, "GeometryNodeEvaluateFunction")
class NODE_MT_category_GEO_LAYOUT(Menu):
bl_idname = "NODE_MT_category_GEO_LAYOUT"
bl_label = "Layout"
@ -665,6 +675,7 @@ class NODE_MT_geometry_node_add_all(Menu):
layout.menu("NODE_MT_category_GEO_UTILITIES")
layout.separator()
layout.menu("NODE_MT_category_GEO_GROUP")
layout.menu("NODE_MT_category_GEO_FUNCTION")
layout.menu("NODE_MT_category_GEO_LAYOUT")
node_add_menu.draw_root_assets(layout)
@ -711,6 +722,7 @@ classes = (
NODE_MT_category_GEO_UTILITIES_MATH,
NODE_MT_category_GEO_UTILITIES_ROTATION,
NODE_MT_category_GEO_GROUP,
NODE_MT_category_GEO_FUNCTION,
NODE_MT_category_GEO_LAYOUT,
)

View File

@ -1033,6 +1033,98 @@ class NODE_PT_simulation_zone_items(Panel):
layout.prop(active_item, "attribute_domain")
class NODE_UL_function_parameters(bpy.types.UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
if self.layout_type in {'DEFAULT', 'COMPACT'}:
row = layout.row(align=True)
row.template_node_socket(color=item.color)
row.prop(item, "name", text="", emboss=False, icon_value=icon)
elif self.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.template_node_socket(color=item.color)
class FunctionSignaturePanel():
# Must be defined by subclasses
param_type = None
params_prop = None
# Dictionary of node types with methods to get the signature
signature_nodes = {
'GeometryNodeEvaluateFunction' : lambda node: node.signature,
}
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None or node.bl_idname not in cls.signature_nodes:
return False
return True
def draw(self, context):
layout = self.layout
node = context.active_node
signature = self.signature_nodes[node.bl_idname](node)
params = getattr(signature, self.params_prop)
split = layout.row()
split.template_list(
"NODE_UL_function_parameters",
self.params_prop,
signature,
self.params_prop,
params,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
props = add_remove_col.operator("node.function_parameter_add", icon='ADD', text="")
props.param_type = self.param_type
props = add_remove_col.operator("node.function_parameter_remove", icon='REMOVE', text="")
props.param_type = self.param_type
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.function_parameter_move", icon='TRIA_UP', text="")
props.param_type = self.param_type
props.direction = 'UP'
props = up_down_col.operator("node.function_parameter_move", icon='TRIA_DOWN', text="")
props.param_type = self.param_type
props.direction = 'DOWN'
active_param = params.active
if active_param is not None:
layout.prop(active_param, "socket_type")
layout.prop(active_param, "name")
class NODE_PT_function_signature_inputs(FunctionSignaturePanel, Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Function Signature Inputs"
param_type = 'INPUT'
params_prop = "inputs"
class NODE_PT_function_signature_outputs(FunctionSignaturePanel, Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Function Signature Outputs"
param_type = 'OUTPUT'
params_prop = "outputs"
# Grease Pencil properties
class NODE_PT_annotation(AnnotationDataPanel, Panel):
bl_space_type = 'NODE_EDITOR'
@ -1099,6 +1191,9 @@ classes = (
NODE_PT_node_tree_interface_outputs,
NODE_UL_simulation_zone_items,
NODE_PT_simulation_zone_items,
NODE_UL_function_parameters,
NODE_PT_function_signature_inputs,
NODE_PT_function_signature_outputs,
node_panel(EEVEE_MATERIAL_PT_settings),
node_panel(MATERIAL_PT_viewport),

View File

@ -224,6 +224,59 @@ typedef int (*NodeGPUExecFunction)(struct GPUMaterial *mat,
struct GPUNodeStack *in,
struct GPUNodeStack *out);
/* -------------------------------------------------------------------- */
/** \name Node Function Signature
* \{ */
/** Set a unique parameter name.
* @return True if the unique name differs from the original name.
*/
bool nodeFunctionParameterSetUniqueName(struct bNodeFunctionSignature *sig,
struct bNodeFunctionParameter *param,
const char *name,
const char *defname);
/** Find the node owning this parameter. */
bool nodeFunctionParameterFindNode(struct bNodeTree *ntree,
const struct bNodeFunctionParameter *param,
struct bNode **r_node,
struct bNodeFunctionSignature **r_sig);
/** Find the node owning this signature. */
bool nodeFunctionSignatureFindNode(struct bNodeTree *ntree,
const struct bNodeFunctionSignature *sig,
struct bNode **r_node);
bool nodeFunctionSignatureContainsParameter(const struct bNodeFunctionSignature *sig,
const struct bNodeFunctionParameter *param,
eNodeFunctionParameterType *r_param_type);
struct bNodeFunctionParameter *nodeFunctionSignatureGetActiveParameter(
struct bNodeFunctionSignature *sig, eNodeFunctionParameterType param_type);
void nodeFunctionSignatureSetActiveParameter(struct bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
struct bNodeFunctionParameter *param);
struct bNodeFunctionParameter *nodeFunctionSignatureFindParameterByName(
struct bNodeFunctionSignature *sig, eNodeFunctionParameterType param_type, const char *name);
struct bNodeFunctionParameter *nodeFunctionSignatureAddParameter(
struct bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
short socket_type,
const char *name);
struct bNodeFunctionParameter *nodeFunctionSignatureInsertParameter(
struct bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
short socket_type,
const char *name,
int index);
void nodeFunctionSignatureRemoveParameter(struct bNodeFunctionSignature *sig,
struct bNodeFunctionParameter *param);
void nodeFunctionSignatureClearParameters(struct bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type);
void nodeFunctionSignatureMoveParameter(struct bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
int from_index,
int to_index);
/** \} */
/**
* \brief Defines a node type.
*
@ -1307,6 +1360,8 @@ void BKE_nodetree_remove_layer_n(struct bNodeTree *ntree, struct Scene *scene, i
#define GEO_NODE_SIMULATION_OUTPUT 2101
#define GEO_NODE_INPUT_SIGNED_DISTANCE 2102
#define GEO_NODE_SAMPLE_VOLUME 2103
#define GEO_NODE_BIND_FUNCTION 2104
#define GEO_NODE_EVALUATE_FUNCTION 2105
/** \} */

View File

@ -644,6 +644,13 @@ inline bool bNode::is_group_output() const
return this->type == NODE_GROUP_OUTPUT;
}
inline bool bNode::can_verify_sockets_on_read() const
{
/* Don't update node groups here because they may depend on other node groups which are not
* fully versioned yet and don't have `typeinfo` pointers set. */
return !is_group() && !ELEM(this->type, GEO_NODE_BIND_FUNCTION);
}
inline blender::Span<bNodeLink> bNode::internal_links() const
{
return this->runtime->internal_links;

View File

@ -10,6 +10,7 @@ struct ID;
struct ImageUser;
struct Main;
struct bNode;
struct bNodeFunctionSignature;
struct bNodeLink;
struct bNodeSocket;
struct bNodeTree;
@ -54,6 +55,8 @@ void BKE_ntree_update_tag_active_output_changed(struct bNodeTree *ntree);
void BKE_ntree_update_tag_missing_runtime_data(struct bNodeTree *ntree);
/** Used when the interface sockets/values have changed. */
void BKE_ntree_update_tag_interface(struct bNodeTree *ntree);
/** Used when a signature is changed. */
void BKE_ntree_update_tag_signature_changed(struct bNodeTree *ntree, struct bNodeFunctionSignature *sig);
/** Used when change parent node. */
void BKE_ntree_update_tag_parent_change(struct bNodeTree *ntree, struct bNode *node);
/** Used when an id data block changed that might be used by nodes that need to be updated. */

View File

@ -11,6 +11,8 @@
#include "FN_init.h"
#include "NOD_socket.h"
struct Tex;
struct Image;
struct Material;
@ -34,6 +36,7 @@ void BKE_cpp_types_init()
{
blender::register_cpp_types();
FN_register_cpp_types();
blender::nodes::register_cpp_types();
BLI_CPP_TYPE_REGISTER(GeometrySet);
BLI_CPP_TYPE_REGISTER(blender::bke::InstanceReference);

View File

@ -131,6 +131,387 @@ static void free_localized_node_groups(bNodeTree *ntree);
static void node_socket_interface_free(bNodeTree * /*ntree*/,
bNodeSocket *sock,
const bool do_id_user);
static void node_socket_set_typeinfo(bNodeTree *ntree,
bNodeSocket *sock,
bNodeSocketType *typeinfo);
} // namespace blender::bke
/* ************ NODE FUNCTION SIGNATURE *************** */
blender::Span<bNodeFunctionParameter> bNodeFunctionSignature::inputs_span() const
{
return blender::Span<bNodeFunctionParameter>(inputs, inputs_num);
}
blender::MutableSpan<bNodeFunctionParameter> bNodeFunctionSignature::inputs_span()
{
return blender::MutableSpan<bNodeFunctionParameter>(inputs, inputs_num);
}
blender::IndexRange bNodeFunctionSignature::inputs_range() const
{
return blender::IndexRange(inputs_num);
}
blender::Span<bNodeFunctionParameter> bNodeFunctionSignature::outputs_span() const
{
return blender::Span<bNodeFunctionParameter>(outputs, outputs_num);
}
blender::MutableSpan<bNodeFunctionParameter> bNodeFunctionSignature::outputs_span()
{
return blender::MutableSpan<bNodeFunctionParameter>(outputs, outputs_num);
}
blender::IndexRange bNodeFunctionSignature::outputs_range() const
{
return blender::IndexRange(outputs_num);
}
blender::Span<bNodeFunctionParameter> bNodeFunctionSignature::params_span(
eNodeFunctionParameterType param_type) const
{
switch (param_type) {
case NODE_FUNC_PARAM_IN:
return inputs_span();
case NODE_FUNC_PARAM_OUT:
return outputs_span();
}
return {};
}
blender::MutableSpan<bNodeFunctionParameter> bNodeFunctionSignature::params_span(
eNodeFunctionParameterType param_type)
{
switch (param_type) {
case NODE_FUNC_PARAM_IN:
return inputs_span();
case NODE_FUNC_PARAM_OUT:
return outputs_span();
}
return {};
}
blender::IndexRange bNodeFunctionSignature::params_range(
eNodeFunctionParameterType param_type) const
{
switch (param_type) {
case NODE_FUNC_PARAM_IN:
return inputs_range();
case NODE_FUNC_PARAM_OUT:
return outputs_range();
}
return {};
}
namespace blender::nodes {
struct NodeFunctionSignatureUniqueNameArgs {
Span<bNodeFunctionParameter> params_span;
const bNodeFunctionParameter *param;
};
static bool nodeFunctionSignatureUniqueNameCheck(void *arg, const char *name)
{
const NodeFunctionSignatureUniqueNameArgs &args =
*static_cast<const NodeFunctionSignatureUniqueNameArgs *>(arg);
for (const bNodeFunctionParameter &param : args.params_span) {
if (&param != args.param) {
if (STREQ(param.name, name)) {
return true;
}
}
}
return false;
}
} // namespace blender::nodes
bool nodeFunctionParameterFindNode(bNodeTree *ntree,
const bNodeFunctionParameter *param,
bNode **r_node,
bNodeFunctionSignature **r_sig)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_EVALUATE_FUNCTION) {
NodeGeometryEvaluateFunction *data = static_cast<NodeGeometryEvaluateFunction *>(
node->storage);
if (nodeFunctionSignatureContainsParameter(&data->signature, param, nullptr)) {
if (r_node) {
*r_node = node;
}
if (r_sig) {
*r_sig = &data->signature;
}
return true;
}
}
}
return false;
}
bool nodeFunctionParameterSetUniqueName(bNodeFunctionSignature *sig,
bNodeFunctionParameter *param,
const char *name,
const char *defname)
{
char unique_name[MAX_NAME + 4];
BLI_strncpy(unique_name, name, sizeof(unique_name));
bool name_changed = false;
eNodeFunctionParameterType param_type;
if (nodeFunctionSignatureContainsParameter(sig, param, &param_type)) {
blender::nodes::NodeFunctionSignatureUniqueNameArgs args{sig->params_span(param_type), param};
name_changed = BLI_uniquename_cb(blender::nodes::nodeFunctionSignatureUniqueNameCheck,
&args,
defname,
'.',
unique_name,
ARRAY_SIZE(unique_name));
}
else if (unique_name[0] == '\0') {
/* Should use default if name is empty, same as BLI_uniquename_cb behavior. */
BLI_strncpy(unique_name, defname, sizeof(unique_name));
}
param->name = BLI_strdup(unique_name);
return name_changed;
}
bool nodeFunctionSignatureFindNode(bNodeTree *ntree,
const bNodeFunctionSignature *sig,
bNode **r_node)
{
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->type == GEO_NODE_EVALUATE_FUNCTION) {
NodeGeometryEvaluateFunction *data = static_cast<NodeGeometryEvaluateFunction *>(
node->storage);
if (sig == &data->signature) {
if (r_node) {
*r_node = node;
}
return true;
}
}
}
return false;
}
bool nodeFunctionSignatureContainsParameter(const bNodeFunctionSignature *sig,
const bNodeFunctionParameter *param,
eNodeFunctionParameterType *r_param_type)
{
if (sig->inputs_span().contains_ptr(param)) {
if (r_param_type) {
*r_param_type = NODE_FUNC_PARAM_IN;
}
return true;
}
if (sig->outputs_span().contains_ptr(param)) {
if (r_param_type) {
*r_param_type = NODE_FUNC_PARAM_OUT;
}
return true;
}
return false;
}
bNodeFunctionParameter *nodeFunctionSignatureGetActiveParameter(
bNodeFunctionSignature *sig, eNodeFunctionParameterType param_type)
{
switch (param_type) {
case NODE_FUNC_PARAM_IN:
if (sig->inputs_range().contains(sig->active_input)) {
return &sig->inputs[sig->active_input];
}
break;
case NODE_FUNC_PARAM_OUT:
if (sig->outputs_range().contains(sig->active_output)) {
return &sig->outputs[sig->active_output];
}
break;
}
return nullptr;
}
void nodeFunctionSignatureSetActiveParameter(bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
bNodeFunctionParameter *param)
{
switch (param_type) {
case NODE_FUNC_PARAM_IN: {
const int index = param - sig->inputs;
if (sig->inputs_range().contains(index)) {
sig->active_input = index;
}
break;
}
case NODE_FUNC_PARAM_OUT: {
const int index = param - sig->outputs;
if (sig->outputs_range().contains(index)) {
sig->active_output = index;
}
break;
}
}
}
bNodeFunctionParameter *nodeFunctionSignatureFindParameterByName(
bNodeFunctionSignature *sig, eNodeFunctionParameterType param_type, const char *name)
{
for (bNodeFunctionParameter &param : sig->params_span(param_type)) {
if (STREQ(param.name, name)) {
return &param;
}
}
return nullptr;
}
bNodeFunctionParameter *nodeFunctionSignatureAddParameter(bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
short socket_type,
const char *name)
{
return nodeFunctionSignatureInsertParameter(
sig, param_type, socket_type, name, sig->params_range(param_type).size());
}
bNodeFunctionParameter *nodeFunctionSignatureInsertParameter(bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
short socket_type,
const char *name,
int index)
{
const Span<bNodeFunctionParameter> old_params = sig->params_span(param_type);
bNodeFunctionParameter *new_params = MEM_cnew_array<bNodeFunctionParameter>(
old_params.size() + 1, __func__);
for (const int i : old_params.index_range().take_front(index)) {
new_params[i] = old_params[i];
}
for (const int i : old_params.index_range().drop_front(index)) {
new_params[i + 1] = old_params[i];
}
const char *defname = nodeStaticSocketLabel(socket_type, 0);
bNodeFunctionParameter &added_param = new_params[index];
added_param.identifier = sig->next_identifier++;
nodeFunctionParameterSetUniqueName(sig, &added_param, name, defname);
added_param.socket_type = socket_type;
switch (param_type) {
case NODE_FUNC_PARAM_IN: {
MEM_SAFE_FREE(sig->inputs);
sig->inputs = new_params;
sig->inputs_num++;
break;
}
case NODE_FUNC_PARAM_OUT: {
MEM_SAFE_FREE(sig->outputs);
sig->outputs = new_params;
sig->outputs_num++;
break;
}
}
return &added_param;
}
static void nodeFunctionSignatureRemoveParameter(bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
bNodeFunctionParameter *param)
{
const MutableSpan<bNodeFunctionParameter> old_params = sig->params_span(param_type);
if (!old_params.as_span().contains_ptr(param)) {
return;
}
const int index = param - old_params.data();
bNodeFunctionParameter *new_params = MEM_cnew_array<bNodeFunctionParameter>(
old_params.size() - 1, __func__);
for (const int i : old_params.index_range().take_front(index)) {
new_params[i] = old_params[i];
}
for (const int i : old_params.index_range().drop_front(index + 1)) {
new_params[i - 1] = old_params[i];
}
MEM_SAFE_FREE(old_params[index].name);
switch (param_type) {
case NODE_FUNC_PARAM_IN: {
MEM_SAFE_FREE(sig->inputs);
sig->inputs = new_params;
sig->inputs_num--;
break;
}
case NODE_FUNC_PARAM_OUT: {
MEM_SAFE_FREE(sig->outputs);
sig->outputs = new_params;
sig->outputs_num--;
break;
}
}
}
void nodeFunctionSignatureRemoveParameter(bNodeFunctionSignature *sig,
bNodeFunctionParameter *param)
{
nodeFunctionSignatureRemoveParameter(sig, NODE_FUNC_PARAM_IN, param);
nodeFunctionSignatureRemoveParameter(sig, NODE_FUNC_PARAM_OUT, param);
}
void nodeFunctionSignatureClearParameters(bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type)
{
switch (param_type) {
case NODE_FUNC_PARAM_IN:
for (bNodeFunctionParameter &param : sig->inputs_span()) {
MEM_SAFE_FREE(param.name);
}
MEM_SAFE_FREE(sig->inputs);
sig->inputs = nullptr;
sig->inputs_num = 0;
break;
case NODE_FUNC_PARAM_OUT:
for (bNodeFunctionParameter &param : sig->outputs_span()) {
MEM_SAFE_FREE(param.name);
}
MEM_SAFE_FREE(sig->outputs);
sig->outputs = nullptr;
sig->outputs_num = 0;
break;
}
}
void nodeFunctionSignatureMoveParameter(bNodeFunctionSignature *sig,
eNodeFunctionParameterType param_type,
int from_index,
int to_index)
{
const MutableSpan<bNodeFunctionParameter> params = sig->params_span(param_type);
BLI_assert(params.index_range().contains(from_index));
BLI_assert(params.index_range().contains(to_index));
if (from_index == to_index) {
return;
}
else if (from_index < to_index) {
const bNodeFunctionParameter tmp = params[from_index];
for (int i = from_index; i < to_index; ++i) {
params[i] = params[i + 1];
}
params[to_index] = tmp;
}
else /* from_index > to_index */ {
const bNodeFunctionParameter tmp = params[from_index];
for (int i = from_index; i > to_index; --i) {
params[i] = params[i - 1];
}
params[to_index] = tmp;
}
}
namespace blender::bke {
static void ntree_init_data(ID *id)
{
@ -324,6 +705,12 @@ static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction &default_value =
*sock->default_value_typed<bNodeSocketValueFunction>();
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, default_value.value, IDWALK_CB_USER);
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -475,6 +862,9 @@ static void write_node_socket_default_value(BlendWriter *writer, bNodeSocket *so
case SOCK_MATERIAL:
BLO_write_struct(writer, bNodeSocketValueMaterial, sock->default_value);
break;
case SOCK_FUNCTION:
BLO_write_struct(writer, bNodeSocketValueFunction, sock->default_value);
break;
case SOCK_CUSTOM:
/* Custom node sockets where default_value is defined uses custom properties for storage. */
break;
@ -513,6 +903,19 @@ static void write_node_socket_interface(BlendWriter *writer, bNodeSocket *sock)
write_node_socket_default_value(writer, sock);
}
static void write_node_function_signature(BlendWriter *writer, bNodeFunctionSignature *sig)
{
BLO_write_struct(writer, bNodeFunctionSignature, sig);
BLO_write_struct_array(writer, bNodeFunctionParameter, sig->inputs_num, sig->inputs);
BLO_write_struct_array(writer, bNodeFunctionParameter, sig->outputs_num, sig->outputs);
for (const bNodeFunctionParameter &param : sig->inputs_span()) {
BLO_write_string(writer, param.name);
}
for (const bNodeFunctionParameter &param : sig->outputs_span()) {
BLO_write_string(writer, param.name);
}
}
void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
{
BKE_id_blend_write(writer, &ntree->id);
@ -600,6 +1003,11 @@ void ntreeBlendWrite(BlendWriter *writer, bNodeTree *ntree)
}
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
}
else if (node->type == GEO_NODE_EVALUATE_FUNCTION) {
NodeGeometryEvaluateFunction *storage = (NodeGeometryEvaluateFunction *)node->storage;
BLO_write_struct_by_name(writer, node->typeinfo->storagename, storage);
write_node_function_signature(writer, &storage->signature);
}
else if (node->typeinfo != &blender::bke::NodeTypeUndefined) {
BLO_write_struct_by_name(writer, node->typeinfo->storagename, node->storage);
}
@ -675,6 +1083,19 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
sock->runtime = MEM_new<bNodeSocketRuntime>(__func__);
}
static void direct_link_node_function_signature(BlendDataReader *reader,
bNodeFunctionSignature *sig)
{
BLO_read_data_address(reader, &sig->inputs);
BLO_read_data_address(reader, &sig->outputs);
for (bNodeFunctionParameter &param : sig->inputs_span()) {
BLO_read_data_address(reader, &param.name);
}
for (bNodeFunctionParameter &param : sig->outputs_span()) {
BLO_read_data_address(reader, &param.name);
}
}
void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
{
/* Special case for this pointer, do not rely on regular `lib_link` process here. Avoids needs
@ -817,6 +1238,11 @@ void ntreeBlendReadData(BlendDataReader *reader, ID *owner_id, bNodeTree *ntree)
}
break;
}
case GEO_NODE_EVALUATE_FUNCTION: {
NodeGeometryEvaluateFunction *storage = (NodeGeometryEvaluateFunction *)node->storage;
direct_link_node_function_signature(reader, &storage->signature);
break;
}
default:
break;
@ -916,6 +1342,11 @@ static void lib_link_node_socket(BlendLibReader *reader, ID *self_id, bNodeSocke
reader, self_id, &sock->default_value_typed<bNodeSocketValueMaterial>()->value);
break;
}
case SOCK_FUNCTION: {
BLO_read_id_address(
reader, self_id, &sock->default_value_typed<bNodeSocketValueFunction>()->value);
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -965,9 +1396,7 @@ void ntreeBlendReadLib(BlendLibReader *reader, bNodeTree *ntree)
* to match the static layout. */
if (!BLO_read_lib_is_undo(reader)) {
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
/* Don't update node groups here because they may depend on other node groups which are not
* fully versioned yet and don't have `typeinfo` pointers set. */
if (!node->is_group()) {
if (node->can_verify_sockets_on_read()) {
node_verify_sockets(ntree, node, false);
}
}
@ -1009,6 +1438,10 @@ static void expand_node_socket(BlendExpander *expander, bNodeSocket *sock)
BLO_expand(expander, sock->default_value_typed<bNodeSocketValueMaterial>()->value);
break;
}
case SOCK_FUNCTION: {
BLO_expand(expander, sock->default_value_typed<bNodeSocketValueFunction>()->value);
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -1698,6 +2131,12 @@ static void socket_id_user_increment(bNodeSocket *sock)
id_us_plus(reinterpret_cast<ID *>(default_value.value));
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction &default_value =
*sock->default_value_typed<bNodeSocketValueFunction>();
id_us_plus(reinterpret_cast<ID *>(default_value.value));
break;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -1744,6 +2183,12 @@ static bool socket_id_user_decrement(bNodeSocket *sock)
id_us_min(reinterpret_cast<ID *>(default_value.value));
return default_value.value != nullptr;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction &default_value =
*sock->default_value_typed<bNodeSocketValueFunction>();
id_us_min(reinterpret_cast<ID *>(default_value.value));
return default_value.value != nullptr;
}
case SOCK_FLOAT:
case SOCK_VECTOR:
case SOCK_RGBA:
@ -1810,6 +2255,7 @@ void nodeModifySocketType(bNodeTree *ntree,
case SOCK_COLLECTION:
case SOCK_TEXTURE:
case SOCK_MATERIAL:
case SOCK_FUNCTION:
break;
}
}
@ -1942,6 +2388,8 @@ const char *nodeStaticSocketType(const int type, const int subtype)
return "NodeSocketTexture";
case SOCK_MATERIAL:
return "NodeSocketMaterial";
case SOCK_FUNCTION:
return "NodeSocketFunction";
default:
break;
}
@ -2021,6 +2469,8 @@ const char *nodeStaticSocketInterfaceType(const int type, const int subtype)
return "NodeSocketInterfaceTexture";
case SOCK_MATERIAL:
return "NodeSocketInterfaceMaterial";
case SOCK_FUNCTION:
return "NodeSocketInterfaceFunction";
default:
break;
}
@ -2056,6 +2506,8 @@ const char *nodeStaticSocketLabel(const int type, const int /*subtype*/)
return "Texture";
case SOCK_MATERIAL:
return "Material";
case SOCK_FUNCTION:
return "Function";
default:
break;
}
@ -2575,6 +3027,8 @@ static void *socket_value_storage(bNodeSocket &socket)
return &socket.default_value_typed<bNodeSocketValueObject>()->value;
case SOCK_MATERIAL:
return &socket.default_value_typed<bNodeSocketValueMaterial>()->value;
case SOCK_FUNCTION:
return &socket.default_value_typed<bNodeSocketValueFunction>()->value;
case SOCK_STRING:
/* We don't want do this now! */
return nullptr;
@ -2632,7 +3086,8 @@ void node_socket_move_default_value(Main & /*bmain*/,
SOCK_IMAGE,
SOCK_MATERIAL,
SOCK_TEXTURE,
SOCK_OBJECT))
SOCK_OBJECT,
SOCK_FUNCTION))
{
src_type.value_initialize(src_value);
}
@ -2667,7 +3122,8 @@ bNodeLink *nodeAddLink(
bNodeLink *link = nullptr;
if (eNodeSocketInOut(fromsock->in_out) == SOCK_OUT &&
eNodeSocketInOut(tosock->in_out) == SOCK_IN) {
eNodeSocketInOut(tosock->in_out) == SOCK_IN)
{
link = MEM_cnew<bNodeLink>("link");
if (ntree) {
BLI_addtail(&ntree->links, link);
@ -2801,7 +3257,8 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
/* remove the link that would be the same as the relinked one */
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link_to_compare, &ntree->links) {
if (link_to_compare->fromsock == fromlink->fromsock &&
link_to_compare->tosock == link->tosock) {
link_to_compare->tosock == link->tosock)
{
blender::bke::adjust_multi_input_indices_after_removed_link(
ntree, link_to_compare->tosock, link_to_compare->multi_input_socket_index);
duplicate_links_to_remove.append_non_duplicates(link_to_compare);

View File

@ -1201,6 +1201,15 @@ void BKE_ntree_update_tag_interface(bNodeTree *ntree)
add_tree_tag(ntree, NTREE_CHANGED_INTERFACE);
}
void BKE_ntree_update_tag_signature_changed(bNodeTree *ntree, bNodeFunctionSignature *sig)
{
/* TODO what needs to happen here for general signatures?
* Flag in bNodeFunctionSignature itself (runtime data),
* then propagate that to parent tree in BKE_ntree_update_main_tree.
*/
add_tree_tag(ntree, NTREE_CHANGED_INTERFACE);
}
void BKE_ntree_update_tag_parent_change(bNodeTree *ntree, bNode *node)
{
add_node_tag(ntree, node, NTREE_CHANGED_PARENT);

View File

@ -915,7 +915,8 @@ void DepsgraphNodeBuilder::build_object_modifiers(Object *object)
}
if (modifier_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
if (nmd->simulation_cache &&
nmd->simulation_cache->cache_state() == bke::sim::CacheState::Valid) {
nmd->simulation_cache->cache_state() == bke::sim::CacheState::Valid)
{
nmd->simulation_cache->invalidate();
}
}
@ -1767,6 +1768,9 @@ void DepsgraphNodeBuilder::build_nodetree_socket(bNodeSocket *socket)
else if (socket->type == SOCK_MATERIAL) {
build_id((ID *)((bNodeSocketValueMaterial *)socket->default_value)->value);
}
else if (socket->type == SOCK_FUNCTION) {
build_id((ID *)((bNodeSocketValueFunction *)socket->default_value)->value);
}
}
void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
@ -1850,9 +1854,8 @@ void DepsgraphNodeBuilder::build_nodetree(bNodeTree *ntree)
else if (id_type == ID_VF) {
build_vfont((VFont *)id);
}
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
else if (id_type == ID_NT) {
build_nodetree((bNodeTree *)id);
}
else {
BLI_assert_msg(0, "Unknown ID type used for node");

View File

@ -2647,6 +2647,12 @@ void DepsgraphRelationBuilder::build_nodetree_socket(bNodeSocket *socket)
build_material(material);
}
}
else if (socket->type == SOCK_FUNCTION) {
bNodeTree *ntree = ((bNodeSocketValueFunction *)socket->default_value)->value;
if (ntree != nullptr) {
build_nodetree(ntree);
}
}
}
void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
@ -2742,7 +2748,7 @@ void DepsgraphRelationBuilder::build_nodetree(bNodeTree *ntree)
ComponentKey vfont_key(id, NodeType::GENERIC_DATABLOCK);
add_relation(vfont_key, ntree_output_key, "VFont -> Node");
}
else if (ELEM(bnode->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
else if (id_type == ID_NT) {
bNodeTree *group_ntree = (bNodeTree *)id;
build_nodetree(group_ntree);
ComponentKey group_output_key(&group_ntree->id, NodeType::NTREE_OUTPUT);

View File

@ -1201,6 +1201,7 @@ static const float std_node_socket_colors[][4] = {
{0.96, 0.96, 0.96, 1.0}, /* SOCK_COLLECTION */
{0.62, 0.31, 0.64, 1.0}, /* SOCK_TEXTURE */
{0.92, 0.46, 0.51, 1.0}, /* SOCK_MATERIAL */
{0.98, 0.30, 0.83, 1.0}, /* SOCK_FUNCTION */
};
/* common color callbacks for standard types */
@ -1295,7 +1296,8 @@ static void std_node_socket_draw(
}
if ((sock->in_out == SOCK_OUT) || (sock->flag & SOCK_IS_LINKED) ||
(sock->flag & SOCK_HIDE_VALUE)) {
(sock->flag & SOCK_HIDE_VALUE))
{
node_socket_button_label(C, layout, ptr, node_ptr, text);
return;
}
@ -1417,6 +1419,10 @@ static void std_node_socket_draw(
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
case SOCK_FUNCTION: {
uiItemR(layout, ptr, "default_value", DEFAULT_FLAGS, text, 0);
break;
}
default:
node_socket_button_label(C, layout, ptr, node_ptr, text);
break;
@ -1459,7 +1465,8 @@ static void std_node_socket_interface_draw(bContext * /*C*/, uiLayout *layout, P
case SOCK_COLLECTION:
case SOCK_IMAGE:
case SOCK_TEXTURE:
case SOCK_MATERIAL: {
case SOCK_MATERIAL:
case SOCK_FUNCTION: {
uiItemR(col, ptr, "default_value", DEFAULT_FLAGS, IFACE_("Default"), 0);
break;
}

View File

@ -2243,6 +2243,7 @@ static int get_main_socket_priority(const bNodeSocket *socket)
case SOCK_COLLECTION:
case SOCK_TEXTURE:
case SOCK_MATERIAL:
case SOCK_FUNCTION:
return 6;
}
return -1;

View File

@ -410,6 +410,9 @@ static Vector<NodeLinkItem> ui_node_link_items(NodeLinkArg *arg,
else if (dynamic_cast<const decl::Object *>(&socket_decl)) {
item.socket_type = SOCK_OBJECT;
}
else if (dynamic_cast<const decl::Function *>(&socket_decl)) {
item.socket_type = SOCK_FUNCTION;
}
else {
item.socket_type = SOCK_CUSTOM;
}

View File

@ -238,6 +238,7 @@ typedef enum eNodeSocketDatatype {
SOCK_COLLECTION = 11,
SOCK_TEXTURE = 12,
SOCK_MATERIAL = 13,
SOCK_FUNCTION = 14,
} eNodeSocketDatatype;
/** Socket shape. */
@ -382,6 +383,7 @@ typedef struct bNode {
bool is_group() const;
bool is_group_input() const;
bool is_group_output() const;
bool can_verify_sockets_on_read() const;
const blender::nodes::NodeDeclaration *declaration() const;
/** A span containing all internal links when the node is muted. */
blender::Span<bNodeLink> internal_links() const;
@ -754,6 +756,10 @@ typedef struct bNodeSocketValueMaterial {
struct Material *value;
} bNodeSocketValueMaterial;
typedef struct bNodeSocketValueFunction {
struct bNodeTree *value;
} bNodeSocketValueFunction;
/* Data structs, for `node->storage`. */
typedef enum CMPNodeMaskType {
@ -1669,6 +1675,48 @@ typedef struct NodeShaderMix {
char _pad[3];
} NodeShaderMix;
/* Input or output in a bNodeFunctionSignature. */
typedef struct bNodeFunctionParameter {
char *name;
int socket_type;
int identifier;
} bNodeFunctionParameter;
typedef enum eNodeFunctionParameterType {
NODE_FUNC_PARAM_IN,
NODE_FUNC_PARAM_OUT,
} eNodeFunctionParameterType;
typedef struct bNodeFunctionSignature {
bNodeFunctionParameter *inputs;
bNodeFunctionParameter *outputs;
int inputs_num;
int outputs_num;
int active_input;
int active_output;
int next_identifier;
int _pad;
#ifdef __cplusplus
blender::Span<bNodeFunctionParameter> inputs_span() const;
blender::MutableSpan<bNodeFunctionParameter> inputs_span();
blender::IndexRange inputs_range() const;
blender::Span<bNodeFunctionParameter> outputs_span() const;
blender::MutableSpan<bNodeFunctionParameter> outputs_span();
blender::IndexRange outputs_range() const;
blender::Span<bNodeFunctionParameter> params_span(eNodeFunctionParameterType param_type) const;
blender::MutableSpan<bNodeFunctionParameter> params_span(eNodeFunctionParameterType param_type);
blender::IndexRange params_range(eNodeFunctionParameterType param_type) const;
#endif
} bNodeFunctionSignature;
typedef struct NodeGeometryEvaluateFunction {
/* Expected signature of the function. */
bNodeFunctionSignature signature;
} NodeGeometryEvaluateFunction;
/* script node mode */
#define NODE_SCRIPT_INTERNAL 0
#define NODE_SCRIPT_EXTERNAL 1

View File

@ -74,6 +74,7 @@ static const EnumPropertyItem node_socket_data_type_items[] = {
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
{SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{SOCK_FUNCTION, "FUNCTION", 0, "Function", ""},
{0, NULL, 0, NULL, NULL},
};
@ -102,6 +103,7 @@ static const EnumPropertyItem node_socket_type_items[] = {
{SOCK_COLLECTION, "COLLECTION", 0, "Collection", ""},
{SOCK_TEXTURE, "TEXTURE", 0, "Texture", ""},
{SOCK_MATERIAL, "MATERIAL", 0, "Material", ""},
{SOCK_FUNCTION, "FUNCTION", 0, "Function", ""},
{0, NULL, 0, NULL, NULL},
};
@ -2085,7 +2087,8 @@ static bool switch_type_supported(const EnumPropertyItem *item)
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_MATERIAL,
SOCK_IMAGE);
SOCK_IMAGE,
SOCK_FUNCTION);
}
static const EnumPropertyItem *rna_GeometryNodeSwitch_type_itemf(bContext *UNUSED(C),
@ -3281,6 +3284,209 @@ static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C,
DEG_relations_tag_update(bmain);
}
/* ******** Node Function Signature ******** */
static void rna_NodeFunctionParameter_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeFunctionParameter *param = (bNodeFunctionParameter *)ptr->data;
bNode *node;
if (nodeFunctionParameterFindNode(ntree, param, &node, NULL)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
}
}
static void rna_NodeFunctionParameter_name_set(PointerRNA *ptr, const char *value)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeFunctionParameter *param = (bNodeFunctionParameter *)ptr->data;
bNode *node;
bNodeFunctionSignature *sig;
if (nodeFunctionParameterFindNode(ntree, param, &node, &sig)) {
const char *defname = nodeStaticSocketLabel(param->socket_type, 0);
nodeFunctionParameterSetUniqueName(sig, param, value, defname);
}
}
static void rna_NodeFunctionParameter_color_get(PointerRNA *ptr, float *values)
{
bNodeFunctionParameter *param = (bNodeFunctionParameter *)ptr->data;
const char *socket_type_idname = nodeStaticSocketType(param->socket_type, 0);
node_type_draw_color(socket_type_idname, values);
}
static bNodeFunctionParameter *rna_NodeFunctionSignature_inputs_new(ID *id,
bNodeFunctionSignature *sig,
Main *bmain,
ReportList *reports,
int socket_type,
const char *name)
{
bNodeFunctionParameter *param = nodeFunctionSignatureAddParameter(sig, NODE_FUNC_PARAM_IN, (short)socket_type, name);
if (param == NULL) {
BKE_report(reports, RPT_ERROR, "Unable to create input parameter");
}
else {
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
return param;
}
static bNodeFunctionParameter *rna_NodeFunctionSignature_outputs_new(ID *id,
bNodeFunctionSignature *sig,
Main *bmain,
ReportList *reports,
int socket_type,
const char *name)
{
bNodeFunctionParameter *param = nodeFunctionSignatureAddParameter(
sig, NODE_FUNC_PARAM_OUT, (short)socket_type, name);
if (param == NULL) {
BKE_report(reports, RPT_ERROR, "Unable to create output parameter");
}
else {
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
return param;
}
static void rna_NodeFunctionSignature_params_remove(ID *id,
bNodeFunctionSignature *sig,
Main *bmain,
ReportList *reports,
bNodeFunctionParameter *param)
{
if (!nodeFunctionSignatureContainsParameter(sig, param, NULL)) {
BKE_reportf(reports, RPT_ERROR, "Unable to remove parameter '%s' from signature", param->name);
}
else {
nodeFunctionSignatureRemoveParameter(sig, param);
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
}
static void rna_NodeFunctionSignature_inputs_clear(ID *id, bNodeFunctionSignature *sig, Main *bmain)
{
nodeFunctionSignatureClearParameters(sig, NODE_FUNC_PARAM_IN);
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
static void rna_NodeFunctionSignature_outputs_clear(ID *id,
bNodeFunctionSignature *sig,
Main *bmain)
{
nodeFunctionSignatureClearParameters(sig, NODE_FUNC_PARAM_OUT);
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
static void rna_NodeFunctionSignature_inputs_move(
ID *id, bNodeFunctionSignature *sig, Main *bmain, int from_index, int to_index)
{
if (from_index < 0 || from_index >= sig->inputs_num || to_index < 0 ||
to_index >= sig->inputs_num) {
return;
}
nodeFunctionSignatureMoveParameter(sig, NODE_FUNC_PARAM_IN, from_index, to_index);
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
static void rna_NodeFunctionSignature_outputs_move(
ID *id, bNodeFunctionSignature *sig, Main *bmain, int from_index, int to_index)
{
if (from_index < 0 || from_index >= sig->outputs_num || to_index < 0 ||
to_index >= sig->outputs_num) {
return;
}
nodeFunctionSignatureMoveParameter(sig, NODE_FUNC_PARAM_OUT, from_index, to_index);
bNodeTree *ntree = (bNodeTree *)id;
bNode *node;
if (nodeFunctionSignatureFindNode(ntree, sig, &node)) {
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
}
static PointerRNA rna_NodeFunctionSignature_active_input_get(PointerRNA *ptr)
{
bNodeFunctionSignature *sig = (bNodeFunctionSignature *)ptr->data;
bNodeFunctionParameter *param = nodeFunctionSignatureGetActiveParameter(sig, NODE_FUNC_PARAM_IN);
PointerRNA r_ptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeFunctionParameter, param, &r_ptr);
return r_ptr;
}
static void rna_NodeFunctionSignature_active_input_set(PointerRNA *ptr, PointerRNA value)
{
bNodeFunctionSignature *sig = (bNodeFunctionSignature *)ptr->data;
nodeFunctionSignatureSetActiveParameter(sig, NODE_FUNC_PARAM_IN, (bNodeFunctionParameter *)value.data);
}
static PointerRNA rna_NodeFunctionSignature_active_output_get(PointerRNA *ptr)
{
bNodeFunctionSignature *sig = (bNodeFunctionSignature *)ptr->data;
bNodeFunctionParameter *param = nodeFunctionSignatureGetActiveParameter(sig, NODE_FUNC_PARAM_OUT);
PointerRNA r_ptr;
RNA_pointer_create(ptr->owner_id, &RNA_NodeFunctionParameter, param, &r_ptr);
return r_ptr;
}
static void rna_NodeFunctionSignature_active_output_set(PointerRNA *ptr, PointerRNA value)
{
bNodeFunctionSignature *sig = (bNodeFunctionSignature *)ptr->data;
nodeFunctionSignatureSetActiveParameter(
sig, NODE_FUNC_PARAM_OUT, (bNodeFunctionParameter *)value.data);
}
/* ******** Node Types ******** */
static void rna_NodeInternalSocketTemplate_name_get(PointerRNA *ptr, char *value)
@ -4655,6 +4861,20 @@ bool rna_NodeSocketMaterial_default_value_poll(PointerRNA *UNUSED(ptr), PointerR
return ma->gp_style == NULL;
}
bool rna_NodeSocketFunction_default_value_poll(PointerRNA *ptr, PointerRNA value)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeTree *ngroup = value.data;
/* only allow node trees of the same type as the group node's tree */
if (ngroup->type != ntree->type) {
return false;
}
const char *disabled_hint = NULL;
return nodeGroupPoll(ntree, ngroup, &disabled_hint);
}
static int rna_NodeConvertColorSpace_from_color_space_get(struct PointerRNA *ptr)
{
bNode *node = (bNode *)ptr->data;
@ -4704,6 +4924,51 @@ static const EnumPropertyItem *rna_NodeConvertColorSpace_color_space_itemf(
return items;
}
static void rna_FunctionNodeBind_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(NULL, bmain, ntree);
DEG_relations_tag_update(bmain);
}
static void rna_FunctionNodeBind_node_tree_set(PointerRNA *ptr,
const PointerRNA value,
struct ReportList *UNUSED(reports))
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = ptr->data;
bNodeTree *ngroup = value.data;
const char *disabled_hint = NULL;
if (nodeGroupPoll(ntree, ngroup, &disabled_hint)) {
if (node->id) {
id_us_min(node->id);
}
if (ngroup) {
id_us_plus(&ngroup->id);
}
node->id = &ngroup->id;
}
}
static bool rna_FunctionNodeBind_node_tree_poll(PointerRNA *ptr, const PointerRNA value)
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNodeTree *ngroup = value.data;
/* only allow node trees of the same type as the bind node's tree */
if (ngroup->type != ntree->type) {
return false;
}
const char *disabled_hint = NULL;
return nodeGroupPoll(ntree, ngroup, &disabled_hint);
}
#else
static const EnumPropertyItem prop_image_layer_items[] = {
@ -5332,6 +5597,33 @@ static void def_fn_combsep_color(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_geo_bind_function(StructRNA *srna)
{
PropertyRNA *prop;
prop = RNA_def_property(srna, "node_tree", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "id");
RNA_def_property_struct_type(prop, "NodeTree");
RNA_def_property_pointer_funcs(
prop, NULL, "rna_FunctionNodeBind_node_tree_set", NULL, "rna_FunctionNodeBind_node_tree_poll");
RNA_def_property_flag(prop, PROP_EDITABLE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Node Tree", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_FunctionNodeBind_update");
}
static void def_geo_evaluate_function(StructRNA *srna)
{
PropertyRNA *prop;
RNA_def_struct_sdna_from(srna, "NodeGeometryEvaluateFunction", "storage");
prop = RNA_def_property(srna, "signature", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "signature");
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_ui_text(prop, "Signature", "Expected signature of the function");
}
/* -- Shader Nodes ---------------------------------------------------------- */
static void def_sh_output(StructRNA *srna)
@ -11691,6 +11983,146 @@ static void rna_def_node_socket_interface(BlenderRNA *brna)
RNA_def_parameter_flags(parm, PROP_NEVER_NULL, PARM_REQUIRED | PARM_RNAPTR);
}
static void rna_def_node_function_parameter(BlenderRNA *brna)
{
PropertyRNA *prop;
StructRNA *srna = RNA_def_struct(brna, "NodeFunctionParameter", NULL);
RNA_def_struct_ui_text(
srna, "Node Function Parameter", "Input or output declaration of a node function");
RNA_def_struct_sdna(srna, "bNodeFunctionParameter");
prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NodeFunctionParameter_name_set");
RNA_def_property_ui_text(prop, "Name", "");
RNA_def_struct_name_property(srna, prop);
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeFunctionParameter_update");
prop = RNA_def_property(srna, "socket_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, node_socket_data_type_items);
RNA_def_property_ui_text(prop, "Socket Type", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeFunctionParameter_update");
prop = RNA_def_property(srna, "color", PROP_FLOAT, PROP_COLOR_GAMMA);
RNA_def_property_array(prop, 4);
RNA_def_property_float_funcs(prop, "rna_NodeFunctionParameter_color_get", NULL, NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Color", "Socket color");
}
static void rna_def_node_function_signature_api(BlenderRNA *brna,
PropertyRNA *cprop,
eNodeFunctionParameterType param_type)
{
StructRNA *srna;
PropertyRNA *prop;
PropertyRNA *parm;
FunctionRNA *func;
const char *structtype = (param_type == NODE_FUNC_PARAM_IN ? "NodeFunctionSignatureInputs" :
"NodeFunctionSignatureOutputs");
const char *uiname = (param_type == NODE_FUNC_PARAM_IN ? "Node Function Signature Inputs" :
"Node Function Signature Outputs");
const char *active_index_sdna = (param_type == NODE_FUNC_PARAM_IN ? "active_input" :
"active_output");
const char *active_param_get = (param_type == NODE_FUNC_PARAM_IN ?
"rna_NodeFunctionSignature_active_input_get" :
"rna_NodeFunctionSignature_active_output_get");
const char *active_param_set = (param_type == NODE_FUNC_PARAM_IN ?
"rna_NodeFunctionSignature_active_input_set" :
"rna_NodeFunctionSignature_active_output_set");
const char *newfunc = (param_type == NODE_FUNC_PARAM_IN ?
"rna_NodeFunctionSignature_inputs_new" :
"rna_NodeFunctionSignature_outputs_new");
const char *removefunc = "rna_NodeFunctionSignature_params_remove";
const char *clearfunc = (param_type == NODE_FUNC_PARAM_IN ?
"rna_NodeFunctionSignature_inputs_clear" :
"rna_NodeFunctionSignature_outputs_clear");
const char *movefunc = (param_type == NODE_FUNC_PARAM_IN ?
"rna_NodeFunctionSignature_inputs_move" :
"rna_NodeFunctionSignature_outputs_move");
RNA_def_property_srna(cprop, structtype);
srna = RNA_def_struct(brna, structtype, NULL);
RNA_def_struct_sdna(srna, "bNodeFunctionSignature");
RNA_def_struct_ui_text(srna, uiname, "Collection of node function parameters");
prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
RNA_def_property_int_sdna(prop, NULL, active_index_sdna);
RNA_def_property_ui_text(prop, "Active Index", "Index of the active parameter");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_NODE, NULL);
prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
RNA_def_property_struct_type(prop, "NodeFunctionParameter");
RNA_def_property_pointer_funcs(prop, active_param_get, active_param_set, NULL, NULL);
RNA_def_property_ui_text(prop, "Active", "Active parameter");
RNA_def_property_update(prop, NC_NODE, NULL);
func = RNA_def_function(srna, "new", newfunc);
RNA_def_function_ui_description(func, "Add a parameter to this signature");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_enum(
func, "socket_type", node_socket_data_type_items, SOCK_FLOAT, "Type", "Data type");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_string(func, "name", NULL, MAX_NAME, "Name", "");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
/* return value */
parm = RNA_def_pointer(func, "param", "NodeFunctionParameter", "", "New parameter");
RNA_def_function_return(func, parm);
func = RNA_def_function(srna, "remove", removefunc);
RNA_def_function_ui_description(func, "Remove a parameter from this signature");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN | FUNC_USE_REPORTS);
parm = RNA_def_pointer(func, "param", "NodeFunctionParameter", "", "The parameter to remove");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
func = RNA_def_function(srna, "clear", clearfunc);
RNA_def_function_ui_description(func, "Remove all parameters from this collection");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
func = RNA_def_function(srna, "move", movefunc);
RNA_def_function_ui_description(func, "Move a parameter to another position");
RNA_def_function_flag(func, FUNC_USE_SELF_ID | FUNC_USE_MAIN);
parm = RNA_def_int(func,
"from_index",
-1,
0,
INT_MAX,
"From Index",
"Index of the parameter to move",
0,
10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
parm = RNA_def_int(
func, "to_index", -1, 0, INT_MAX, "To Index", "Target index for the parameter", 0, 10000);
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
}
static void rna_def_node_function_signature(BlenderRNA *brna)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, "NodeFunctionSignature", NULL);
RNA_def_struct_ui_text(
srna, "Node Function Signature", "Declaration of inputs and outputs of a node function");
RNA_def_struct_sdna(srna, "bNodeFunctionSignature");
prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "inputs", "inputs_num");
RNA_def_property_struct_type(prop, "NodeFunctionParameter");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Inputs", "Function inputs");
rna_def_node_function_signature_api(brna, prop, NODE_FUNC_PARAM_IN);
prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, NULL, "outputs", "outputs_num");
RNA_def_property_struct_type(prop, "NodeFunctionParameter");
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Outputs", "Function outputs");
rna_def_node_function_signature_api(brna, prop, NODE_FUNC_PARAM_OUT);
}
static void rna_def_node_socket_float(BlenderRNA *brna,
const char *idname,
const char *interface_idname,
@ -12239,6 +12671,47 @@ static void rna_def_node_socket_material(BlenderRNA *brna,
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_function(BlenderRNA *brna,
const char *identifier,
const char *interface_idname)
{
StructRNA *srna;
PropertyRNA *prop;
srna = RNA_def_struct(brna, identifier, "NodeSocketStandard");
RNA_def_struct_ui_text(srna, "Function Node Socket", "Function socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "NodeTree");
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_NodeSocketFunction_default_value_poll");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
srna = RNA_def_struct(brna, interface_idname, "NodeSocketInterfaceStandard");
RNA_def_struct_ui_text(srna, "Function Node Socket Interface", "Function socket of a node");
RNA_def_struct_sdna(srna, "bNodeSocket");
RNA_def_struct_sdna_from(srna, "bNodeSocketValueMaterial", "default_value");
prop = RNA_def_property(srna, "default_value", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "NodeTree");
RNA_def_property_pointer_funcs(
prop, NULL, NULL, NULL, "rna_NodeSocketFunction_default_value_poll");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketInterface_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT);
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
}
static void rna_def_node_socket_standard_types(BlenderRNA *brna)
{
/* XXX Workaround: Registered functions are not exposed in python by bpy,
@ -12391,6 +12864,8 @@ static void rna_def_node_socket_standard_types(BlenderRNA *brna)
rna_def_node_socket_texture(brna, "NodeSocketTexture", "NodeSocketInterfaceTexture");
rna_def_node_socket_material(brna, "NodeSocketMaterial", "NodeSocketInterfaceMaterial");
rna_def_node_socket_function(brna, "NodeSocketFunction", "NodeSocketInterfaceFunction");
}
static void rna_def_internal_node(BlenderRNA *brna)
@ -13386,6 +13861,8 @@ void RNA_def_nodetree(BlenderRNA *brna)
rna_def_node_socket(brna);
rna_def_node_socket_interface(brna);
rna_def_node_function_parameter(brna);
rna_def_node_function_signature(brna);
rna_def_node(brna);
rna_def_node_link(brna);

View File

@ -85,6 +85,7 @@
#include "ED_undo.h"
#include "ED_viewer_path.hh"
#include "NOD_closure.hh"
#include "NOD_geometry.h"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_node_declaration.hh"
@ -144,6 +145,12 @@ static void add_used_ids_from_sockets(const ListBase &sockets, Set<ID *> &ids)
}
break;
}
case SOCK_FUNCTION: {
if (bNodeTree *ntree = ((bNodeSocketValueFunction *)socket->default_value)->value) {
ids.add(&ntree->id);
}
break;
}
}
}
}
@ -508,6 +515,11 @@ static std::unique_ptr<IDProperty, bke::idprop::IDPropertyDeleter> id_property_c
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
case SOCK_FUNCTION: {
const bNodeSocketValueFunction *value = static_cast<const bNodeSocketValueFunction *>(
socket.default_value);
return bke::idprop::create(socket.identifier, reinterpret_cast<ID *>(value->value));
}
}
return nullptr;
}
@ -532,6 +544,7 @@ static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDP
case SOCK_TEXTURE:
case SOCK_IMAGE:
case SOCK_MATERIAL:
case SOCK_FUNCTION:
return property.type == IDP_ID;
}
BLI_assert_unreachable();
@ -611,6 +624,14 @@ static void init_socket_cpp_value_from_property(const IDProperty &property,
*(Material **)r_value = material;
break;
}
case SOCK_FUNCTION: {
using Closure = blender::nodes::Closure;
ID *id = IDP_Id(&property);
bNodeTree *ntree = (id && GS(id->name) == ID_NT) ? (bNodeTree *)id : nullptr;
new (r_value) Closure(Closure::make_from_node_tree(ntree));
break;
}
default: {
BLI_assert_unreachable();
break;
@ -630,7 +651,7 @@ static void update_input_properties_from_node_tree(const bNodeTree &tree,
if (new_prop == nullptr) {
/* Out of the set of supported input sockets, only
* geometry sockets aren't added to the modifier. */
BLI_assert(socket.type == SOCK_GEOMETRY);
BLI_assert(ELEM(socket.type, SOCK_GEOMETRY, SOCK_FUNCTION));
continue;
}
@ -1792,6 +1813,10 @@ static void draw_property_for_socket(const bContext &C,
uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "images", socket.name, ICON_IMAGE);
break;
}
case SOCK_FUNCTION: {
uiItemPointerR(row, md_ptr, rna_path, bmain_ptr, "node_groups", socket.name, ICON_NODETREE);
break;
}
default: {
if (input_has_attribute_toggle(*nmd->node_group, socket_index)) {
add_attribute_search_or_value_buttons(C, row, *nmd, md_ptr, socket);

View File

@ -40,6 +40,7 @@ set(INC
set(SRC
intern/add_node_search.cc
intern/closure.cc
intern/derived_node_tree.cc
intern/geometry_nodes_lazy_function.cc
intern/geometry_nodes_log.cc
@ -56,6 +57,7 @@ set(SRC
intern/socket_search_link.cc
NOD_add_node_search.hh
NOD_closure.hh
NOD_common.h
NOD_composite.h
NOD_derived_node_tree.hh

View File

@ -0,0 +1,51 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup nodes
*/
#include "BLI_generic_pointer.hh"
struct bNodeTree;
namespace blender::nodes {
struct GeometryNodesLazyFunctionGraphInfo;
class ClosureInputValues {
private:
Vector<GMutablePointer> values_;
public:
ClosureInputValues(const Span<GMutablePointer> &values);
~ClosureInputValues();
Span<GMutablePointer> values() const;
MutableSpan<GMutablePointer> values();
};
class Closure {
private:
const GeometryNodesLazyFunctionGraphInfo *lf_graph_info_ = nullptr;
std::shared_ptr<ClosureInputValues> bound_values_;
public:
Closure();
Closure(const Closure &other);
explicit Closure(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
MutableSpan<GMutablePointer> &&bound_values);
~Closure() = default;
Closure &operator=(const Closure &other) = default;
const GeometryNodesLazyFunctionGraphInfo *lf_graph_info() const;
Span<GMutablePointer> bound_values() const;
MutableSpan<GMutablePointer> bound_values();
static Closure make_from_node_tree(const bNodeTree *node_tree);
};
} // namespace blender::nodes

View File

@ -29,10 +29,16 @@ struct bNodeSocket *node_group_output_find_socket(struct bNode *node, const char
namespace blender::nodes {
struct FieldInferencingInterface;
void node_group_declare_dynamic(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration);
void node_function_signature_declare(const bNodeFunctionSignature &sig,
const FieldInferencingInterface *field_interface,
NodeDeclaration &r_declaration);
} // namespace blender::nodes
#endif

View File

@ -753,4 +753,7 @@ inline Span<SocketDeclarationPtr> NodeDeclaration::sockets(eNodeSocketInOut in_o
/** \} */
SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree,
const bNodeSocket &io_socket);
} // namespace blender::nodes

View File

@ -22,6 +22,8 @@ struct bNodeTree;
extern "C" {
#endif
void node_type_draw_color(const char *idname, float *r_color);
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
@ -41,6 +43,8 @@ void register_standard_node_socket_types(void);
namespace blender::nodes {
void register_cpp_types();
void update_node_declaration_and_sockets(bNodeTree &ntree, bNode &node);
} // namespace blender::nodes

View File

@ -225,6 +225,13 @@ class Shader : public SocketDeclaration {
class ShaderBuilder : public SocketDeclarationBuilder<Shader> {
};
class Function : public IDSocketDeclaration {
public:
using Builder = SocketDeclarationBuilder<Function>;
Function();
};
class ExtendBuilder;
class Extend : public SocketDeclaration {
@ -401,6 +408,10 @@ inline Texture::Texture() : IDSocketDeclaration("NodeSocketTexture") {}
inline Image::Image() : IDSocketDeclaration("NodeSocketImage") {}
inline Function::Function() : IDSocketDeclaration("NodeSocketFunction")
{
}
/** \} */
SocketDeclarationPtr create_extend_declaration(const eNodeSocketInOut in_out);

View File

@ -282,6 +282,7 @@ DefNode(FunctionNode, FN_NODE_VALUE_TO_STRING, 0, "VALUE_TO_STRING", ValueToStri
DefNode(GeometryNode, GEO_NODE_ACCUMULATE_FIELD, def_geo_accumulate_field, "ACCUMULATE_FIELD", AccumulateField, "Accumulate Field", "Add the values of an evaluated field together and output the running total for each element")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_DOMAIN_SIZE, def_geo_attribute_domain_size, "ATTRIBUTE_DOMAIN_SIZE", AttributeDomainSize, "Domain Size", "Retrieve the number of elements in a geometry for each attribute domain")
DefNode(GeometryNode, GEO_NODE_ATTRIBUTE_STATISTIC, def_geo_attribute_statistic, "ATTRIBUTE_STATISTIC",AttributeStatistic, "Attribute Statistic","Calculate statistics about a data set from a field evaluated on a geometry")
DefNode(GeometryNode, GEO_NODE_BIND_FUNCTION, def_geo_bind_function, "BIND_FUNCTION", BindFunction, "Bind Function", "")
DefNode(GeometryNode, GEO_NODE_BLUR_ATTRIBUTE, def_geo_blur_attribute, "BLUR_ATTRIBUTE", BlurAttribute, "Blur Attribute", "Mix attribute values of neighboring elements")
DefNode(GeometryNode, GEO_NODE_BOUNDING_BOX, 0, "BOUNDING_BOX", BoundBox, "Bounding Box", "Calculate the limits of a geometry's positions and generate a box mesh with those dimensions")
DefNode(GeometryNode, GEO_NODE_CAPTURE_ATTRIBUTE, def_geo_attribute_capture,"CAPTURE_ATTRIBUTE", CaptureAttribute, "Capture Attribute", "Store the result of a field on a geometry and output the data as a node socket. Allows remembering or interpolating data as the geometry changes, such as positions before deformation")
@ -315,6 +316,7 @@ DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_CURVES, 0, "EDGE_PATHS_TO_CURVES",
DefNode(GeometryNode, GEO_NODE_EDGE_PATHS_TO_SELECTION, 0, "EDGE_PATHS_TO_SELECTION", EdgePathsToSelection, "Edge Paths to Selection", "")
DefNode(GeometryNode, GEO_NODE_EDGES_TO_FACE_GROUPS, 0, "EDGES_TO_FACE_GROUPS", EdgesToFaceGroups, "Edges to Face Groups", "Group faces into regions surrounded by the selected boundary edges")
DefNode(GeometryNode, GEO_NODE_EVALUATE_AT_INDEX, def_geo_evaluate_at_index, "FIELD_AT_INDEX", FieldAtIndex, "Evaluate at Index", "Retrieve data of other elements in the context's geometry")
DefNode(GeometryNode, GEO_NODE_EVALUATE_FUNCTION, def_geo_evaluate_function, "EVALUATE_FUNCTION", EvaluateFunction, "Evaluate Function", "")
DefNode(GeometryNode, GEO_NODE_EVALUATE_ON_DOMAIN, def_geo_evaluate_on_domain, "FIELD_ON_DOMAIN", FieldOnDomain, "Evaluate on Domain", "Retrieve values from a field on a different domain besides the domain from the context")
DefNode(GeometryNode, GEO_NODE_EXTRUDE_MESH, def_geo_extrude_mesh, "EXTRUDE_MESH", ExtrudeMesh, "Extrude Mesh", "Generate new vertices, edges, or faces from selected elements and move them based on an offset while keeping them connected by their boundary")
DefNode(GeometryNode, GEO_NODE_FILL_CURVE, def_geo_curve_fill, "FILL_CURVE", FillCurve, "Fill Curve", "Generate a mesh on the XY plane with faces on the inside of input curves")

View File

@ -29,6 +29,7 @@ set(SRC
nodes/node_geo_attribute_capture.cc
nodes/node_geo_attribute_domain_size.cc
nodes/node_geo_attribute_statistic.cc
nodes/node_geo_bind_function.cc
nodes/node_geo_blur_attribute.cc
nodes/node_geo_boolean.cc
nodes/node_geo_bounding_box.cc
@ -71,6 +72,7 @@ set(SRC
nodes/node_geo_edge_split.cc
nodes/node_geo_edges_to_face_groups.cc
nodes/node_geo_evaluate_at_index.cc
nodes/node_geo_evaluate_function.cc
nodes/node_geo_evaluate_on_domain.cc
nodes/node_geo_extrude_mesh.cc
nodes/node_geo_flip_faces.cc

View File

@ -14,6 +14,7 @@ void register_geometry_nodes()
register_node_type_geo_attribute_capture();
register_node_type_geo_attribute_domain_size();
register_node_type_geo_attribute_statistic();
register_node_type_geo_bind_function();
register_node_type_geo_blur_attribute();
register_node_type_geo_boolean();
register_node_type_geo_bounding_box();
@ -55,6 +56,7 @@ void register_geometry_nodes()
register_node_type_geo_edge_split();
register_node_type_geo_edges_to_face_groups();
register_node_type_geo_evaluate_at_index();
register_node_type_geo_evaluate_function();
register_node_type_geo_evaluate_on_domain();
register_node_type_geo_extrude_mesh();
register_node_type_geo_flip_faces();

View File

@ -11,6 +11,7 @@ void register_node_type_geo_attribute_capture();
void register_node_type_geo_attribute_domain_size();
void register_node_type_geo_attribute_separate_xyz();
void register_node_type_geo_attribute_statistic();
void register_node_type_geo_bind_function();
void register_node_type_geo_blur_attribute();
void register_node_type_geo_boolean();
void register_node_type_geo_bounding_box();
@ -52,6 +53,7 @@ void register_node_type_geo_edge_paths_to_selection();
void register_node_type_geo_edge_split();
void register_node_type_geo_edges_to_face_groups();
void register_node_type_geo_evaluate_at_index();
void register_node_type_geo_evaluate_function();
void register_node_type_geo_evaluate_on_domain();
void register_node_type_geo_extrude_mesh();
void register_node_type_geo_flip_faces();

View File

@ -103,7 +103,8 @@ static bool geometry_node_tree_socket_type_valid(bNodeTreeType * /*treetype*/,
SOCK_COLLECTION,
SOCK_TEXTURE,
SOCK_IMAGE,
SOCK_MATERIAL);
SOCK_MATERIAL,
SOCK_FUNCTION);
}
void register_node_tree_type_geo()

View File

@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_listbase.h"
#include "BKE_node_runtime.hh"
#include "FN_lazy_function_graph.hh"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_closure.hh"
#include "NOD_common.h"
#include "NOD_node_declaration.hh"
#include "node_common.h"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_bind_function_cc {
static void node_declare(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration)
{
NodeDeclarationBuilder builder(r_declaration);
builder.add_output<decl::Function>(N_("Function"));
const bNodeTree *group = reinterpret_cast<const bNodeTree *>(node.id);
if (!group) {
return;
}
if (ID_IS_LINKED(&group->id) && (group->id.tag & LIB_TAG_MISSING)) {
r_declaration.skip_updating_sockets = true;
return;
}
r_declaration.skip_updating_sockets = false;
LISTBASE_FOREACH (const bNodeSocket *, input, &group->inputs) {
SocketDeclarationPtr decl = declaration_for_interface_socket(node_tree, *input);
/* Need single values to bind. */
decl->input_field_type = InputSocketFieldType::None;
r_declaration.inputs.append(std::move(decl));
}
}
static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
uiTemplateIDBrowse(
layout, C, ptr, "node_tree", nullptr, nullptr, nullptr, UI_TEMPLATE_ID_FILTER_ALL, nullptr);
}
static void node_geo_exec(GeoNodeExecParams params)
{
if (params.node().id == nullptr) {
params.set_default_remaining_outputs();
return;
}
const bNodeTree &bind_tree = *reinterpret_cast<bNodeTree *>(params.node().id);
const GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
ensure_geometry_nodes_lazy_function_graph(bind_tree);
BLI_assert(lf_graph_info);
Array<GMutablePointer> bound_values(params.node().input_sockets().size());
for (const int i : params.node().input_sockets().index_range()) {
const bNodeSocket *socket = params.node().input_sockets()[i];
const CPPType *cpptype = socket->typeinfo->geometry_nodes_cpp_type;
BLI_assert(cpptype != nullptr);
void *bound_value_buffer = MEM_mallocN_aligned(
cpptype->size(), cpptype->alignment(), "function bound value");
switch (socket->type) {
case SOCK_FLOAT: {
ValueOrField<float> value = params.get_input<float>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_VECTOR: {
ValueOrField<float3> value = params.get_input<float3>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_RGBA: {
ValueOrField<ColorGeometry4f> value = params.get_input<ColorGeometry4f>(
socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_BOOLEAN: {
ValueOrField<bool> value = params.get_input<bool>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_INT: {
ValueOrField<int> value = params.get_input<int>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_STRING: {
ValueOrField<std::string> value = params.get_input<std::string>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_OBJECT: {
Object *value = params.get_input<Object *>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_IMAGE: {
Image *value = params.get_input<Image *>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_GEOMETRY: {
GeometrySet value = params.get_input<GeometrySet>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_COLLECTION: {
Collection *value = params.get_input<Collection *>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_TEXTURE: {
Tex *value = params.get_input<Tex *>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_MATERIAL: {
Material *value = params.get_input<Material *>(socket->identifier);
cpptype->move_construct(&value, bound_value_buffer);
break;
}
case SOCK_FUNCTION:
BLI_assert_unreachable();
}
bound_values[i] = {cpptype, bound_value_buffer};
}
Closure closure(*lf_graph_info, bound_values);
params.set_output("Function", std::move(closure));
params.set_default_remaining_outputs();
}
} // namespace blender::nodes::node_geo_bind_function_cc
void register_node_type_geo_bind_function()
{
namespace file_ns = blender::nodes::node_geo_bind_function_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_BIND_FUNCTION, "Bind Function", NODE_CLASS_GROUP);
ntype.declare_dynamic = file_ns::node_declare;
ntype.poll_instance = node_group_poll_instance;
ntype.draw_buttons = file_ns::node_layout;
ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "RNA_enum_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "NOD_closure.hh"
#include "NOD_common.h"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_evaluate_function_cc {
NODE_STORAGE_FUNCS(NodeGeometryEvaluateFunction)
static void node_declare(const bNodeTree &node_tree,
const bNode &node,
NodeDeclaration &r_declaration)
{
/* May be called before storage is initialized. */
if (node.storage == nullptr) {
return;
}
const NodeGeometryEvaluateFunction &storage = node_storage(node);
NodeDeclarationBuilder builder(r_declaration);
builder.add_input<decl::Function>(N_("Function"));
/* TODO define FieldInferencingInterface for this node */
node_function_signature_declare(storage.signature, nullptr, r_declaration);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeGeometryEvaluateFunction *data = MEM_cnew<NodeGeometryEvaluateFunction>(__func__);
node->storage = data;
}
static void node_update(bNodeTree *ntree, bNode *node)
{
}
static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
{
}
static void node_geo_exec(GeoNodeExecParams params)
{
params.set_default_remaining_outputs();
}
} // namespace blender::nodes::node_geo_evaluate_function_cc
void register_node_type_geo_evaluate_function()
{
namespace file_ns = blender::nodes::node_geo_evaluate_function_cc;
static bNodeType ntype;
geo_node_type_base(&ntype, GEO_NODE_EVALUATE_FUNCTION, "Evaluate Function", NODE_CLASS_GROUP);
ntype.declare_dynamic = file_ns::node_declare;
ntype.draw_buttons = file_ns::node_layout;
ntype.initfunc = file_ns::node_init;
ntype.updatefunc = file_ns::node_update;
node_type_storage(
&ntype, "NodeGeometryEvaluateFunction", node_free_standard_storage, node_copy_standard_storage);
ntype.geometry_node_execute = file_ns::node_geo_exec;
nodeRegisterType(&ntype);
}

View File

@ -0,0 +1,94 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup fn
*/
#include "NOD_closure.hh"
#include "DNA_node_types.h"
#include "BKE_node_runtime.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
namespace blender::nodes {
ClosureInputValues::ClosureInputValues(const Span<GMutablePointer> &values) : values_(values) {}
ClosureInputValues::~ClosureInputValues()
{
for (GMutablePointer &ptr : values_) {
if (ptr.get()) {
ptr.destruct();
MEM_freeN(ptr.get());
}
}
}
Span<GMutablePointer> ClosureInputValues::values() const
{
return values_;
}
MutableSpan<GMutablePointer> ClosureInputValues::values()
{
return values_;
}
Closure::Closure() : bound_values_(nullptr) {}
Closure::Closure(const Closure &other)
: lf_graph_info_(other.lf_graph_info_), bound_values_(other.bound_values_)
{
}
Closure::Closure(const GeometryNodesLazyFunctionGraphInfo &lf_graph_info,
MutableSpan<GMutablePointer> &&bound_values)
: lf_graph_info_(&lf_graph_info),
bound_values_(std::make_shared<ClosureInputValues>(bound_values))
{
}
const GeometryNodesLazyFunctionGraphInfo *Closure::lf_graph_info() const
{
return lf_graph_info_;
}
Span<GMutablePointer> Closure::bound_values() const
{
return bound_values_->values();
}
MutableSpan<GMutablePointer> Closure::bound_values()
{
return bound_values_->values();
}
Closure Closure ::make_from_node_tree(const bNodeTree *node_tree)
{
if (node_tree == nullptr) {
return Closure();
}
BLI_assert(node_tree->runtime);
const blender::nodes::GeometryNodesLazyFunctionGraphInfo *lf_graph_info =
ensure_geometry_nodes_lazy_function_graph(*node_tree);
BLI_assert(lf_graph_info);
Array<GMutablePointer> bound_values(node_tree->interface_inputs().size());
for (const int i : node_tree->interface_inputs().index_range()) {
const bNodeSocket *socket = node_tree->interface_inputs()[i];
const CPPType *cpptype = socket->typeinfo->geometry_nodes_cpp_type;
if (cpptype && socket->typeinfo->get_geometry_nodes_cpp_value) {
void *buffer = MEM_mallocN_aligned(
cpptype->size(), cpptype->alignment(), "default graph input buffer");
socket->typeinfo->get_geometry_nodes_cpp_value(*socket, buffer);
bound_values[i] = {cpptype, buffer};
}
}
return Closure(*lf_graph_info, bound_values);
}
} // namespace blender::nodes

View File

@ -13,6 +13,7 @@
* complexity. So far, this does not seem to be a performance issue.
*/
#include "NOD_closure.hh"
#include "NOD_geometry_exec.hh"
#include "NOD_geometry_nodes_lazy_function.hh"
#include "NOD_multi_function.hh"
@ -34,6 +35,7 @@
#include "BKE_type_conversions.hh"
#include "FN_field_cpp_type.hh"
#include "FN_lazy_function_execute.hh"
#include "FN_lazy_function_graph_executor.hh"
#include "DEG_depsgraph_query.h"
@ -906,7 +908,6 @@ class LazyFunctionForGroupNode : public LazyFunction {
group_lf_graph_info.mapping.group_output_used_sockets[i]);
inputs_.append_as("Output is Used", CPPType::get<bool>(), lf::ValueUsage::Maybe);
}
graph_inputs.extend(group_lf_graph_info.mapping.group_output_used_sockets);
/* Add an attribute set input for every output geometry socket that can propagate attributes
* from inputs. */
@ -1151,6 +1152,458 @@ class LazyFunctionForSwitchSocketUsage : public lf::LazyFunction {
}
};
/* Specialized Params class that passes parameters to a graph executor
* and communicates input requests back to the evaluation node. */
class EvaluateFunctionNodeParams final : public lf::Params {
private:
Params &outer_params_;
/* Index of outer parameter used for a groph parameter,
* or -1 if the bound value must be used instead. */
const Span<int> outer_input_indices_;
const Span<int> outer_output_indices_;
const Span<GMutablePointer> bound_inputs_;
const Span<GMutablePointer> bound_outputs_;
MutableSpan<std::optional<lf::ValueUsage>> bound_input_usages_;
Span<lf::ValueUsage> bound_output_usages_;
MutableSpan<bool> set_bound_outputs_;
public:
EvaluateFunctionNodeParams(const lf::LazyFunction &fn,
const Span<GMutablePointer> bound_inputs,
const Span<GMutablePointer> bound_outputs,
MutableSpan<std::optional<lf::ValueUsage>> bound_input_usages,
Span<lf::ValueUsage> bound_output_usages,
MutableSpan<bool> set_bound_outputs,
Params &outer_params,
Span<int> outer_input_indices,
Span<int> outer_output_indices)
: lf::Params(fn, outer_params.allow_multi_threading_),
outer_params_(outer_params),
outer_input_indices_(outer_input_indices),
outer_output_indices_(outer_output_indices),
bound_inputs_(bound_inputs),
bound_outputs_(bound_outputs),
bound_input_usages_(bound_input_usages),
bound_output_usages_(bound_output_usages),
set_bound_outputs_(set_bound_outputs)
{
}
private:
void *try_get_input_data_ptr_impl(const int index) const override
{
const int outer_index = outer_input_indices_[index];
if (outer_index >= 0) {
return outer_params_.try_get_input_data_ptr(outer_index);
}
else {
return bound_inputs_[index].get();
}
}
void *try_get_input_data_ptr_or_request_impl(const int index) override
{
const int outer_index = outer_input_indices_[index];
if (outer_index >= 0) {
return outer_params_.try_get_input_data_ptr_or_request(outer_index);
}
else {
void *value = bound_inputs_[index].get();
if (value == nullptr) {
bound_input_usages_[index] = lf::ValueUsage::Used;
}
return value;
}
}
void *get_output_data_ptr_impl(const int index) override
{
const int outer_index = outer_output_indices_[index];
if (outer_index >= 0) {
return outer_params_.get_output_data_ptr(outer_index);
}
else {
return bound_outputs_[index].get();
}
}
void output_set_impl(const int index) override
{
const int outer_index = outer_output_indices_[index];
if (outer_index >= 0) {
return outer_params_.output_set(outer_index);
}
else {
set_bound_outputs_[index] = true;
}
}
bool output_was_set_impl(const int index) const override
{
const int outer_index = outer_output_indices_[index];
if (outer_index >= 0) {
return outer_params_.output_was_set(outer_index);
}
else {
return set_bound_outputs_[index];
}
}
lf::ValueUsage get_output_usage_impl(const int index) const override
{
const int outer_index = outer_output_indices_[index];
if (outer_index >= 0) {
return outer_params_.get_output_usage(outer_index);
}
else {
return bound_output_usages_[index];
}
}
void set_input_unused_impl(const int index) override
{
const int outer_index = outer_input_indices_[index];
if (outer_index >= 0) {
return outer_params_.set_input_unused(outer_index);
}
else {
bound_input_usages_[index] = lf::ValueUsage::Unused;
}
}
bool try_enable_multi_threading_impl() override
{
return outer_params_.try_enable_multi_threading();
}
};
/**
* Evaluates the function input and then executes it.
*/
class LazyFunctionForEvaluateFunctionNode : public LazyFunction {
private:
const bNode &eval_node_;
// bool has_many_nodes_ = false;
public:
LazyFunctionForEvaluateFunctionNode(const bNode &eval_node,
GeometryNodesLazyFunctionGraphInfo &own_lf_graph_info)
: eval_node_(eval_node)
{
debug_name_ = eval_node.name;
allow_missing_requested_inputs_ = true;
lazy_function_interface_from_node(
eval_node, inputs_, outputs_, own_lf_graph_info.mapping.lf_index_by_bsocket);
for (lf::Input &input : inputs_) {
input.usage = lf::ValueUsage::Maybe;
}
/* Add a boolean input for every output bsocket that indicates whether that socket is used. */
for (const int i : eval_node.output_sockets().index_range()) {
own_lf_graph_info.mapping.lf_input_index_for_output_bsocket_usage
[eval_node.output_socket(i).index_in_all_outputs()] = inputs_.append_and_get_index_as(
"Output is Used", CPPType::get<bool>(), lf::ValueUsage::Maybe);
}
/* Add an attribute set input for every output geometry socket that can propagate attributes
* from inputs. */
for (const int i : eval_node.output_sockets().index_range()) {
own_lf_graph_info.mapping.lf_input_index_for_attribute_propagation_to_output
[eval_node.output_socket(i).index_in_all_outputs()] = inputs_.append_and_get_index_as(
"Attribute Set", CPPType::get<bke::AnonymousAttributeSet>(), lf::ValueUsage::Maybe);
}
/* Add a boolean output for every input bsocket that indicates whether that socket is used. */
for (const int i : eval_node.input_sockets().index_range()) {
/* Treat all sockets as dynamic, i.e. their usage is undetermined at this point
* and a usage output is added. */
outputs_.append_as("Input is Used", CPPType::get<bool>());
UNUSED_VARS(i);
}
}
void execute_impl(lf::Params &params, const lf::Context &context) const override
{
GeoNodesLFUserData *user_data = dynamic_cast<GeoNodesLFUserData *>(context.user_data);
BLI_assert(user_data != nullptr);
geo_eval_log::GeoTreeLogger *tree_logger =
(user_data->modifier_data->eval_log != nullptr) ?
&user_data->modifier_data->eval_log->get_local_tree_logger(
*user_data->compute_context) :
nullptr;
Closure *closure = params.try_get_input_data_ptr_or_request<Closure>(0);
if (!closure) {
/* Wait for closure request. */
return;
}
if (!closure->lf_graph_info()) {
if (tree_logger) {
tree_logger->node_warnings.append(
{eval_node_.identifier,
{NodeWarningType::Warning,
tree_logger->allocator->copy_string("Function graph undefined")}});
}
params.set_default_remaining_outputs();
return;
}
const GeometryNodesLazyFunctionGraphInfo &lf_graph_info = *closure->lf_graph_info();
// has_many_nodes_ = lf_graph_info.num_inline_nodes_approximate > 1000;
/* There is a bound value for each regular graph input. */
const IndexRange input_values_range{0, lf_graph_info.mapping.group_input_sockets.size()};
/* There is a bound value for each "output used" flag. */
const IndexRange output_used_range{lf_graph_info.mapping.group_input_sockets.size(),
lf_graph_info.mapping.group_output_used_sockets.size()};
/* There is a bound value for each output attribute set. */
const IndexRange attribute_set_range(
lf_graph_info.mapping.group_output_used_sockets.size(),
lf_graph_info.mapping.attribute_set_by_geometry_output.size());
const IndexRange output_values_range{
0, lf_graph_info.mapping.standard_group_output_sockets.size()};
const IndexRange input_usage_range{lf_graph_info.mapping.standard_group_output_sockets.size(),
lf_graph_info.mapping.group_input_usage_sockets.size()};
/* We make a few assumptions below about the size of these ranges. */
BLI_assert(closure->bound_values().size() == input_values_range.size());
BLI_assert(lf_graph_info.mapping.group_input_usage_sockets.size() ==
input_values_range.size());
Vector<const lf::OutputSocket *> graph_inputs;
/* Add inputs that also exist on the bnode. */
graph_inputs.extend(lf_graph_info.mapping.group_input_sockets);
/* Add a boolean input for every output bsocket that indicates whether that socket is
* used. */
graph_inputs.extend(lf_graph_info.mapping.group_output_used_sockets);
/* Add an attribute set input for every output geometry socket that can propagate attributes
* from inputs. */
for (const lf::OutputSocket *lf_socket :
lf_graph_info.mapping.attribute_set_by_geometry_output.values())
{
graph_inputs.append(lf_socket);
}
Vector<const lf::InputSocket *> graph_outputs;
/* Add outputs that also exist on the bnode. */
graph_outputs.extend(lf_graph_info.mapping.standard_group_output_sockets);
/* Add a boolean output for every input bsocket that indicates whether that socket is used. */
graph_outputs.extend(lf_graph_info.mapping.group_input_usage_sockets);
GeometryNodesLazyFunctionLogger lf_logger(lf_graph_info);
GeometryNodesLazyFunctionSideEffectProvider lf_side_effect_provider;
lf::GraphExecutor graph_executor(lf_graph_info.graph,
std::move(graph_inputs),
std::move(graph_outputs),
&lf_logger,
&lf_side_effect_provider);
// if (has_many_nodes_) {
// /* If the called node group has many nodes, it's likely that executing it takes a while
// * even if every individual node is very small. */
// lazy_threading::send_hint();
// }
Array<GMutablePointer> bound_inputs(graph_inputs.size());
bound_inputs.as_mutable_span().slice(input_values_range).copy_from(closure->bound_values());
Array<bool> bound_input_for_output_used(output_used_range.size(), false);
for (const int i : bound_input_for_output_used.index_range()) {
bound_inputs[output_used_range[i]] = {CPPType::get<bool>(), &bound_input_for_output_used[i]};
}
Array<bke::AnonymousAttributeSet> bound_input_for_attribute_set(attribute_set_range.size());
for (const int i : bound_input_for_attribute_set.index_range()) {
bound_inputs[attribute_set_range[i]] = {CPPType::get<bke::AnonymousAttributeSet>(),
&bound_input_for_attribute_set[i]};
}
/* These are written to if the graph outputs a value that is not captured by the evaluation
* node. */
Array<GMutablePointer> unused_outputs(graph_outputs.size());
for (const int i : unused_outputs.index_range()) {
const CPPType &cpptype = graph_outputs[i]->type();
void *buffer = MEM_mallocN_aligned(
cpptype.size(), cpptype.alignment(), "unused graph output buffer");
unused_outputs[i] = {&cpptype, buffer};
}
Array<std::optional<lf::ValueUsage>> bound_input_usages(graph_inputs.size());
Array<lf::ValueUsage> bound_output_usages(graph_outputs.size(), lf::ValueUsage::Used);
Array<bool> bound_set_outputs(graph_outputs.size(), false);
Array<int> outer_input_indices(graph_inputs.size(), -1);
Array<int> outer_output_indices(graph_outputs.size(), -1);
/* Find a socket in the evaluation node that matches the graph socket. */
auto find_matching_socket = [](const Span<const bNodeSocket *> &sockets,
const Span<int> matched_sockets,
const StringRef name,
const CPPType &type) -> int {
BLI_assert(matched_sockets.size() == sockets.index_range().one_after_last());
for (const int socket_i : sockets.index_range()) {
const bNodeSocket *socket = sockets[socket_i];
BLI_assert(socket->typeinfo->geometry_nodes_cpp_type != nullptr);
/* Skip sockets that have been matched already. */
if (matched_sockets[socket_i] >= 0) {
continue;
}
if (socket->name == name && *socket->typeinfo->geometry_nodes_cpp_type == type) {
/* Index relative to the socket list (ignore the Function input socket). */
return socket_i - sockets.index_range().start();
}
}
return -1;
};
Array<int> matched_inputs(eval_node_.input_sockets().size(), -1);
for (const int i : lf_graph_info.mapping.group_input_sockets.index_range()) {
const lf::OutputSocket *graph_input = lf_graph_info.mapping.group_input_sockets[i];
/* Drop the Function output socket at the start of the list. */
const int matching_socket_index = find_matching_socket(
eval_node_.input_sockets().drop_front(1),
matched_inputs,
graph_input->name(),
graph_input->type());
outer_input_indices[input_values_range[i]] = matching_socket_index;
}
Array<int> matched_outputs(eval_node_.output_sockets().size(), -1);
for (const int i : lf_graph_info.mapping.standard_group_output_sockets.index_range()) {
const lf::InputSocket *graph_output = lf_graph_info.mapping.standard_group_output_sockets[i];
const int matching_socket_index = find_matching_socket(eval_node_.output_sockets(),
matched_outputs,
graph_output->name(),
graph_output->type());
outer_output_indices[output_values_range[i]] = matching_socket_index;
}
for (const int i : lf_graph_info.mapping.group_output_used_sockets.index_range()) {
outer_input_indices[output_used_range[i]] =
outer_output_indices[i] >= 0 ?
lf_graph_info.mapping.group_input_sockets.size() + outer_output_indices[i] :
-1;
}
for (const int i : lf_graph_info.mapping.group_input_usage_sockets.index_range()) {
outer_output_indices[input_usage_range[i]] =
outer_input_indices[i] >= 0 ?
lf_graph_info.mapping.standard_group_output_sockets.size() + outer_input_indices[i] :
-1;
}
// XXX TODO
// {
// int i = 0;
// for (auto [output_index, lf_socket] :
// lf_graph_info.mapping.attribute_set_by_geometry_output.items())
// {
// const int lf_socket_index = lf_graph_info.mapping.group_
// outer_input_indices[attribute_set_range[i]] = ;
// ++i;
// }
// }
/* If any output is not mapped the graph is considered invalid. */
for (const int matched_graph_output : matched_outputs) {
if (matched_graph_output < 0) {
if (tree_logger) {
tree_logger->node_warnings.append(
{eval_node_.identifier,
{NodeWarningType::Warning,
tree_logger->allocator->copy_string("Missing output in graph")}});
}
params.set_default_remaining_outputs();
return;
}
}
LinearAllocator allocator;
void *graph_executor_storage = graph_executor.init_storage(allocator);
/* The compute context changes when entering a node group. */
std::optional<ComputeContextHash> context_hash_cache;
bke::NodeGroupComputeContext compute_context{
user_data->compute_context, eval_node_.identifier, context_hash_cache};
context_hash_cache = compute_context.hash();
GeoNodesLFUserData func_user_data = *user_data;
func_user_data.compute_context = &compute_context;
if (user_data->modifier_data->socket_log_contexts) {
func_user_data.log_socket_values = user_data->modifier_data->socket_log_contexts->contains(
compute_context.hash());
}
lf::Context func_context = context;
func_context.user_data = &func_user_data;
func_context.storage = graph_executor_storage;
EvaluateFunctionNodeParams func_params{graph_executor,
bound_inputs,
unused_outputs,
bound_input_usages,
bound_output_usages,
bound_set_outputs,
params,
outer_input_indices,
outer_output_indices};
graph_executor.execute(func_params, func_context);
}
// void *init_storage(LinearAllocator<> &allocator) const override
// {
// Storage *s = allocator.construct<Storage>().release();
// s->graph_executor_storage = graph_executor_->init_storage(allocator);
// return s;
// }
// void destruct_storage(void *storage) const override
// {
// Storage *s = static_cast<Storage *>(storage);
// graph_executor_->destruct_storage(s->graph_executor_storage);
// std::destroy_at(s);
// }
// std::string name() const override
// {
// std::stringstream ss;
// ss << "Group '" << (group_node_.id->name + 2) << "' (" << group_node_.name << ")";
// return ss.str();
// }
// std::string input_name(const int i) const override
// {
// if (i < group_node_.input_sockets().size()) {
// return group_node_.input_socket(i).name;
// }
// for (const auto [bsocket_index, lf_socket_index] :
// lf_input_for_output_bsocket_usage_.items()) {
// if (i == lf_socket_index) {
// std::stringstream ss;
// ss << "'" << group_node_.output_socket(bsocket_index).name << "' output is used";
// return ss.str();
// }
// }
// for (const auto [bsocket_index, lf_index] :
// lf_input_for_attribute_propagation_to_output_.items()) {
// if (i == lf_index) {
// std::stringstream ss;
// ss << "Propagate to '" << group_node_.output_socket(bsocket_index).name << "'";
// return ss.str();
// }
// }
// return inputs_[i].debug_name;
// }
// std::string output_name(const int i) const override
// {
// if (i < group_node_.output_sockets().size()) {
// return group_node_.output_socket(i).name;
// }
// for (const auto [bsocket_index, lf_socket_index] :
// lf_output_for_input_bsocket_usage_.items()) {
// if (i == lf_socket_index) {
// std::stringstream ss;
// ss << "'" << group_node_.input_socket(bsocket_index).name << "' input is used";
// return ss.str();
// }
// }
// return outputs_[i].debug_name;
// }
};
/**
* Takes a field as input and extracts the set of anonymous attributes that it references.
*/
@ -1369,8 +1822,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
*/
Map<const bNodeSocket *, lf::InputSocket *> attribute_set_propagation_map_;
/**
* Boolean inputs that tell a node if some socket (of the same or another node) is used. If this
* socket is in a link-cycle, its input can become a constant true.
* Boolean inputs that tell a node if some socket (of the same or another node) is used. If
* this socket is in a link-cycle, its input can become a constant true.
*/
Set<const lf::InputSocket *> socket_usage_inputs_;
@ -1532,6 +1985,14 @@ struct GeometryNodesLazyFunctionGraphBuilder {
this->handle_switch_node(*bnode);
break;
}
case GEO_NODE_BIND_FUNCTION: {
this->handle_bind_function_node(*bnode);
break;
}
case GEO_NODE_EVALUATE_FUNCTION: {
this->handle_evaluate_function_node(*bnode);
break;
}
default: {
if (node_type->geometry_node_execute) {
this->handle_geometry_node(*bnode);
@ -1884,6 +2345,63 @@ struct GeometryNodesLazyFunctionGraphBuilder {
}
}
void handle_bind_function_node(const bNode &bnode)
{
const bNodeTree *group_btree = reinterpret_cast<bNodeTree *>(bnode.id);
if (group_btree == nullptr) {
return;
}
/* Tree graph is needed by the bind node. */
ensure_geometry_nodes_lazy_function_graph(*group_btree);
/* Continue as regular node. */
handle_geometry_node(bnode);
}
void handle_evaluate_function_node(const bNode &bnode)
{
auto lazy_function = std::make_unique<LazyFunctionForEvaluateFunctionNode>(bnode,
*lf_graph_info_);
lf::FunctionNode &lf_node = lf_graph_->add_function(*lazy_function);
for (const int i : bnode.input_sockets().index_range()) {
const bNodeSocket &bsocket = bnode.input_socket(i);
BLI_assert(!bsocket.is_multi_input());
lf::InputSocket &lf_socket = lf_node.input(i);
input_socket_map_.add(&bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
}
for (const int i : bnode.output_sockets().index_range()) {
const bNodeSocket &bsocket = bnode.output_socket(i);
lf::OutputSocket &lf_socket = lf_node.output(i);
output_socket_map_.add_new(&bsocket, &lf_socket);
mapping_->bsockets_by_lf_socket_map.add(&lf_socket, &bsocket);
}
static const bool static_false = false;
for (const bNodeSocket *bsocket : bnode.output_sockets()) {
{
const int lf_input_index =
mapping_->lf_input_index_for_output_bsocket_usage[bsocket->index_in_all_outputs()];
if (lf_input_index != -1) {
lf::InputSocket &lf_input = lf_node.input(lf_input_index);
lf_input.set_default_value(&static_false);
socket_usage_inputs_.add(&lf_input);
}
}
{
/* Keep track of attribute set inputs that need to be populated later. */
const int lf_input_index = mapping_->lf_input_index_for_attribute_propagation_to_output
[bsocket->index_in_all_outputs()];
if (lf_input_index != -1) {
lf::InputSocket &lf_input = lf_node.input(lf_input_index);
attribute_set_propagation_map_.add(bsocket, &lf_input);
}
}
}
lf_graph_info_->functions.append(std::move(lazy_function));
}
void handle_undefined_node(const bNode &bnode)
{
auto lazy_function = std::make_unique<LazyFunctionForUndefinedNode>(
@ -2090,8 +2608,9 @@ struct GeometryNodesLazyFunctionGraphBuilder {
}
/**
* Every output geometry socket that may propagate attributes has to know which attributes should
* be propagated. Therefore, every one of these outputs gets a corresponding attribute set input.
* Every output geometry socket that may propagate attributes has to know which attributes
* should be propagated. Therefore, every one of these outputs gets a corresponding attribute
* set input.
*/
void build_attribute_propagation_input_node()
{
@ -2447,7 +2966,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
Vector<lf::OutputSocket *> output_usages;
for (const int i : input_usage_hint.output_dependencies) {
if (lf::OutputSocket *lf_socket =
socket_is_used_map_[bnode.output_socket(i).index_in_tree()]) {
socket_is_used_map_[bnode.output_socket(i).index_in_tree()])
{
output_usages.append(lf_socket);
}
}
@ -2470,7 +2990,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
BLI_assert(lf_input_index >= 0);
lf::InputSocket &lf_socket = lf_group_node.input(lf_input_index);
if (lf::OutputSocket *lf_output_is_used =
socket_is_used_map_[output_bsocket->index_in_tree()]) {
socket_is_used_map_[output_bsocket->index_in_tree()])
{
lf_graph_->add_link(*lf_output_is_used, lf_socket);
}
else {
@ -2577,8 +3098,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
const int sockets_num = btree_.all_sockets().size();
const int attribute_references_num = attribute_reference_keys.size();
/* The code below uses #BitGroupVector to store a set of attribute references per socket. Each
* socket has a bit span where each bit corresponds to one attribute reference. */
/* The code below uses #BitGroupVector to store a set of attribute references per socket.
* Each socket has a bit span where each bit corresponds to one attribute reference. */
BitGroupVector<> referenced_by_field_socket(sockets_num, attribute_references_num, false);
BitGroupVector<> propagated_to_geometry_socket(sockets_num, attribute_references_num, false);
this->gather_referenced_and_potentially_propagated_data(relations_by_node,
@ -2933,15 +3454,15 @@ struct GeometryNodesLazyFunctionGraphBuilder {
}
/**
* By depending on "the future" (whether a specific socket is used in the future), it is possible
* to introduce cycles in the graph. This function finds those cycles and breaks them by removing
* specific links.
* By depending on "the future" (whether a specific socket is used in the future), it is
* possible to introduce cycles in the graph. This function finds those cycles and breaks them
* by removing specific links.
*
* Example for a cycle: There is a `Distribute Points on Faces` node and its `Normal` output is
* only used when the number of generated points is larger than 1000 because of some switch node
* later in the tree. In this case, to know whether the `Normal` output is needed, one first has
* to compute the points, but for that one has to know whether the normal information has to be
* added to the points. The fix is to always add the normal information in this case.
* only used when the number of generated points is larger than 1000 because of some switch
* node later in the tree. In this case, to know whether the `Normal` output is needed, one
* first has to compute the points, but for that one has to know whether the normal information
* has to be added to the points. The fix is to always add the normal information in this case.
*/
void fix_link_cycles()
{
@ -3012,8 +3533,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
* computation later, but does not change correctness.
*
* After the cycle is broken, the cycle-detection is "rolled back" to the socket where
* the first socket of the cycle was found. This is necessary in case another cycle goes
* through this socket. */
* the first socket of the cycle was found. This is necessary in case another cycle
* goes through this socket. */
detected_cycle = true;
const int index_in_socket_stack = lf_socket_stack.first_index_of(lf_origin_socket);
@ -3025,7 +3546,8 @@ struct GeometryNodesLazyFunctionGraphBuilder {
bool broke_cycle = false;
for (lf::Socket *lf_cycle_socket : cycle) {
if (lf_cycle_socket->is_input() &&
socket_usage_inputs_.contains(&lf_cycle_socket->as_input())) {
socket_usage_inputs_.contains(&lf_cycle_socket->as_input()))
{
lf::InputSocket &lf_cycle_input_socket = lf_cycle_socket->as_input();
lf_graph_->clear_origin(lf_cycle_input_socket);
static const bool static_true = true;

View File

@ -162,8 +162,8 @@ static std::function<ID *(const bNode &node)> get_default_id_getter(const bNodeT
};
}
static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree,
const bNodeSocket &io_socket)
SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &ntree,
const bNodeSocket &io_socket)
{
SocketDeclarationPtr dst;
switch (io_socket.type) {
@ -256,6 +256,12 @@ static SocketDeclarationPtr declaration_for_interface_socket(const bNodeTree &nt
dst = std::move(value);
break;
}
case SOCK_FUNCTION: {
auto value = std::make_unique<decl::Function>();
value->default_value_fn = get_default_id_getter(ntree, io_socket);
dst = std::move(value);
break;
}
case SOCK_CUSTOM:
std::unique_ptr<decl::Custom> decl = std::make_unique<decl::Custom>();
decl->idname_ = io_socket.idname;
@ -293,6 +299,87 @@ void node_group_declare_dynamic(const bNodeTree & /*node_tree*/,
}
}
SocketDeclarationPtr declaration_for_signature_parameter(const bNodeFunctionParameter &param,
eNodeSocketInOut in_out)
{
SocketDeclarationPtr dst;
switch (param.socket_type) {
case SOCK_FLOAT:
dst = std::make_unique<decl::Float>();
break;
case SOCK_VECTOR:
dst = std::make_unique<decl::Vector>();
break;
case SOCK_RGBA:
dst = std::make_unique<decl::Color>();
break;
case SOCK_SHADER:
dst = std::make_unique<decl::Shader>();
break;
case SOCK_BOOLEAN:
dst = std::make_unique<decl::Bool>();
break;
case SOCK_INT:
dst = std::make_unique<decl::Int>();
break;
case SOCK_STRING:
dst = std::make_unique<decl::String>();
break;
case SOCK_OBJECT:
dst = std::make_unique<decl::Object>();
break;
case SOCK_IMAGE:
dst = std::make_unique<decl::Image>();
break;
case SOCK_GEOMETRY:
dst = std::make_unique<decl::Geometry>();
break;
case SOCK_COLLECTION:
dst = std::make_unique<decl::Collection>();
break;
case SOCK_TEXTURE:
dst = std::make_unique<decl::Texture>();
break;
case SOCK_MATERIAL:
dst = std::make_unique<decl::Material>();
break;
case SOCK_FUNCTION:
dst = std::make_unique<decl::Function>();
break;
case SOCK_CUSTOM:
dst = std::make_unique<decl::Custom>();
break;
}
dst->name = param.name ? param.name : "";
char buf[MAX_NAME];
dst->identifier = BLI_snprintf(buf, sizeof(buf), "Item_%d", param.identifier);
dst->in_out = in_out;
dst->hide_value = true;
dst->compact = false;
return dst;
}
void node_function_signature_declare(const bNodeFunctionSignature &sig,
const FieldInferencingInterface *field_interface,
NodeDeclaration &r_declaration)
{
for (const int socket_i : IndexRange(sig.inputs_num)) {
SocketDeclarationPtr decl = declaration_for_signature_parameter(sig.inputs[socket_i], SOCK_IN);
if (field_interface) {
decl->input_field_type = field_interface->inputs[socket_i];
}
r_declaration.inputs.append(std::move(decl));
}
for (const int socket_i : IndexRange(sig.outputs_num)) {
SocketDeclarationPtr decl = declaration_for_signature_parameter(sig.outputs[socket_i],
SOCK_OUT);
if (field_interface) {
decl->output_field_dependency = field_interface->outputs[socket_i];
}
r_declaration.outputs.append(std::move(decl));
}
}
} // namespace blender::nodes
/** \} */
@ -460,7 +547,8 @@ bool blender::bke::node_is_connected_to_output(const bNodeTree *ntree, const bNo
for (const bNodeSocket *socket : next_node->output_sockets()) {
for (const bNodeLink *link : socket->directly_linked_links()) {
if (link->tonode->typeinfo->nclass == NODE_CLASS_OUTPUT &&
link->tonode->flag & NODE_DO_OUTPUT) {
link->tonode->flag & NODE_DO_OUTPUT)
{
return true;
}
nodes_to_check.push(link->tonode);

View File

@ -10,6 +10,7 @@
#include "DNA_node_types.h"
#include "BLI_color.hh"
#include "BLI_cpp_type_make.hh"
#include "BLI_listbase.h"
#include "BLI_math_vector.h"
#include "BLI_math_vector_types.hh"
@ -30,6 +31,7 @@
#include "MEM_guardedalloc.h"
#include "NOD_closure.hh"
#include "NOD_node_declaration.hh"
#include "NOD_socket.h"
@ -39,6 +41,13 @@ using namespace blender;
using blender::fn::ValueOrField;
using blender::nodes::SocketDeclarationPtr;
extern "C" void ED_node_type_draw_color(const char *idname, float *r_color);
void node_type_draw_color(const char *idname, float *r_color)
{
ED_node_type_draw_color(idname, r_color);
}
struct bNodeSocket *node_add_socket_from_template(struct bNodeTree *ntree,
struct bNode *node,
struct bNodeSocketTemplate *stemp,
@ -173,8 +182,15 @@ static void verify_socket_template_list(bNodeTree *ntree,
}
}
BLI_CPP_TYPE_MAKE(blender::nodes::Closure, CPPTypeFlags::None);
namespace blender::nodes {
void register_cpp_types()
{
BLI_CPP_TYPE_REGISTER(blender::nodes::Closure);
}
static void refresh_socket_list(bNodeTree &ntree,
bNode &node,
ListBase &sockets,
@ -402,6 +418,14 @@ void node_socket_init_default_value(bNodeSocket *sock)
"node socket value material");
dval->value = nullptr;
sock->default_value = dval;
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction *dval = MEM_cnew<bNodeSocketValueFunction>(
"node socket value function");
dval->value = nullptr;
sock->default_value = dval;
break;
}
@ -498,6 +522,13 @@ void node_socket_copy_default_value(bNodeSocket *to, const bNodeSocket *from)
id_us_plus(&toval->value->id);
break;
}
case SOCK_FUNCTION: {
bNodeSocketValueFunction *toval = (bNodeSocketValueFunction *)to->default_value;
bNodeSocketValueFunction *fromval = (bNodeSocketValueFunction *)from->default_value;
*toval = *fromval;
id_us_plus(&toval->value->id);
break;
}
}
to->flag |= (from->flag & SOCK_HIDE_VALUE);
@ -778,6 +809,21 @@ static bNodeSocketType *make_socket_type_material()
return socktype;
}
static bNodeSocketType *make_socket_type_function()
{
using Closure = blender::nodes::Closure;
bNodeSocketType *socktype = make_standard_socket_type(SOCK_FUNCTION, PROP_NONE);
socktype->base_cpp_type = &blender::CPPType::get<Closure>();
socktype->get_base_cpp_value = [](const bNodeSocket &socket, void *r_value) {
const bNodeTree *ntree = ((bNodeSocketValueFunction *)socket.default_value)->value;
new (r_value) Closure(Closure::make_from_node_tree(ntree));
};
socktype->geometry_nodes_cpp_type = socktype->base_cpp_type;
socktype->get_geometry_nodes_cpp_value = socktype->get_base_cpp_value;
return socktype;
}
void register_standard_node_socket_types()
{
/* Draw callbacks are set in `drawnode.c` to avoid bad-level calls. */
@ -824,5 +870,7 @@ void register_standard_node_socket_types()
nodeRegisterSocketType(make_socket_type_material());
nodeRegisterSocketType(make_socket_type_function());
nodeRegisterSocketType(make_socket_type_virtual());
}