WIP: Closures and deferred evaluation for geometry nodes #107842
|
@ -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,
|
||||
)
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
|
||||
/** \} */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 ¶m : args.params_span) {
|
||||
if (¶m != 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, ¶m_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 ¶m : sig->params_span(param_type)) {
|
||||
if (STREQ(param.name, name)) {
|
||||
return ¶m;
|
||||
}
|
||||
}
|
||||
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 ¶m : 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 ¶m : 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 ¶m : sig->inputs_span()) {
|
||||
BLO_write_string(writer, param.name);
|
||||
}
|
||||
for (const bNodeFunctionParameter ¶m : 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 ¶m : sig->inputs_span()) {
|
||||
BLO_read_data_address(reader, ¶m.name);
|
||||
}
|
||||
for (bNodeFunctionParameter ¶m : sig->outputs_span()) {
|
||||
BLO_read_data_address(reader, ¶m.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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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 ¶ms, 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;
|
||||
|
|
|
@ -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 ¶m,
|
||||
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);
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue