Geometry Nodes: Operator to wrap a modifier's node group #104546

Merged
Hans Goudey merged 19 commits from HooglyBoogly/blender:geometry-nodes-wrapper-operator into main 2023-02-14 18:22:04 +01:00
8 changed files with 321 additions and 2 deletions
Showing only changes of commit 46a5f553b8 - Show all commits

View File

@ -6,7 +6,7 @@ from bpy.types import Operator
from bpy.app.translations import pgettext_data as data_
def geometry_node_group_empty_new():
def geometry_node_group_empty_new(add_link=True):
HooglyBoogly marked this conversation as resolved
Review

Better use two separate functions that call a common function instead of adding optional parameters to this one.

Better use two separate functions that call a common function instead of adding optional parameters to this one.
group = bpy.data.node_groups.new(data_("Geometry Nodes"), 'GeometryNodeTree')
group.inputs.new('NodeSocketGeometry', data_("Geometry"))
group.outputs.new('NodeSocketGeometry', data_("Geometry"))
@ -20,7 +20,8 @@ def geometry_node_group_empty_new():
input_node.location.x = -200 - input_node.width
output_node.location.x = 200
group.links.new(output_node.inputs[0], input_node.outputs[0])
if add_link:
group.links.new(output_node.inputs[0], input_node.outputs[0])
HooglyBoogly marked this conversation as resolved
Review

Return the input and output node from the function, and move this line into geometry_node_group_empty_new.

Return the input and output node from the function, and move this line into `geometry_node_group_empty_new`.
return group
@ -84,7 +85,53 @@ class NewGeometryNodeTreeAssign(Operator):
return {'FINISHED'}
class CreateModifierWrapperGroup(Operator):
"""Create a new node group wrapping the modifier's group"""
bl_idname = "node.new_geometry_node_group_wrapper"
bl_label = "Create Wrapper Group"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return geometry_modifier_poll(context)
HooglyBoogly marked this conversation as resolved
Review

The name is still kind of ugly. Maybe something like Move to Nodes (description: Move inputs and outputs set in the modifier to a new node group) or what Simon suggested.

The name is still kind of ugly. Maybe something like `Move to Nodes` (description: `Move inputs and outputs set in the modifier to a new node group`) or what Simon suggested.
Review

Yeah, good idea!

Yeah, good idea!
def execute(self, context):
if context.area.type == 'PROPERTIES':
modifier = context.modifier
HooglyBoogly marked this conversation as resolved
Review

The class name and idname should be updated to respect the new label.

The class name and idname should be updated to respect the new label.
else:
modifier = context.object.modifiers.active
if not modifier:
return {'CANCELLED'}
old_group = modifier.node_group
if not old_group:
return {'CANCELLED'}
group = geometry_node_group_empty_new(add_link=False)
new_group_node = group.nodes.new("GeometryNodeGroup")
new_group_node.node_tree = old_group
group_input_node = group.nodes["Group Input"]
group_output_node = group.nodes["Group Output"]
for input in old_group.inputs:
group.inputs.new()
if hasattr(input, "default_value"):
new_group_node.inputs[input.identifier].default_value = modifier[input.identifier]
else:
group.links.new(group_input_node.outputs[input.identifier], new_group_node.inputs[input.identifier])
HooglyBoogly marked this conversation as resolved
Review

Avoid using input as variable name because this is a built-in function in Python.

Avoid using `input` as variable name because this is a built-in function in Python.
for output in old_group.outputs:
group.links.new(new_group_node.outputs[input.identifier], group_output_node.inputs[input.identifier])
modifier.node_group = group
return {'FINISHED'}
classes = (
NewGeometryNodesModifier,
NewGeometryNodeTreeAssign,
CreateModifierWrapperGroup,
)

View File

@ -33,6 +33,7 @@ set(INC_SYS
)
set(SRC
geometry_nodes_wrapper.cc
object_add.cc
object_bake.cc
object_bake_api.cc

View File

@ -0,0 +1,222 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#include "DNA_modifier_types.h"
#include "BKE_context.h"
#include "BKE_idprop.hh"
#include "BKE_lib_id.h"
#include "BKE_node.h"
#include "BKE_object.h"
#include "RNA_prototypes.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "WM_api.h"
#include "ED_object.h"
#include "ED_screen.h"
#include "NOD_socket.h"
#include "MOD_nodes.h"
#include "object_intern.h"
/* ------------------------------------------------------------------- */
/** \name Create Geometry Nodes Wrapper Object
* \{ */
namespace blender::ed::object {
static eNodeSocketDatatype custom_data_type_to_socket_type(const eCustomDataType type)
{
switch (type) {
case CD_PROP_FLOAT:
return SOCK_FLOAT;
case CD_PROP_INT32:
return SOCK_INT;
case CD_PROP_FLOAT3:
return SOCK_VECTOR;
case CD_PROP_BOOL:
return SOCK_BOOLEAN;
case CD_PROP_COLOR:
return SOCK_RGBA;
default:
BLI_assert_unreachable();
return SOCK_FLOAT;
}
}
static eCustomDataType socket_type_to_custom_data_type(const eNodeSocketDatatype socket_type)
{
switch (socket_type) {
case SOCK_FLOAT:
return CD_PROP_FLOAT;
case SOCK_INT:
return CD_PROP_INT32;
case SOCK_VECTOR:
return CD_PROP_FLOAT3;
case SOCK_BOOLEAN:
return CD_PROP_BOOL;
case SOCK_RGBA:
return CD_PROP_COLOR;
default:
/* Fallback. */
return CD_AUTO_FROM_NAME;
}
}
static bool create_group_wrapper_poll(bContext *C)
{
return edit_modifier_poll_generic(C, &RNA_NodesModifier, 0, true, false);
}
static int create_group_wrapper_exec(bContext *C, wmOperator *op)
{
// if context.area.type == 'PROPERTIES':
// modifier = context.modifier
// else:
// modifier = context.object.modifiers.active
// if not modifier:
// return {'CANCELLED'}
// old_group = modifier.node_group
// if not old_group:
// return {'CANCELLED'}
// group = geometry_node_group_empty_new(add_link=False)
// new_group_node = group.nodes.new("GeometryNodeGroup")
// new_group_node.node_tree = old_group
// group_input_node = group.nodes["Group Input"]
// group_output_node = group.nodes["Group Output"]
// for input in old_group.inputs:
// group.inputs.new()
// if hasattr(input, "default_value"):
// new_group_node.inputs[input.identifier].default_value = modifier[input.identifier]
// else:
// group.links.new(group_input_node.outputs[input.identifier],
// new_group_node.inputs[input.identifier])
// for output in old_group.outputs:
// group.links.new(new_group_node.outputs[input.identifier],
// group_output_node.inputs[input.identifier])
// modifier.node_group = group
// return {'FINISHED'}
Main *bmain = CTX_data_main(C);
Object *object = CTX_data_active_object(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", &RNA_NodesModifier);
NodesModifierData *nmd = static_cast<NodesModifierData *>(ptr.data);
if (!nmd) {
return OPERATOR_CANCELLED;
}
const bNodeTree *old_group = nmd->node_group;
if (!old_group) {
return OPERATOR_CANCELLED;
}
bNodeTree *new_group = reinterpret_cast<bNodeTree *>(
BKE_id_new(bmain, ID_NT, nmd->modifier.name));
old_group->ensure_topology_cache();
bNode *new_group_node = nodeAddNode(C, new_group, "GeometryNodeGroup");
new_group->id = nmd->node_group;
nodes::update_node_declaration_and_sockets(*new_group, *new_group_node);
for (const bNodeSocket *input : old_group->interface_inputs()) {
bNodeSocket *group_node_socket = nodeFindSocket(new_group_node, SOCK_IN, input->identifier);
ntreeAddSocketInterfaceFromSocket(new_group, new_group_node, group_node_socket);
}
for (const bNodeSocket *output : old_group->interface_outputs()) {
bNodeSocket *group_node_socket = nodeFindSocket(new_group_node, SOCK_IN, output->identifier);
ntreeAddSocketInterfaceFromSocket(new_group, new_group_node, group_node_socket);
}
bNode *new_group_input_node = nodeAddNode(C, new_group, "NodeGroupInput");
bNode *new_group_output_node = nodeAddNode(C, new_group, "NodeGroupInput");
for (const bNodeSocket *input : old_group->interface_inputs()) {
if (const std::optional<StringRef> attribute = MOD_nodes_property_try_get_attribute(
*nmd, input->identifier)) {
bNode *named_attribute_node = nodeAddNode(C, new_group, "NodeGeometryInputNamedAttribute");
const auto &storage = *static_cast<const NodeGeometryInputNamedAttribute *>(
named_attribute_node->storage);
storage.data_type = socket_type_to_custom_data_type(input->type);
named_attribute_node->typeinfo->updatefunc(new_group, named_attribute_node);
bNodeSocket *name_socket = nodeFindSocket(named_attribute_node, SOCK_IN, "Name");
BLI_strncpy(name_socket->default_value_typed<bNodeSocketValueString>()->value,
attribute->data(),
attribute->size());
nodeAddLink(new_group,
named_attribute_node,
bke::node_find_enabled_output_socket(*named_attribute_node, "Attribute"),
new_group_node,
nodeFindSocket(new_group_node, SOCK_IN, input->identifier));
}
else {
IDProperty *prop = IDP_GetPropertyFromGroup(nmd->settings.properties, input->identifier);
switch (eNodeSocketDatatype(input->type)) {
case SOCK_FLOAT:
break;
case SOCK_VECTOR:
break;
case SOCK_RGBA:
break;
case SOCK_SHADER:
break;
case SOCK_BOOLEAN:
break;
case SOCK_INT:
break;
case SOCK_STRING:
break;
case SOCK_OBJECT:
break;
case SOCK_IMAGE:
break;
case SOCK_GEOMETRY:
break;
case SOCK_COLLECTION:
break;
case SOCK_TEXTURE:
break;
case SOCK_MATERIAL:
break;
}
}
}
for (const bNodeSocket *output : old_group->interface_outputs()) {
}
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
return OPERATOR_FINISHED;
}
} // namespace blender::ed::object
void OBJECT_OT_new_geometry_node_group_wrapper(wmOperatorType *ot)
{
ot->name = "Create Wrapper Geometry Group";
ot->description = "Create a new node group that contains the modifier's node group as a node";
ot->idname = "OBJECT_OT_new_geometry_node_group_wrapper";
ot->exec = blender::ed::object::create_group_wrapper_exec;
ot->poll = blender::ed::object::create_group_wrapper_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
edit_modifier_properties(ot);
}
/** \} */

View File

@ -352,6 +352,10 @@ void OBJECT_OT_quadriflow_remesh(struct wmOperatorType *ot);
void OBJECT_OT_data_transfer(struct wmOperatorType *ot);
void OBJECT_OT_datalayout_transfer(struct wmOperatorType *ot);
/* geometry_nodes_wrapper.c */
void OBJECT_OT_new_geometry_node_group_wrapper(struct wmOperatorType *ot);
#ifdef __cplusplus
}
#endif

View File

@ -279,6 +279,8 @@ void ED_operatortypes_object(void)
WM_operatortype_append(OBJECT_OT_voxel_size_edit);
WM_operatortype_append(OBJECT_OT_quadriflow_remesh);
WM_operatortype_append(OBJECT_OT_new_geometry_node_group_wrapper);
}
void ED_operatormacros_object(void)

View File

@ -5,6 +5,17 @@
struct NodesModifierData;
struct Object;
#ifdef __cplusplus
# include <optional>
# include "BLI_string_ref.hh"
#endif
#ifdef __cplusplus
std::optional<blender::StringRef> MOD_nodes_property_try_get_attribute(
const NodesModifierData &nmd, const blender::StringRef identifier);
#endif
#ifdef __cplusplus
extern "C" {
#endif

View File

@ -411,6 +411,27 @@ static bool logging_enabled(const ModifierEvalContext *ctx)
static const std::string use_attribute_suffix = "_use_attribute";
static const std::string attribute_name_suffix = "_attribute_name";
std::optional<StringRef> MOD_nodes_property_try_get_attribute(const NodesModifierData &nmd,
const StringRef identifier)
{
if (!nmd.settings.properties) {
return std::nullopt;
}
const std::string use_attribute_name = identifier + use_attribute_suffix;
const IDProperty *use_attribute_prop = IDP_GetPropertyFromGroup(nmd.settings.properties,
use_attribute_name.c_str());
if (!use_attribute_prop) {
return std::nullopt;
}
if (IDP_Int(use_attribute_prop) == 0) {
return std::nullopt;
}
const std::string name_prop_name = identifier + attribute_name_suffix;
const IDProperty *name_prop = IDP_GetPropertyFromGroup(nmd.settings.properties,
name_prop_name.c_str());
return IDP_String(name_prop);
}
/**
* \return Whether using an attribute to input values of this type is supported.
*/

View File

@ -274,6 +274,17 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
if (!md->next) {
uiLayoutSetEnabled(row, false);
}
if (md->type == eModifierType_Nodes) {
uiItemFullO(layout,
"OBJECT_OT_new_geometry_node_group_wrapper",
IFACE_("Create Wrapper Group"),
ICON_NONE,
NULL,
WM_OP_INVOKE_DEFAULT,
0,
&op_ptr);
}
}
static void modifier_panel_header(const bContext *C, Panel *panel)