Geometry Nodes: move socket items ui and operators from Python to C++

This has some benefits:
* Nodes with dynamic socket amounts can remain more self-contained
  (2 fewer files to edit with this patch).
* It's easier to reuse existing C++ code, reducing redundancy.

One new thing I'm doing here is to define operators in node files. It seems
reasonable to register operators that belong to a node together with that
node. Without this, code spreads out further than necessary without any real benefit.

This patch affects the simulation zone, repeat zone, bake node and index switch node.

The UI is slightly affected too. Since we had the UI defined in Python before,
it wasn't possible to integrate it into the node properties panel. That is possible
now and looks better anyway. The previous UI was an artifact of technical limitations.

Pull Request: https://projects.blender.org/blender/blender/pulls/121178
This commit is contained in:
Jacques Lucke
2024-04-29 19:50:11 +02:00
parent 3847cf4dc1
commit 0c585a1b8a
10 changed files with 789 additions and 615 deletions

View File

@@ -361,242 +361,9 @@ class ZoneOperator:
return True
class NodeOperator:
@classmethod
def get_node(cls, context):
node = context.active_node
if node is None:
return None
if node.bl_idname == cls.node_type:
return node
@classmethod
def poll(cls, context):
space = context.space_data
# Needs active node editor and a tree.
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree or space.edit_tree.library:
return False
node = cls.get_node(context)
if node is None:
return False
return True
class SocketItemAddOperator:
items_name = None
active_index_name = None
default_socket_type = 'GEOMETRY'
def execute(self, context):
node = self.get_node(context)
items = getattr(node, self.items_name)
# Remember index to move the item.
old_active_index = getattr(node, self.active_index_name)
if 0 <= old_active_index < len(items):
old_active_item = items[old_active_index]
dst_index = old_active_index + 1
dst_type = old_active_item.socket_type
dst_name = old_active_item.name
else:
dst_index = len(items)
dst_type = self.default_socket_type
# Empty name so it is based on the type.
dst_name = ""
items.new(dst_type, dst_name)
items.move(len(items) - 1, dst_index)
setattr(node, self.active_index_name, dst_index)
return {'FINISHED'}
class SocketItemRemoveOperator:
items_name = None
active_index_name = None
def execute(self, context):
node = self.get_node(context)
items = getattr(node, self.items_name)
old_active_index = getattr(node, self.active_index_name)
if 0 <= old_active_index < len(items):
items.remove(items[old_active_index])
return {'FINISHED'}
class SocketMoveItemOperator:
items_name = None
active_index_name = None
direction: EnumProperty(
name="Direction",
items=[('UP', "Up", ""), ('DOWN', "Down", "")],
default='UP',
)
def execute(self, context):
node = self.get_node(context)
items = getattr(node, self.items_name)
old_active_index = getattr(node, self.active_index_name)
if self.direction == 'UP' and old_active_index > 0:
items.move(old_active_index, old_active_index - 1)
setattr(node, self.active_index_name, old_active_index - 1)
elif self.direction == 'DOWN' and old_active_index < len(items) - 1:
items.move(old_active_index, old_active_index + 1)
setattr(node, self.active_index_name, old_active_index + 1)
return {'FINISHED'}
class SimulationZoneOperator(ZoneOperator):
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
items_name = "state_items"
active_index_name = "active_index"
class SimulationZoneItemAddOperator(SimulationZoneOperator, SocketItemAddOperator, Operator):
"""Add a state item to the simulation zone"""
bl_idname = "node.simulation_zone_item_add"
bl_label = "Add State Item"
bl_options = {'REGISTER', 'UNDO'}
class SimulationZoneItemRemoveOperator(SimulationZoneOperator, SocketItemRemoveOperator, Operator):
"""Remove a state item from the simulation zone"""
bl_idname = "node.simulation_zone_item_remove"
bl_label = "Remove State Item"
bl_options = {'REGISTER', 'UNDO'}
class SimulationZoneItemMoveOperator(SimulationZoneOperator, SocketMoveItemOperator, Operator):
"""Move a simulation state item up or down in the list"""
bl_idname = "node.simulation_zone_item_move"
bl_label = "Move State Item"
bl_options = {'REGISTER', 'UNDO'}
class RepeatZoneOperator(ZoneOperator):
input_node_type = 'GeometryNodeRepeatInput'
output_node_type = 'GeometryNodeRepeatOutput'
items_name = "repeat_items"
active_index_name = "active_index"
class RepeatZoneItemAddOperator(RepeatZoneOperator, SocketItemAddOperator, Operator):
"""Add a repeat item to the repeat zone"""
bl_idname = "node.repeat_zone_item_add"
bl_label = "Add Repeat Item"
bl_options = {'REGISTER', 'UNDO'}
class RepeatZoneItemRemoveOperator(RepeatZoneOperator, SocketItemRemoveOperator, Operator):
"""Remove a repeat item from the repeat zone"""
bl_idname = "node.repeat_zone_item_remove"
bl_label = "Remove Repeat Item"
bl_options = {'REGISTER', 'UNDO'}
class RepeatZoneItemMoveOperator(RepeatZoneOperator, SocketMoveItemOperator, Operator):
"""Move a repeat item up or down in the list"""
bl_idname = "node.repeat_zone_item_move"
bl_label = "Move Repeat Item"
bl_options = {'REGISTER', 'UNDO'}
class BakeNodeOperator(NodeOperator):
node_type = 'GeometryNodeBake'
items_name = "bake_items"
active_index_name = "active_index"
class BakeNodeItemAddOperator(BakeNodeOperator, SocketItemAddOperator, Operator):
"""Add a bake item to the bake node"""
bl_idname = "node.bake_node_item_add"
bl_label = "Add Bake Item"
bl_options = {'REGISTER', 'UNDO'}
class BakeNodeItemRemoveOperator(BakeNodeOperator, SocketItemRemoveOperator, Operator):
"""Remove a bake item from the bake node"""
bl_idname = "node.bake_node_item_remove"
bl_label = "Remove Bake Item"
bl_options = {'REGISTER', 'UNDO'}
class BakeNodeItemMoveOperator(BakeNodeOperator, SocketMoveItemOperator, Operator):
"""Move a bake item up or down in the list"""
bl_idname = "node.bake_node_item_move"
bl_label = "Move Bake Item"
bl_options = {'REGISTER', 'UNDO'}
def _editable_tree_with_active_node_type(context, node_type):
space = context.space_data
# Needs active node editor and a tree.
if not space or space.type != 'NODE_EDITOR' or not space.edit_tree or space.edit_tree.library:
return False
node = context.active_node
if node is None or node.bl_idname != node_type:
return False
return True
class IndexSwitchItemAddOperator(Operator):
"""Add an item to the index switch"""
bl_idname = "node.index_switch_item_add"
bl_label = "Add Item"
bl_options = {'REGISTER', 'UNDO'}
@classmethod
def poll(cls, context):
return _editable_tree_with_active_node_type(context, 'GeometryNodeIndexSwitch')
def execute(self, context):
node = context.active_node
node.index_switch_items.new()
return {'FINISHED'}
class IndexSwitchItemRemoveOperator(Operator):
"""Remove an item from the index switch"""
bl_idname = "node.index_switch_item_remove"
bl_label = "Remove Item"
bl_options = {'REGISTER', 'UNDO'}
index: IntProperty(
name="Index",
description="Index of item to remove",
)
@classmethod
def poll(cls, context):
return _editable_tree_with_active_node_type(context, 'GeometryNodeIndexSwitch')
def execute(self, context):
node = context.active_node
items = node.index_switch_items
items.remove(items[self.index])
return {'FINISHED'}
classes = (
NewGeometryNodesModifier,
NewGeometryNodeTreeAssign,
NewGeometryNodeGroupTool,
MoveModifierToNodes,
SimulationZoneItemAddOperator,
SimulationZoneItemRemoveOperator,
SimulationZoneItemMoveOperator,
RepeatZoneItemAddOperator,
RepeatZoneItemRemoveOperator,
RepeatZoneItemMoveOperator,
BakeNodeItemAddOperator,
BakeNodeItemRemoveOperator,
BakeNodeItemMoveOperator,
IndexSwitchItemAddOperator,
IndexSwitchItemRemoveOperator,
)

View File

@@ -991,244 +991,6 @@ class NODE_PT_node_tree_properties(Panel):
col.prop(group, "is_tool")
def draw_socket_item_in_list(uilist, layout, item, icon):
if uilist.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 uilist.layout_type == 'GRID':
layout.alignment = 'CENTER'
layout.template_node_socket(color=item.color)
class NODE_UL_simulation_zone_items(UIList):
def draw_item(self, context, layout, _data, item, icon, _active_data, _active_propname, _index):
draw_socket_item_in_list(self, layout, item, icon)
class NODE_PT_simulation_zone_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Simulation State"
input_node_type = 'GeometryNodeSimulationInput'
output_node_type = 'GeometryNodeSimulationOutput'
@classmethod
def get_output_node(cls, context):
node = context.active_node
if node.bl_idname == cls.input_node_type:
return node.paired_output
if node.bl_idname == cls.output_node_type:
return node
@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.input_node_type, cls.output_node_type]:
return False
if cls.get_output_node(context) is None:
return False
return True
def draw(self, context):
layout = self.layout
output_node = self.get_output_node(context)
split = layout.row()
split.template_list(
"NODE_UL_simulation_zone_items",
"",
output_node,
"state_items",
output_node,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.simulation_zone_item_add", icon='ADD', text="")
add_remove_col.operator("node.simulation_zone_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.simulation_zone_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.simulation_zone_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = output_node.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "socket_type")
if active_item.socket_type in {'VECTOR', 'INT', 'BOOLEAN', 'FLOAT', 'RGBA', 'ROTATION'}:
layout.prop(active_item, "attribute_domain")
class NODE_UL_repeat_zone_items(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
draw_socket_item_in_list(self, layout, item, icon)
class NODE_PT_repeat_zone_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Repeat"
input_node_type = 'GeometryNodeRepeatInput'
output_node_type = 'GeometryNodeRepeatOutput'
@classmethod
def get_output_node(cls, context):
node = context.active_node
if node.bl_idname == cls.input_node_type:
return node.paired_output
if node.bl_idname == cls.output_node_type:
return node
return None
@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.input_node_type, cls.output_node_type}:
return False
if cls.get_output_node(context) is None:
return False
return True
def draw(self, context):
layout = self.layout
output_node = self.get_output_node(context)
split = layout.row()
split.template_list(
"NODE_UL_repeat_zone_items",
"",
output_node,
"repeat_items",
output_node,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.repeat_zone_item_add", icon='ADD', text="")
add_remove_col.operator("node.repeat_zone_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.repeat_zone_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = output_node.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "socket_type")
layout.prop(output_node, "inspection_index")
class NODE_UL_bake_node_items(UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
draw_socket_item_in_list(self, layout, item, icon)
class NODE_PT_bake_node_items(bpy.types.Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Bake Items"
@classmethod
def poll(cls, context):
snode = context.space_data
if snode is None:
return False
node = context.active_node
if node is None:
return False
if node.bl_idname != "GeometryNodeBake":
return False
return True
def draw(self, context):
layout = self.layout
node = context.active_node
split = layout.row()
split.template_list(
"NODE_UL_bake_node_items",
"",
node,
"bake_items",
node,
"active_index")
ops_col = split.column()
add_remove_col = ops_col.column(align=True)
add_remove_col.operator("node.bake_node_item_add", icon='ADD', text="")
add_remove_col.operator("node.bake_node_item_remove", icon='REMOVE', text="")
ops_col.separator()
up_down_col = ops_col.column(align=True)
props = up_down_col.operator("node.bake_node_item_move", icon='TRIA_UP', text="")
props.direction = 'UP'
props = up_down_col.operator("node.bake_node_item_move", icon='TRIA_DOWN', text="")
props.direction = 'DOWN'
active_item = node.active_item
if active_item is not None:
layout.use_property_split = True
layout.use_property_decorate = False
layout.prop(active_item, "socket_type")
if active_item.socket_type in {'VECTOR', 'INT', 'BOOLEAN', 'FLOAT', 'RGBA', 'ROTATION'}:
layout.prop(active_item, "attribute_domain")
layout.prop(active_item, "is_attribute")
class NODE_PT_index_switch_node_items(Panel):
bl_space_type = 'NODE_EDITOR'
bl_region_type = 'UI'
bl_category = "Node"
bl_label = "Index Switch"
@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 != 'GeometryNodeIndexSwitch':
return False
return True
def draw(self, context):
layout = self.layout
node = context.active_node
layout.operator("node.index_switch_item_add", icon='ADD', text="Add Item")
col = layout.column()
for i, item in enumerate(node.index_switch_items):
row = col.row()
row.label(text=node.inputs[i + 1].name)
row.operator("node.index_switch_item_remove", icon='REMOVE', text="").index = i
class NODE_UL_enum_definition_items(bpy.types.UIList):
def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, _index):
layout.prop(item, "name", text="", emboss=False, icon_value=icon)
@@ -1349,13 +1111,6 @@ classes = (
NODE_PT_quality,
NODE_PT_annotation,
NODE_PT_overlay,
NODE_UL_simulation_zone_items,
NODE_PT_simulation_zone_items,
NODE_UL_repeat_zone_items,
NODE_UL_bake_node_items,
NODE_PT_bake_node_items,
NODE_PT_index_switch_node_items,
NODE_PT_repeat_zone_items,
NODE_UL_enum_definition_items,
NODE_PT_menu_switch_items,
NODE_PT_active_node_properties,

View File

@@ -349,6 +349,13 @@ struct bNodeType {
/** Get extra information that is drawn next to the node. */
NodeExtraInfoFunction get_extra_info;
/**
* Registers operators that are specific to this node. This allows nodes to be more
* self-contained compared to the alternative to registering all operators in a more central
* place.
*/
void (*register_operators)();
/** True when the node cannot be muted. */
bool no_muting;
/** True when the node still works but it's usage is discouraged. */

View File

@@ -106,6 +106,13 @@ void node_operatortypes()
WM_operatortype_append(NODE_OT_cryptomatte_layer_add);
WM_operatortype_append(NODE_OT_cryptomatte_layer_remove);
NODE_TYPES_BEGIN (ntype) {
if (ntype->register_operators) {
ntype->register_operators();
}
}
NODE_TYPES_END;
}
void node_keymap(wmKeyConfig *keyconf)

View File

@@ -111,6 +111,7 @@ set(SRC
NOD_socket_declarations.hh
NOD_socket_declarations_geometry.hh
NOD_socket_items.hh
NOD_socket_items_ops.hh
NOD_socket_search_link.hh
NOD_static_types.h
NOD_texture.h

View File

@@ -0,0 +1,233 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include "NOD_socket_items.hh"
#include "WM_api.hh"
#include "BKE_context.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_node_tree_zones.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.h"
#include "ED_node.hh"
#include "DNA_space_types.h"
namespace blender::nodes::socket_items::ops {
inline PointerRNA get_active_node_to_operate_on(bContext *C, const int node_type)
{
SpaceNode *snode = CTX_wm_space_node(C);
if (!snode) {
return PointerRNA_NULL;
}
if (!snode->edittree) {
return PointerRNA_NULL;
}
if (ID_IS_LINKED(snode->edittree)) {
return PointerRNA_NULL;
}
const bke::bNodeTreeZones *zones = snode->edittree->zones();
if (!zones) {
return PointerRNA_NULL;
}
bNode *active_node = nodeGetActive(snode->edittree);
if (!active_node) {
return PointerRNA_NULL;
}
const bke::bNodeTreeZone *zone = zones->get_zone_by_node(active_node->identifier);
if (zone->input_node == active_node) {
/* Assume the data is generally stored on the output and not the input node. */
active_node = const_cast<bNode *>(zone->output_node);
}
if (active_node->type != node_type) {
return PointerRNA_NULL;
}
return RNA_pointer_create(&snode->edittree->id, &RNA_Node, active_node);
}
inline void update_after_node_change(bContext *C, const PointerRNA node_ptr)
{
bNode *node = static_cast<bNode *>(node_ptr.data);
bNodeTree *ntree = reinterpret_cast<bNodeTree *>(node_ptr.owner_id);
BKE_ntree_update_tag_node_property(ntree, node);
ED_node_tree_propagate_change(nullptr, CTX_data_main(C), ntree);
WM_main_add_notifier(NC_NODE | NA_EDITED, ntree);
}
template<typename Accessor> inline bool editable_node_active_poll(bContext *C)
{
return get_active_node_to_operate_on(C, Accessor::node_type).data != nullptr;
}
template<typename Accessor>
inline void remove_item(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator * /*op*/) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
SocketItemsRef ref = Accessor::get_items_from_node(node);
dna::array::remove_index(
ref.items, ref.items_num, ref.active_index, *ref.active_index, Accessor::destruct_item);
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
}
template<typename Accessor>
inline void remove_item_by_index(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator *op) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
const int index_to_remove = RNA_int_get(op->ptr, "index");
SocketItemsRef ref = Accessor::get_items_from_node(node);
dna::array::remove_index(
ref.items, ref.items_num, ref.active_index, index_to_remove, Accessor::destruct_item);
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
RNA_def_int(ot->srna, "index", 0, 0, INT32_MAX, "Index", "Index to remove", 0, INT32_MAX);
}
template<typename Accessor>
inline void add_item_with_name_and_type(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
static_assert(Accessor::has_type);
static_assert(Accessor::has_name);
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator * /*op*/) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
SocketItemsRef ref = Accessor::get_items_from_node(node);
const int old_active_index = *ref.active_index;
eNodeSocketDatatype socket_type;
std::string name;
int dst_index;
if (old_active_index >= 0 && old_active_index < *ref.items_num) {
dst_index = old_active_index + 1;
const typename Accessor::ItemT &active_item = (*ref.items)[old_active_index];
socket_type = eNodeSocketDatatype(active_item.socket_type);
name = active_item.name;
}
else {
dst_index = *ref.items_num;
socket_type = SOCK_GEOMETRY;
/* Empty name so it is based on the type. */
name = "";
}
add_item_with_socket_and_name<Accessor>(node, socket_type, name.c_str());
dna::array::move_index(*ref.items, *ref.items_num, *ref.items_num - 1, dst_index);
*ref.active_index = dst_index;
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
}
template<typename Accessor>
inline void add_item(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
static_assert(!Accessor::has_type);
static_assert(!Accessor::has_name);
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator * /*op*/) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
socket_items::add_item<Accessor>(node);
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
}
enum class MoveDirection {
Up = 0,
Down = 1,
};
template<typename Accessor>
inline void move_item(wmOperatorType *ot,
const char *name,
const char *idname,
const char *description)
{
ot->name = name;
ot->idname = idname;
ot->description = description;
ot->poll = editable_node_active_poll<Accessor>;
ot->exec = [](bContext *C, wmOperator *op) -> int {
PointerRNA node_ptr = get_active_node_to_operate_on(C, Accessor::node_type);
bNode &node = *static_cast<bNode *>(node_ptr.data);
const MoveDirection direction = MoveDirection(RNA_enum_get(op->ptr, "direction"));
SocketItemsRef ref = Accessor::get_items_from_node(node);
const int old_active_index = *ref.active_index;
if (direction == MoveDirection::Up && old_active_index > 0) {
dna::array::move_index(*ref.items, *ref.items_num, old_active_index, old_active_index - 1);
*ref.active_index -= 1;
}
else if (direction == MoveDirection::Down && old_active_index < *ref.items_num - 1) {
dna::array::move_index(*ref.items, *ref.items_num, old_active_index, old_active_index + 1);
*ref.active_index += 1;
}
update_after_node_change(C, node_ptr);
return OPERATOR_FINISHED;
};
static const EnumPropertyItem direction_items[] = {
{int(MoveDirection::Up), "UP", 0, "Up", ""},
{int(MoveDirection::Down), "DOWN", 0, "Down", ""},
{0, nullptr, 0, nullptr, nullptr},
};
RNA_def_enum(ot->srna, "direction", direction_items, 0, "Direction", "Move direction");
}
} // namespace blender::nodes::socket_items::ops

View File

@@ -7,6 +7,7 @@
#include "NOD_geo_bake.hh"
#include "NOD_node_extra_info.hh"
#include "NOD_rna_define.hh"
#include "NOD_socket_items_ops.hh"
#include "UI_interface.hh"
#include "UI_resources.hh"
@@ -114,6 +115,109 @@ static const CPPType &get_item_cpp_type(const eNodeSocketDatatype socket_type)
return *typeinfo->geometry_nodes_cpp_type;
}
static void draw_bake_item(uiList * /*ui_list*/,
const bContext *C,
uiLayout *layout,
PointerRNA * /*idataptr*/,
PointerRNA *itemptr,
int /*icon*/,
PointerRNA * /*active_dataptr*/,
const char * /*active_propname*/,
int /*index*/,
int /*flt_flag*/)
{
uiLayout *row = uiLayoutRow(layout, true);
float4 color;
RNA_float_get_array(itemptr, "color", color);
uiTemplateNodeSocket(row, const_cast<bContext *>(C), color);
uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
uiItemR(row, itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
}
static void draw_bake_items(const bContext *C, uiLayout *layout, PointerRNA node_ptr)
{
static const uiListType *bake_items_list = []() {
uiListType *list = MEM_cnew<uiListType>(__func__);
STRNCPY(list->idname, "DATA_UL_bake_node_items");
list->draw_item = draw_bake_item;
WM_uilisttype_add(list);
return list;
}();
bNode &node = *static_cast<bNode *>(node_ptr.data);
if (uiLayout *panel = uiLayoutPanel(C, layout, "bake_items", false, TIP_("Bake Items"))) {
uiLayout *row = uiLayoutRow(panel, false);
uiTemplateList(row,
C,
bake_items_list->idname,
"",
&node_ptr,
"bake_items",
&node_ptr,
"active_index",
nullptr,
3,
5,
UILST_LAYOUT_DEFAULT,
0,
UI_TEMPLATE_LIST_FLAG_NONE);
{
uiLayout *ops_col = uiLayoutColumn(row, false);
{
uiLayout *add_remove_col = uiLayoutColumn(ops_col, true);
uiItemO(add_remove_col, "", ICON_ADD, "node.bake_node_item_add");
uiItemO(add_remove_col, "", ICON_REMOVE, "node.bake_node_item_remove");
}
{
uiLayout *up_down_col = uiLayoutColumn(ops_col, true);
uiItemEnumO(up_down_col, "node.bake_node_item_move", "", ICON_TRIA_UP, "direction", 0);
uiItemEnumO(up_down_col, "node.bake_node_item_move", "", ICON_TRIA_DOWN, "direction", 1);
}
}
NodeGeometryBake &storage = node_storage(node);
if (storage.active_index >= 0 && storage.active_index < storage.items_num) {
NodeGeometryBakeItem &active_item = storage.items[storage.active_index];
PointerRNA item_ptr = RNA_pointer_create(
node_ptr.owner_id, BakeItemsAccessor::item_srna, &active_item);
uiLayoutSetPropSep(panel, true);
uiLayoutSetPropDecorate(panel, false);
uiItemR(panel, &item_ptr, "socket_type", UI_ITEM_NONE, nullptr, ICON_NONE);
if (socket_type_supports_fields(eNodeSocketDatatype(active_item.socket_type))) {
uiItemR(panel, &item_ptr, "attribute_domain", UI_ITEM_NONE, nullptr, ICON_NONE);
uiItemR(panel, &item_ptr, "is_attribute", UI_ITEM_NONE, nullptr, ICON_NONE);
}
}
}
}
static void NODE_OT_bake_node_item_remove(wmOperatorType *ot)
{
socket_items::ops::remove_item<BakeItemsAccessor>(
ot, "Remove Bake Item", __func__, "Remove active bake item");
}
static void NODE_OT_bake_node_item_add(wmOperatorType *ot)
{
socket_items::ops::add_item_with_name_and_type<BakeItemsAccessor>(
ot, "Add Bake Item", __func__, "Add bake item");
}
static void NODE_OT_bake_node_item_move(wmOperatorType *ot)
{
socket_items::ops::move_item<BakeItemsAccessor>(
ot, "Move Bake Item", __func__, "Move active bake item");
}
static void node_operators()
{
WM_operatortype_append(NODE_OT_bake_node_item_add);
WM_operatortype_append(NODE_OT_bake_node_item_remove);
WM_operatortype_append(NODE_OT_bake_node_item_move);
}
static bake::BakeSocketConfig make_bake_socket_config(const Span<NodeGeometryBakeItem> bake_items)
{
bake::BakeSocketConfig config;
@@ -563,6 +667,8 @@ static void node_layout(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
draw_bake_items(C, layout, *ptr);
BakeDrawContext ctx;
const bNode &node = *static_cast<const bNode *>(ptr->data);
if (!get_bake_draw_context(C, node, ctx)) {
@@ -628,6 +734,7 @@ static void node_register()
ntype.insert_link = node_insert_link;
ntype.draw_buttons_ex = node_layout_ex;
ntype.get_extra_info = node_extra_info;
ntype.register_operators = node_operators;
node_type_storage(&ntype, "NodeGeometryBake", node_free_storage, node_copy_storage);
nodeRegisterType(&ntype);
}

View File

@@ -10,6 +10,7 @@
#include "NOD_geo_index_switch.hh"
#include "NOD_rna_define.hh"
#include "NOD_socket.hh"
#include "NOD_socket_items_ops.hh"
#include "NOD_socket_search_link.hh"
#include "RNA_enum_types.hh"
@@ -65,6 +66,38 @@ static void node_layout(uiLayout *layout, bContext * /*C*/, PointerRNA *ptr)
uiItemR(layout, ptr, "data_type", UI_ITEM_NONE, "", ICON_NONE);
}
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
bNode &node = *static_cast<bNode *>(ptr->data);
NodeIndexSwitch &storage = node_storage(node);
if (uiLayout *panel = uiLayoutPanel(C, layout, "index_switch_items", false, TIP_("Items"))) {
uiItemO(panel, "Add Item", ICON_ADD, "node.index_switch_item_add");
uiLayout *col = uiLayoutColumn(panel, false);
for (const int i : IndexRange(storage.items_num)) {
uiLayout *row = uiLayoutRow(col, false);
uiItemL(row, node.input_socket(i + 1).name, ICON_NONE);
uiItemIntO(row, "", ICON_REMOVE, "node.index_switch_item_remove", "index", i);
}
}
}
static void NODE_OT_index_switch_item_add(wmOperatorType *ot)
{
socket_items::ops::add_item<IndexSwitchItemsAccessor>(ot, "Add Item", __func__, "Add bake item");
}
static void NODE_OT_index_switch_item_remove(wmOperatorType *ot)
{
socket_items::ops::remove_item_by_index<IndexSwitchItemsAccessor>(
ot, "Remove Item", __func__, "Remove an item from the index switch");
}
static void node_operators()
{
WM_operatortype_append(NODE_OT_index_switch_item_add);
WM_operatortype_append(NODE_OT_index_switch_item_remove);
}
static void node_init(bNodeTree * /*tree*/, bNode *node)
{
NodeIndexSwitch *data = MEM_cnew<NodeIndexSwitch>(__func__);
@@ -344,6 +377,8 @@ static void register_node()
node_type_storage(&ntype, "NodeIndexSwitch", node_free_storage, node_copy_storage);
ntype.gather_link_search_ops = node_gather_link_searches;
ntype.draw_buttons = node_layout;
ntype.draw_buttons_ex = node_layout_ex;
ntype.register_operators = node_operators;
nodeRegisterType(&ntype);
node_rna(ntype.rna_ext.srna);

View File

@@ -7,17 +7,116 @@
#include "NOD_geo_repeat.hh"
#include "NOD_socket.hh"
#include "NOD_socket_items_ops.hh"
#include "BLO_read_write.hh"
#include "BLI_string_utils.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.h"
#include "BKE_screen.hh"
#include "WM_api.hh"
#include "UI_interface.hh"
#include "node_geometry_util.hh"
namespace blender::nodes::node_geo_repeat_cc {
static void draw_repeat_state_item(uiList * /*ui_list*/,
const bContext *C,
uiLayout *layout,
PointerRNA * /*idataptr*/,
PointerRNA *itemptr,
int /*icon*/,
PointerRNA * /*active_dataptr*/,
const char * /*active_propname*/,
int /*index*/,
int /*flt_flag*/)
{
uiLayout *row = uiLayoutRow(layout, true);
float4 color;
RNA_float_get_array(itemptr, "color", color);
uiTemplateNodeSocket(row, const_cast<bContext *>(C), color);
uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
uiItemR(row, itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
}
/** Shared between repeat zone input and output node. */
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_node_ptr)
{
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(current_node_ptr->owner_id);
bNode *current_node = static_cast<bNode *>(current_node_ptr->data);
const bke::bNodeTreeZones *zones = ntree.zones();
if (!zones) {
return;
}
const bke::bNodeTreeZone *zone = zones->get_zone_by_node(current_node->identifier);
if (!zone) {
return;
}
if (!zone->output_node) {
return;
}
bNode &output_node = const_cast<bNode &>(*zone->output_node);
PointerRNA output_node_ptr = RNA_pointer_create(
current_node_ptr->owner_id, &RNA_Node, &output_node);
static const uiListType *state_items_list = []() {
uiListType *list = MEM_cnew<uiListType>(__func__);
STRNCPY(list->idname, "DATA_UL_repeat_zone_state");
list->draw_item = draw_repeat_state_item;
WM_uilisttype_add(list);
return list;
}();
if (uiLayout *panel = uiLayoutPanel(C, layout, "repeat_items", false, TIP_("Repeat Items"))) {
uiLayout *row = uiLayoutRow(panel, false);
uiTemplateList(row,
C,
state_items_list->idname,
"",
&output_node_ptr,
"repeat_items",
&output_node_ptr,
"active_index",
nullptr,
3,
5,
UILST_LAYOUT_DEFAULT,
0,
UI_TEMPLATE_LIST_FLAG_NONE);
{
uiLayout *ops_col = uiLayoutColumn(row, false);
{
uiLayout *add_remove_col = uiLayoutColumn(ops_col, true);
uiItemO(add_remove_col, "", ICON_ADD, "node.repeat_zone_item_add");
uiItemO(add_remove_col, "", ICON_REMOVE, "node.repeat_zone_item_remove");
}
{
uiLayout *up_down_col = uiLayoutColumn(ops_col, true);
uiItemEnumO(up_down_col, "node.repeat_zone_item_move", "", ICON_TRIA_UP, "direction", 0);
uiItemEnumO(up_down_col, "node.repeat_zone_item_move", "", ICON_TRIA_DOWN, "direction", 1);
}
}
auto &storage = *static_cast<NodeGeometryRepeatOutput *>(output_node.storage);
if (storage.active_index >= 0 && storage.active_index < storage.items_num) {
NodeRepeatItem &active_item = storage.items[storage.active_index];
PointerRNA item_ptr = RNA_pointer_create(
output_node_ptr.owner_id, RepeatItemsAccessor::item_srna, &active_item);
uiLayoutSetPropSep(panel, true);
uiLayoutSetPropDecorate(panel, false);
uiItemR(panel, &item_ptr, "socket_type", UI_ITEM_NONE, nullptr, ICON_NONE);
}
}
uiItemR(layout, &output_node_ptr, "inspection_index", UI_ITEM_NONE, nullptr, ICON_NONE);
}
namespace repeat_input_node {
NODE_STORAGE_FUNCS(NodeGeometryRepeatInput);
@@ -90,6 +189,7 @@ static void node_register()
ntype.gather_link_search_ops = nullptr;
ntype.insert_link = node_insert_link;
ntype.no_muting = true;
ntype.draw_buttons_ex = node_layout_ex;
node_type_storage(
&ntype, "NodeGeometryRepeatInput", node_free_standard_storage, node_copy_standard_storage);
nodeRegisterType(&ntype);
@@ -162,6 +262,31 @@ static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
*ntree, *node, *node, *link);
}
static void NODE_OT_repeat_zone_item_remove(wmOperatorType *ot)
{
socket_items::ops::remove_item<RepeatItemsAccessor>(
ot, "Remove Repeat Zone Item", __func__, "Remove active repeat zone item");
}
static void NODE_OT_repeat_zone_item_add(wmOperatorType *ot)
{
socket_items::ops::add_item_with_name_and_type<RepeatItemsAccessor>(
ot, "Add Repeat Zone Item", __func__, "Add repeat zone item");
}
static void NODE_OT_repeat_zone_item_move(wmOperatorType *ot)
{
socket_items::ops::move_item<RepeatItemsAccessor>(
ot, "Move Repeat Zone Item", __func__, "Move active repeat zone item");
}
static void node_operators()
{
WM_operatortype_append(NODE_OT_repeat_zone_item_add);
WM_operatortype_append(NODE_OT_repeat_zone_item_remove);
WM_operatortype_append(NODE_OT_repeat_zone_item_move);
}
static void node_register()
{
static bNodeType ntype;
@@ -171,6 +296,8 @@ static void node_register()
ntype.labelfunc = repeat_input_node::node_label;
ntype.insert_link = node_insert_link;
ntype.no_muting = true;
ntype.draw_buttons_ex = node_layout_ex;
ntype.register_operators = node_operators;
node_type_storage(&ntype, "NodeGeometryRepeatOutput", node_free_storage, node_copy_storage);
nodeRegisterType(&ntype);
}

View File

@@ -17,8 +17,11 @@
#include "BKE_instances.hh"
#include "BKE_modifier.hh"
#include "BKE_node_socket_value.hh"
#include "BKE_node_tree_update.hh"
#include "BKE_node_tree_zones.hh"
#include "BKE_object.hh"
#include "BKE_scene.hh"
#include "BKE_screen.hh"
#include "DEG_depsgraph_query.hh"
@@ -28,6 +31,7 @@
#include "NOD_geo_simulation.hh"
#include "NOD_geometry.hh"
#include "NOD_socket.hh"
#include "NOD_socket_items_ops.hh"
#include "DNA_curves_types.h"
#include "DNA_mesh_types.h"
@@ -38,6 +42,7 @@
#include "ED_node.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.h"
#include "MOD_nodes.hh"
@@ -181,6 +186,267 @@ static bke::bake::BakeState move_values_to_simulation_state(
return bake_state;
}
static void draw_simulation_state_item(uiList * /*ui_list*/,
const bContext *C,
uiLayout *layout,
PointerRNA * /*idataptr*/,
PointerRNA *itemptr,
int /*icon*/,
PointerRNA * /*active_dataptr*/,
const char * /*active_propname*/,
int /*index*/,
int /*flt_flag*/)
{
uiLayout *row = uiLayoutRow(layout, true);
float4 color;
RNA_float_get_array(itemptr, "color", color);
uiTemplateNodeSocket(row, const_cast<bContext *>(C), color);
uiLayoutSetEmboss(row, UI_EMBOSS_NONE);
uiItemR(row, itemptr, "name", UI_ITEM_NONE, "", ICON_NONE);
}
static void draw_simulation_state(const bContext *C,
uiLayout *layout,
bNodeTree &ntree,
bNode &output_node)
{
static const uiListType *state_items_list = []() {
uiListType *list = MEM_cnew<uiListType>(__func__);
STRNCPY(list->idname, "DATA_UL_simulation_zone_state");
list->draw_item = draw_simulation_state_item;
WM_uilisttype_add(list);
return list;
}();
PointerRNA output_node_ptr = RNA_pointer_create(&ntree.id, &RNA_Node, &output_node);
if (uiLayout *panel = uiLayoutPanel(
C, layout, "simulation_state_items", false, TIP_("Simulation State")))
{
uiLayout *row = uiLayoutRow(panel, false);
uiTemplateList(row,
C,
state_items_list->idname,
"",
&output_node_ptr,
"state_items",
&output_node_ptr,
"active_index",
nullptr,
3,
5,
UILST_LAYOUT_DEFAULT,
0,
UI_TEMPLATE_LIST_FLAG_NONE);
{
uiLayout *ops_col = uiLayoutColumn(row, false);
{
uiLayout *add_remove_col = uiLayoutColumn(ops_col, true);
uiItemO(add_remove_col, "", ICON_ADD, "node.simulation_zone_item_add");
uiItemO(add_remove_col, "", ICON_REMOVE, "node.simulation_zone_item_remove");
}
{
uiLayout *up_down_col = uiLayoutColumn(ops_col, true);
uiItemEnumO(
up_down_col, "node.simulation_zone_item_move", "", ICON_TRIA_UP, "direction", 0);
uiItemEnumO(
up_down_col, "node.simulation_zone_item_move", "", ICON_TRIA_DOWN, "direction", 1);
}
}
auto &storage = *static_cast<NodeGeometrySimulationOutput *>(output_node.storage);
if (storage.active_index >= 0 && storage.active_index < storage.items_num) {
NodeSimulationItem &active_item = storage.items[storage.active_index];
PointerRNA item_ptr = RNA_pointer_create(
output_node_ptr.owner_id, SimulationItemsAccessor::item_srna, &active_item);
uiLayoutSetPropSep(panel, true);
uiLayoutSetPropDecorate(panel, false);
uiItemR(panel, &item_ptr, "socket_type", UI_ITEM_NONE, nullptr, ICON_NONE);
if (socket_type_supports_fields(eNodeSocketDatatype(active_item.socket_type))) {
uiItemR(panel, &item_ptr, "attribute_domain", UI_ITEM_NONE, nullptr, ICON_NONE);
}
}
}
}
static void NODE_OT_simulation_zone_item_remove(wmOperatorType *ot)
{
socket_items::ops::remove_item<SimulationItemsAccessor>(
ot, "Remove Simulation Zone Item", __func__, "Remove active simulation zone item");
}
static void NODE_OT_simulation_zone_item_add(wmOperatorType *ot)
{
socket_items::ops::add_item_with_name_and_type<SimulationItemsAccessor>(
ot, "Add Simulation Zone Item", __func__, "Add simulation zone item");
}
static void NODE_OT_simulation_zone_item_move(wmOperatorType *ot)
{
socket_items::ops::move_item<SimulationItemsAccessor>(
ot, "Move Simulation Zone Item", __func__, "Move active simulation zone item");
}
/** Shared for simulation input and output node. */
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *current_node_ptr)
{
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(current_node_ptr->owner_id);
bNode *current_node = static_cast<bNode *>(current_node_ptr->data);
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
const bke::bNodeTreeZones *zones = ntree.zones();
if (!zones) {
return;
}
const bke::bNodeTreeZone *zone = zones->get_zone_by_node(current_node->identifier);
if (!zone) {
return;
}
if (!zone->output_node) {
return;
}
bNode &output_node = const_cast<bNode &>(*zone->output_node);
draw_simulation_state(C, layout, ntree, output_node);
if (snode == nullptr) {
return;
}
std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
ed::space_node::get_modifier_for_node_editor(*snode);
if (!object_and_modifier.has_value()) {
return;
}
const Object &object = *object_and_modifier->object;
const NodesModifierData &nmd = *object_and_modifier->nmd;
const std::optional<int32_t> bake_id = ed::space_node::find_nested_node_id_in_root(*snode,
output_node);
if (!bake_id.has_value()) {
return;
}
const NodesModifierBake *bake = nullptr;
for (const NodesModifierBake &iter_bake : Span{nmd.bakes, nmd.bakes_num}) {
if (iter_bake.id == *bake_id) {
bake = &iter_bake;
break;
}
}
if (bake == nullptr) {
return;
}
PointerRNA bake_rna = RNA_pointer_create(
const_cast<ID *>(&object.id), &RNA_NodesModifierBake, (void *)bake);
const std::optional<IndexRange> simulation_range = bke::bake::get_node_bake_frame_range(
*scene, object, nmd, *bake_id);
std::optional<IndexRange> baked_range;
if (nmd.runtime->cache) {
const bke::bake::ModifierCache &cache = *nmd.runtime->cache;
std::lock_guard lock{cache.mutex};
if (const std::unique_ptr<bke::bake::SimulationNodeCache> *node_cache_ptr =
cache.simulation_cache_by_id.lookup_ptr(*bake_id))
{
const bke::bake::SimulationNodeCache &node_cache = **node_cache_ptr;
if (node_cache.cache_status == bke::bake::CacheStatus::Baked &&
!node_cache.bake.frames.is_empty())
{
const int first_frame = node_cache.bake.frames.first()->frame.frame();
const int last_frame = node_cache.bake.frames.last()->frame.frame();
baked_range = IndexRange(first_frame, last_frame - first_frame + 1);
}
}
}
bool is_baked = baked_range.has_value();
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayoutSetEnabled(layout, !ID_IS_LINKED(&object));
{
uiLayout *col = uiLayoutColumn(layout, false);
uiLayout *row = uiLayoutRow(col, true);
{
char bake_label[1024] = N_("Bake");
PointerRNA ptr;
uiItemFullO(row,
"OBJECT_OT_geometry_node_bake_single",
bake_label,
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&ptr);
WM_operator_properties_id_lookup_set_from_id(&ptr, &object.id);
RNA_string_set(&ptr, "modifier_name", nmd.modifier.name);
RNA_int_set(&ptr, "bake_id", bake->id);
}
{
PointerRNA ptr;
uiItemFullO(row,
"OBJECT_OT_geometry_node_bake_delete_single",
"",
ICON_TRASH,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&ptr);
WM_operator_properties_id_lookup_set_from_id(&ptr, &object.id);
RNA_string_set(&ptr, "modifier_name", nmd.modifier.name);
RNA_int_set(&ptr, "bake_id", bake->id);
}
if (is_baked) {
char baked_range_label[64];
SNPRINTF(baked_range_label,
N_("Baked %d - %d"),
int(baked_range->first()),
int(baked_range->last()));
uiItemL(layout, baked_range_label, ICON_NONE);
}
else if (simulation_range.has_value()) {
char simulation_range_label[64];
SNPRINTF(simulation_range_label,
N_("Frames %d - %d"),
int(simulation_range->first()),
int(simulation_range->last()));
uiItemL(layout, simulation_range_label, ICON_NONE);
}
}
{
uiLayout *settings_col = uiLayoutColumn(layout, false);
uiLayoutSetActive(settings_col, !is_baked);
{
uiLayout *col = uiLayoutColumn(settings_col, true);
uiLayoutSetActive(col, !is_baked);
uiItemR(col, &bake_rna, "use_custom_path", UI_ITEM_NONE, "Custom Path", ICON_NONE);
uiLayout *subcol = uiLayoutColumn(col, true);
uiLayoutSetActive(subcol, bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH);
uiItemR(subcol, &bake_rna, "directory", UI_ITEM_NONE, "Path", ICON_NONE);
}
{
uiLayout *col = uiLayoutColumn(settings_col, true);
uiItemR(col,
&bake_rna,
"use_custom_simulation_frame_range",
UI_ITEM_NONE,
"Custom Range",
ICON_NONE);
uiLayout *subcol = uiLayoutColumn(col, true);
uiLayoutSetActive(subcol, bake->flag & NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE);
uiItemR(subcol, &bake_rna, "frame_start", UI_ITEM_NONE, "Start", ICON_NONE);
uiItemR(subcol, &bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
}
}
draw_data_blocks(C, layout, bake_rna);
}
namespace sim_input_node {
NODE_STORAGE_FUNCS(NodeGeometrySimulationInput);
@@ -412,6 +678,7 @@ static void node_register()
ntype.insert_link = node_insert_link;
ntype.gather_link_search_ops = nullptr;
ntype.no_muting = true;
ntype.draw_buttons_ex = node_layout_ex;
node_type_storage(&ntype,
"NodeGeometrySimulationInput",
node_free_standard_storage,
@@ -744,144 +1011,11 @@ static void node_copy_storage(bNodeTree * /*dst_tree*/, bNode *dst_node, const b
socket_items::copy_array<SimulationItemsAccessor>(*src_node, *dst_node);
}
static void node_layout_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
static void node_operators()
{
const bNode *node = static_cast<bNode *>(ptr->data);
Scene *scene = CTX_data_scene(C);
SpaceNode *snode = CTX_wm_space_node(C);
if (snode == nullptr) {
return;
}
std::optional<ed::space_node::ObjectAndModifier> object_and_modifier =
ed::space_node::get_modifier_for_node_editor(*snode);
if (!object_and_modifier.has_value()) {
return;
}
const Object &object = *object_and_modifier->object;
const NodesModifierData &nmd = *object_and_modifier->nmd;
const std::optional<int32_t> bake_id = ed::space_node::find_nested_node_id_in_root(*snode,
*node);
if (!bake_id.has_value()) {
return;
}
const NodesModifierBake *bake = nullptr;
for (const NodesModifierBake &iter_bake : Span{nmd.bakes, nmd.bakes_num}) {
if (iter_bake.id == *bake_id) {
bake = &iter_bake;
break;
}
}
if (bake == nullptr) {
return;
}
PointerRNA bake_rna = RNA_pointer_create(
const_cast<ID *>(&object.id), &RNA_NodesModifierBake, (void *)bake);
const std::optional<IndexRange> simulation_range = bke::bake::get_node_bake_frame_range(
*scene, object, nmd, *bake_id);
std::optional<IndexRange> baked_range;
if (nmd.runtime->cache) {
const bke::bake::ModifierCache &cache = *nmd.runtime->cache;
std::lock_guard lock{cache.mutex};
if (const std::unique_ptr<bke::bake::SimulationNodeCache> *node_cache_ptr =
cache.simulation_cache_by_id.lookup_ptr(*bake_id))
{
const bke::bake::SimulationNodeCache &node_cache = **node_cache_ptr;
if (node_cache.cache_status == bke::bake::CacheStatus::Baked &&
!node_cache.bake.frames.is_empty())
{
const int first_frame = node_cache.bake.frames.first()->frame.frame();
const int last_frame = node_cache.bake.frames.last()->frame.frame();
baked_range = IndexRange(first_frame, last_frame - first_frame + 1);
}
}
}
bool is_baked = baked_range.has_value();
uiLayoutSetPropSep(layout, true);
uiLayoutSetPropDecorate(layout, false);
uiLayoutSetEnabled(layout, !ID_IS_LINKED(&object));
{
uiLayout *col = uiLayoutColumn(layout, false);
uiLayout *row = uiLayoutRow(col, true);
{
char bake_label[1024] = N_("Bake");
PointerRNA ptr;
uiItemFullO(row,
"OBJECT_OT_geometry_node_bake_single",
bake_label,
ICON_NONE,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&ptr);
WM_operator_properties_id_lookup_set_from_id(&ptr, &object.id);
RNA_string_set(&ptr, "modifier_name", nmd.modifier.name);
RNA_int_set(&ptr, "bake_id", bake->id);
}
{
PointerRNA ptr;
uiItemFullO(row,
"OBJECT_OT_geometry_node_bake_delete_single",
"",
ICON_TRASH,
nullptr,
WM_OP_INVOKE_DEFAULT,
UI_ITEM_NONE,
&ptr);
WM_operator_properties_id_lookup_set_from_id(&ptr, &object.id);
RNA_string_set(&ptr, "modifier_name", nmd.modifier.name);
RNA_int_set(&ptr, "bake_id", bake->id);
}
if (is_baked) {
char baked_range_label[64];
SNPRINTF(baked_range_label,
N_("Baked %d - %d"),
int(baked_range->first()),
int(baked_range->last()));
uiItemL(layout, baked_range_label, ICON_NONE);
}
else if (simulation_range.has_value()) {
char simulation_range_label[64];
SNPRINTF(simulation_range_label,
N_("Frames %d - %d"),
int(simulation_range->first()),
int(simulation_range->last()));
uiItemL(layout, simulation_range_label, ICON_NONE);
}
}
{
uiLayout *settings_col = uiLayoutColumn(layout, false);
uiLayoutSetActive(settings_col, !is_baked);
{
uiLayout *col = uiLayoutColumn(settings_col, true);
uiLayoutSetActive(col, !is_baked);
uiItemR(col, &bake_rna, "use_custom_path", UI_ITEM_NONE, "Custom Path", ICON_NONE);
uiLayout *subcol = uiLayoutColumn(col, true);
uiLayoutSetActive(subcol, bake->flag & NODES_MODIFIER_BAKE_CUSTOM_PATH);
uiItemR(subcol, &bake_rna, "directory", UI_ITEM_NONE, "Path", ICON_NONE);
}
{
uiLayout *col = uiLayoutColumn(settings_col, true);
uiItemR(col,
&bake_rna,
"use_custom_simulation_frame_range",
UI_ITEM_NONE,
"Custom Range",
ICON_NONE);
uiLayout *subcol = uiLayoutColumn(col, true);
uiLayoutSetActive(subcol, bake->flag & NODES_MODIFIER_BAKE_CUSTOM_SIMULATION_FRAME_RANGE);
uiItemR(subcol, &bake_rna, "frame_start", UI_ITEM_NONE, "Start", ICON_NONE);
uiItemR(subcol, &bake_rna, "frame_end", UI_ITEM_NONE, "End", ICON_NONE);
}
}
draw_data_blocks(C, layout, bake_rna);
WM_operatortype_append(NODE_OT_simulation_zone_item_add);
WM_operatortype_append(NODE_OT_simulation_zone_item_remove);
WM_operatortype_append(NODE_OT_simulation_zone_item_move);
}
static bool node_insert_link(bNodeTree *ntree, bNode *node, bNodeLink *link)
@@ -903,6 +1037,7 @@ static void node_register()
ntype.insert_link = node_insert_link;
ntype.draw_buttons_ex = node_layout_ex;
ntype.no_muting = true;
ntype.register_operators = node_operators;
node_type_storage(&ntype, "NodeGeometrySimulationOutput", node_free_storage, node_copy_storage);
nodeRegisterType(&ntype);
}