Modifiers: Affect all selected objects when holding alt
Support holding alt while invoking modifier operations for add, apply, remove, and move to index in the property editor. This affects all selected editable objects instead of just the active object. Though the alt key is not that visible, it's consistent with the existing multi-object property editing shortcut. If/when multi- object editing is every made the default, the alt key could be reversed here too. Changes as part of #120230. Pull Request: https://projects.blender.org/blender/blender/pulls/120695
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
import bpy
|
||||
from bpy.types import Operator
|
||||
from bpy.props import IntProperty
|
||||
from bpy.props import IntProperty, BoolProperty
|
||||
|
||||
from bpy.app.translations import pgettext_data as data_
|
||||
|
||||
@@ -136,6 +136,101 @@ def get_enabled_socket_with_name(sockets, name):
|
||||
return socket
|
||||
return None
|
||||
|
||||
def create_wrapper_group(modifier, old_group):
|
||||
wrapper_name = old_group.name + ".wrapper"
|
||||
group = bpy.data.node_groups.new(wrapper_name, 'GeometryNodeTree')
|
||||
group.interface.new_socket(data_("Geometry"), in_out='OUTPUT', socket_type='NodeSocketGeometry')
|
||||
group.is_modifier = True
|
||||
|
||||
first_geometry_input = next((item for item in old_group.interface.items_tree if item.item_type == 'SOCKET' and
|
||||
item.in_out == 'INPUT' and
|
||||
item.bl_socket_idname == 'NodeSocketGeometry'), None)
|
||||
if first_geometry_input:
|
||||
group.interface.new_socket(data_("Geometry"), in_out='INPUT', socket_type='NodeSocketGeometry')
|
||||
group_input_node = group.nodes.new('NodeGroupInput')
|
||||
group_input_node.location.x = -200 - group_input_node.width
|
||||
group_input_node.select = False
|
||||
|
||||
group_output_node = group.nodes.new('NodeGroupOutput')
|
||||
group_output_node.is_active_output = True
|
||||
group_output_node.location.x = 200
|
||||
group_output_node.select = False
|
||||
|
||||
group_node = group.nodes.new("GeometryNodeGroup")
|
||||
group_node.node_tree = old_group
|
||||
group_node.update()
|
||||
|
||||
# Copy default values for inputs and create named attribute input nodes.
|
||||
input_nodes = []
|
||||
for input_socket in old_group.interface.items_tree:
|
||||
if input_socket.item_type != 'SOCKET' or (input_socket.in_out not in {'INPUT', 'BOTH'}):
|
||||
continue
|
||||
identifier = input_socket.identifier
|
||||
group_node_input = get_socket_with_identifier(group_node.inputs, identifier)
|
||||
if modifier_input_use_attribute(modifier, identifier):
|
||||
input_node = group.nodes.new("GeometryNodeInputNamedAttribute")
|
||||
input_nodes.append(input_node)
|
||||
input_node.data_type = socket_idname_to_attribute_type(input_socket.bl_socket_idname)
|
||||
attribute_name = modifier_attribute_name_get(modifier, identifier)
|
||||
input_node.inputs["Name"].default_value = attribute_name
|
||||
output_socket = get_enabled_socket_with_name(input_node.outputs, "Attribute")
|
||||
group.links.new(output_socket, group_node_input)
|
||||
elif hasattr(input_socket, "default_value"):
|
||||
group_node_input.default_value = modifier[identifier]
|
||||
|
||||
if first_geometry_input:
|
||||
group.links.new(group_input_node.outputs[0],
|
||||
get_socket_with_identifier(group_node.inputs, first_geometry_input.identifier))
|
||||
|
||||
# Adjust locations of named attribute input nodes and group input node to make some space.
|
||||
if input_nodes:
|
||||
for i, node in enumerate(input_nodes):
|
||||
node.location.x = -175
|
||||
node.location.y = i * -50
|
||||
group_input_node.location.x = -350
|
||||
|
||||
# Connect outputs to store named attribute nodes to replace modifier attribute outputs.
|
||||
store_nodes = []
|
||||
first_geometry_output = None
|
||||
for output_socket in old_group.interface.items_tree:
|
||||
if output_socket.item_type != 'SOCKET' or (output_socket.in_out not in {'OUTPUT', 'BOTH'}):
|
||||
continue
|
||||
identifier = output_socket.identifier
|
||||
group_node_output = get_socket_with_identifier(group_node.outputs, identifier)
|
||||
attribute_name = modifier_attribute_name_get(modifier, identifier)
|
||||
if attribute_name:
|
||||
store_node = group.nodes.new("GeometryNodeStoreNamedAttribute")
|
||||
store_nodes.append(store_node)
|
||||
store_node.data_type = socket_idname_to_attribute_type(output_socket.bl_socket_idname)
|
||||
store_node.domain = output_socket.attribute_domain
|
||||
store_node.inputs["Name"].default_value = attribute_name
|
||||
input_socket = get_enabled_socket_with_name(store_node.inputs, "Value")
|
||||
group.links.new(group_node_output, input_socket)
|
||||
elif output_socket.bl_socket_idname == 'NodeSocketGeometry':
|
||||
if not first_geometry_output:
|
||||
first_geometry_output = group_node_output
|
||||
|
||||
# Adjust locations of store named attribute nodes and move group output.
|
||||
# Note that the node group has its sockets names translated, while the built-in nodes don't.
|
||||
if store_nodes:
|
||||
for i, node in enumerate(store_nodes):
|
||||
node.location.x = (i + 1) * 175
|
||||
node.location.y = 0
|
||||
group_output_node.location.x = (len(store_nodes) + 1) * 175
|
||||
|
||||
group.links.new(first_geometry_output, store_nodes[0].inputs["Geometry"])
|
||||
for i in range(len(store_nodes) - 1):
|
||||
group.links.new(store_nodes[i].outputs["Geometry"], store_nodes[i + 1].inputs["Geometry"])
|
||||
|
||||
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs[data_("Geometry")])
|
||||
else:
|
||||
if not first_geometry_output:
|
||||
self.report({'WARNING'}, "Node group must have a geometry output")
|
||||
return {'CANCELLED'}
|
||||
group.links.new(first_geometry_output, group_output_node.inputs[data_("Geometry")])
|
||||
|
||||
return group
|
||||
|
||||
|
||||
class MoveModifierToNodes(Operator):
|
||||
"""Move inputs and outputs from in the modifier to a new node group"""
|
||||
@@ -144,111 +239,40 @@ class MoveModifierToNodes(Operator):
|
||||
bl_label = "Move to Nodes"
|
||||
bl_options = {'REGISTER', 'UNDO'}
|
||||
|
||||
use_selected_objects: BoolProperty(
|
||||
name="Selected Objects",
|
||||
description="Affect all selected objects instead of just the active object",
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return edit_geometry_nodes_modifier_poll(context)
|
||||
|
||||
def invoke(self, context, event):
|
||||
if event.alt:
|
||||
self.use_selected_objects = True
|
||||
return self.execute(context)
|
||||
|
||||
def execute(self, context):
|
||||
modifier = get_context_modifier(context)
|
||||
if not modifier:
|
||||
return {'CANCELLED'}
|
||||
old_group = modifier.node_group
|
||||
if not old_group:
|
||||
active_modifier = get_context_modifier(context)
|
||||
if not active_modifier:
|
||||
return {'CANCELLED'}
|
||||
modifier_name = active_modifier.name
|
||||
|
||||
wrapper_name = old_group.name + ".wrapper"
|
||||
group = bpy.data.node_groups.new(wrapper_name, 'GeometryNodeTree')
|
||||
group.interface.new_socket(data_("Geometry"), in_out='OUTPUT', socket_type='NodeSocketGeometry')
|
||||
group.is_modifier = True
|
||||
|
||||
first_geometry_input = next((item for item in old_group.interface.items_tree if item.item_type == 'SOCKET' and
|
||||
item.in_out == 'INPUT' and
|
||||
item.bl_socket_idname == 'NodeSocketGeometry'), None)
|
||||
if first_geometry_input:
|
||||
group.interface.new_socket(data_("Geometry"), in_out='INPUT', socket_type='NodeSocketGeometry')
|
||||
group_input_node = group.nodes.new('NodeGroupInput')
|
||||
group_input_node.location.x = -200 - group_input_node.width
|
||||
group_input_node.select = False
|
||||
|
||||
group_output_node = group.nodes.new('NodeGroupOutput')
|
||||
group_output_node.is_active_output = True
|
||||
group_output_node.location.x = 200
|
||||
group_output_node.select = False
|
||||
|
||||
group_node = group.nodes.new("GeometryNodeGroup")
|
||||
group_node.node_tree = old_group
|
||||
group_node.update()
|
||||
|
||||
# Copy default values for inputs and create named attribute input nodes.
|
||||
input_nodes = []
|
||||
for input_socket in old_group.interface.items_tree:
|
||||
if input_socket.item_type != 'SOCKET' or (input_socket.in_out not in {'INPUT', 'BOTH'}):
|
||||
continue
|
||||
identifier = input_socket.identifier
|
||||
group_node_input = get_socket_with_identifier(group_node.inputs, identifier)
|
||||
if modifier_input_use_attribute(modifier, identifier):
|
||||
input_node = group.nodes.new("GeometryNodeInputNamedAttribute")
|
||||
input_nodes.append(input_node)
|
||||
input_node.data_type = socket_idname_to_attribute_type(input_socket.bl_socket_idname)
|
||||
attribute_name = modifier_attribute_name_get(modifier, identifier)
|
||||
input_node.inputs["Name"].default_value = attribute_name
|
||||
output_socket = get_enabled_socket_with_name(input_node.outputs, "Attribute")
|
||||
group.links.new(output_socket, group_node_input)
|
||||
elif hasattr(input_socket, "default_value"):
|
||||
group_node_input.default_value = modifier[identifier]
|
||||
|
||||
if first_geometry_input:
|
||||
group.links.new(group_input_node.outputs[0],
|
||||
get_socket_with_identifier(group_node.inputs, first_geometry_input.identifier))
|
||||
|
||||
# Adjust locations of named attribute input nodes and group input node to make some space.
|
||||
if input_nodes:
|
||||
for i, node in enumerate(input_nodes):
|
||||
node.location.x = -175
|
||||
node.location.y = i * -50
|
||||
group_input_node.location.x = -350
|
||||
|
||||
# Connect outputs to store named attribute nodes to replace modifier attribute outputs.
|
||||
store_nodes = []
|
||||
first_geometry_output = None
|
||||
for output_socket in old_group.interface.items_tree:
|
||||
if output_socket.item_type != 'SOCKET' or (output_socket.in_out not in {'OUTPUT', 'BOTH'}):
|
||||
continue
|
||||
identifier = output_socket.identifier
|
||||
group_node_output = get_socket_with_identifier(group_node.outputs, identifier)
|
||||
attribute_name = modifier_attribute_name_get(modifier, identifier)
|
||||
if attribute_name:
|
||||
store_node = group.nodes.new("GeometryNodeStoreNamedAttribute")
|
||||
store_nodes.append(store_node)
|
||||
store_node.data_type = socket_idname_to_attribute_type(output_socket.bl_socket_idname)
|
||||
store_node.domain = output_socket.attribute_domain
|
||||
store_node.inputs["Name"].default_value = attribute_name
|
||||
input_socket = get_enabled_socket_with_name(store_node.inputs, "Value")
|
||||
group.links.new(group_node_output, input_socket)
|
||||
elif output_socket.bl_socket_idname == 'NodeSocketGeometry':
|
||||
if not first_geometry_output:
|
||||
first_geometry_output = group_node_output
|
||||
|
||||
# Adjust locations of store named attribute nodes and move group output.
|
||||
# Note that the node group has its sockets names translated, while the built-in nodes don't.
|
||||
if store_nodes:
|
||||
for i, node in enumerate(store_nodes):
|
||||
node.location.x = (i + 1) * 175
|
||||
node.location.y = 0
|
||||
group_output_node.location.x = (len(store_nodes) + 1) * 175
|
||||
|
||||
group.links.new(first_geometry_output, store_nodes[0].inputs["Geometry"])
|
||||
for i in range(len(store_nodes) - 1):
|
||||
group.links.new(store_nodes[i].outputs["Geometry"], store_nodes[i + 1].inputs["Geometry"])
|
||||
|
||||
group.links.new(store_nodes[-1].outputs["Geometry"], group_output_node.inputs[data_("Geometry")])
|
||||
objects = []
|
||||
if self.use_selected_objects:
|
||||
objects = context.selected_editable_objects
|
||||
else:
|
||||
if not first_geometry_output:
|
||||
self.report({'WARNING'}, "Node group must have a geometry output")
|
||||
return {'CANCELLED'}
|
||||
group.links.new(first_geometry_output, group_output_node.inputs[data_("Geometry")])
|
||||
objects = [context.object]
|
||||
|
||||
modifier.node_group = group
|
||||
for ob in objects:
|
||||
modifier = ob.modifiers[modifier_name]
|
||||
if not modifier:
|
||||
continue
|
||||
old_group = modifier.node_group
|
||||
if not old_group:
|
||||
continue
|
||||
modifier.node_group = create_wrapper_group(modifier, old_group)
|
||||
|
||||
return {'FINISHED'}
|
||||
|
||||
|
||||
@@ -66,7 +66,7 @@ class OBJECT_MT_modifier_add(ModifierAddMenu, Menu):
|
||||
icon='VIEWZOOM').menu_idname = "OBJECT_MT_modifier_add"
|
||||
layout.separator()
|
||||
|
||||
layout.operator_context = 'EXEC_REGION_WIN'
|
||||
layout.operator_context = 'INVOKE_REGION_WIN'
|
||||
|
||||
if geometry_nodes_supported:
|
||||
self.operator_modifier_add(layout, 'NODES')
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_define.hh"
|
||||
|
||||
#include "ED_asset.hh"
|
||||
#include "ED_asset_menu_utils.hh"
|
||||
@@ -295,11 +296,9 @@ static int modifier_add_asset_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *object = context_active_object(C);
|
||||
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(
|
||||
modifier_add(op->reports, bmain, scene, object, nullptr, eModifierType_Nodes));
|
||||
if (!nmd) {
|
||||
Vector<PointerRNA> objects = modifier_get_edit_objects(*C, *op);
|
||||
if (objects.is_empty()) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
@@ -308,21 +307,43 @@ static int modifier_add_asset_exec(bContext *C, wmOperator *op)
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
nmd->node_group = node_group;
|
||||
id_us_plus(&node_group->id);
|
||||
MOD_nodes_update_interface(object, nmd);
|
||||
bool changed = false;
|
||||
for (const PointerRNA &ptr : objects) {
|
||||
Object *object = static_cast<Object *>(ptr.data);
|
||||
NodesModifierData *nmd = reinterpret_cast<NodesModifierData *>(
|
||||
modifier_add(op->reports, bmain, scene, object, nullptr, eModifierType_Nodes));
|
||||
if (!nmd) {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
nmd->node_group = node_group;
|
||||
id_us_plus(&node_group->id);
|
||||
MOD_nodes_update_interface(object, nmd);
|
||||
|
||||
/* By default, don't show the data-block selector since it's not usually necessary for assets. */
|
||||
nmd->flag |= NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR;
|
||||
/* Don't show the data-block selector since it's not usually necessary for assets. */
|
||||
nmd->flag |= NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR;
|
||||
|
||||
STRNCPY(nmd->modifier.name, DATA_(node_group->id.name + 2));
|
||||
BKE_modifier_unique_name(&object->modifiers, &nmd->modifier);
|
||||
STRNCPY(nmd->modifier.name, DATA_(node_group->id.name + 2));
|
||||
BKE_modifier_unique_name(&object->modifiers, &nmd->modifier);
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int modifier_add_asset_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (event->modifier & KM_ALT) {
|
||||
RNA_boolean_set(op->ptr, "use_selected_objects", true);
|
||||
}
|
||||
return modifier_add_asset_exec(C, op);
|
||||
}
|
||||
|
||||
static std::string modifier_add_asset_get_description(bContext *C,
|
||||
wmOperatorType * /*ot*/,
|
||||
PointerRNA *ptr)
|
||||
@@ -344,6 +365,7 @@ static void OBJECT_OT_modifier_add_node_group(wmOperatorType *ot)
|
||||
ot->description = "Add a procedural operation/effect to the active object";
|
||||
ot->idname = "OBJECT_OT_modifier_add_node_group";
|
||||
|
||||
ot->invoke = modifier_add_asset_invoke;
|
||||
ot->exec = modifier_add_asset_exec;
|
||||
ot->poll = ED_operator_object_active_editable;
|
||||
ot->get_description = modifier_add_asset_get_description;
|
||||
@@ -352,6 +374,7 @@ static void OBJECT_OT_modifier_add_node_group(wmOperatorType *ot)
|
||||
|
||||
asset::operator_asset_reference_props_register(*ot->srna);
|
||||
WM_operator_properties_id_lookup(ot, false);
|
||||
modifier_register_use_selected_objects_prop(ot);
|
||||
}
|
||||
|
||||
static MenuType modifier_add_unassigned_assets_menu_type()
|
||||
|
||||
@@ -8,6 +8,10 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "RNA_types.hh"
|
||||
|
||||
struct bContext;
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
@@ -379,4 +383,7 @@ void object_modifier_add_asset_register();
|
||||
|
||||
void collection_exporter_register();
|
||||
|
||||
Vector<PointerRNA> modifier_get_edit_objects(const bContext &C, const wmOperator &op);
|
||||
void modifier_register_use_selected_objects_prop(wmOperatorType *ot);
|
||||
|
||||
} // namespace blender::ed::object
|
||||
|
||||
@@ -1233,6 +1233,29 @@ bool modifier_copy(
|
||||
|
||||
/** \} */
|
||||
|
||||
Vector<PointerRNA> modifier_get_edit_objects(const bContext &C, const wmOperator &op)
|
||||
{
|
||||
Vector<PointerRNA> objects;
|
||||
if (RNA_boolean_get(op.ptr, "use_selected_objects")) {
|
||||
CTX_data_selected_editable_objects(&C, &objects);
|
||||
}
|
||||
else {
|
||||
if (Object *object = context_active_object(&C)) {
|
||||
objects.append(RNA_id_pointer_create(&object->id));
|
||||
}
|
||||
}
|
||||
return objects;
|
||||
}
|
||||
|
||||
void modifier_register_use_selected_objects_prop(wmOperatorType *ot)
|
||||
{
|
||||
RNA_def_boolean(ot->srna,
|
||||
"use_selected_objects",
|
||||
false,
|
||||
"Selected Objects",
|
||||
"Affect all selected objects instead of just the active object");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------- */
|
||||
/** \name Add Modifier Operator
|
||||
* \{ */
|
||||
@@ -1241,18 +1264,35 @@ static int modifier_add_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = context_active_object(C);
|
||||
int type = RNA_enum_get(op->ptr, "type");
|
||||
|
||||
if (!modifier_add(op->reports, bmain, scene, ob, nullptr, type)) {
|
||||
bool changed = false;
|
||||
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
||||
Object *ob = static_cast<Object *>(ptr.data);
|
||||
if (!modifier_add(op->reports, bmain, scene, ob, nullptr, type)) {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
}
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
|
||||
static int modifier_add_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
||||
{
|
||||
if (event->modifier & KM_ALT) {
|
||||
RNA_boolean_set(op->ptr, "use_selected_objects", true);
|
||||
}
|
||||
if (!RNA_struct_property_is_set(op->ptr, "type")) {
|
||||
return WM_menu_invoke(C, op, event);
|
||||
}
|
||||
return modifier_add_exec(C, op);
|
||||
}
|
||||
|
||||
static const EnumPropertyItem *modifier_add_itemf(bContext *C,
|
||||
PointerRNA * /*ptr*/,
|
||||
PropertyRNA * /*prop*/,
|
||||
@@ -1311,7 +1351,7 @@ void OBJECT_OT_modifier_add(wmOperatorType *ot)
|
||||
ot->idname = "OBJECT_OT_modifier_add";
|
||||
|
||||
/* api callbacks */
|
||||
ot->invoke = WM_menu_invoke;
|
||||
ot->invoke = modifier_add_invoke;
|
||||
ot->exec = modifier_add_exec;
|
||||
ot->poll = ED_operator_object_active_editable;
|
||||
|
||||
@@ -1323,6 +1363,7 @@ void OBJECT_OT_modifier_add(wmOperatorType *ot)
|
||||
ot->srna, "type", rna_enum_object_modifier_type_items, eModifierType_Subsurf, "Type", "");
|
||||
RNA_def_enum_funcs(prop, modifier_add_itemf);
|
||||
ot->prop = prop;
|
||||
modifier_register_use_selected_objects_prop(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1432,6 +1473,12 @@ static bool edit_modifier_invoke_properties_with_hover(bContext *C,
|
||||
const wmEvent *event,
|
||||
int *r_retval)
|
||||
{
|
||||
if (RNA_struct_find_property(op->ptr, "use_selected_objects")) {
|
||||
if (event->modifier & KM_ALT) {
|
||||
RNA_boolean_set(op->ptr, "use_selected_objects", true);
|
||||
}
|
||||
}
|
||||
|
||||
if (RNA_struct_property_is_set(op->ptr, "modifier")) {
|
||||
return true;
|
||||
}
|
||||
@@ -1488,34 +1535,42 @@ static int modifier_remove_exec(bContext *C, wmOperator *op)
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
ViewLayer *view_layer = CTX_data_view_layer(C);
|
||||
Object *ob = context_active_object(C);
|
||||
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
||||
int mode_orig = ob->mode;
|
||||
|
||||
if (md == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
/* Store name temporarily for report. */
|
||||
char name[MAX_NAME];
|
||||
STRNCPY(name, md->name);
|
||||
RNA_string_get(op->ptr, "modifier", name);
|
||||
|
||||
if (!modifier_remove(op->reports, bmain, scene, ob, md)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
bool changed = false;
|
||||
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
||||
Object *ob = static_cast<Object *>(ptr.data);
|
||||
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
||||
if (md == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
int mode_orig = ob->mode;
|
||||
if (!modifier_remove(op->reports, bmain, scene, ob, md)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* if cloth/softbody was removed, particle mode could be cleared */
|
||||
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
|
||||
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
|
||||
BKE_view_layer_synced_ensure(scene, view_layer);
|
||||
if (ob == BKE_view_layer_active_object_get(view_layer)) {
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr);
|
||||
changed = true;
|
||||
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
/* if cloth/softbody was removed, particle mode could be cleared */
|
||||
if (mode_orig & OB_MODE_PARTICLE_EDIT) {
|
||||
if ((ob->mode & OB_MODE_PARTICLE_EDIT) == 0) {
|
||||
BKE_view_layer_synced_ensure(scene, view_layer);
|
||||
if (ob == BKE_view_layer_active_object_get(view_layer)) {
|
||||
WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (RNA_boolean_get(op->ptr, "report")) {
|
||||
BKE_reportf(op->reports, RPT_INFO, "Removed modifier: %s", name);
|
||||
}
|
||||
@@ -1546,6 +1601,7 @@ void OBJECT_OT_modifier_remove(wmOperatorType *ot)
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
edit_modifier_report_property(ot);
|
||||
modifier_register_use_selected_objects_prop(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1646,11 +1702,26 @@ void OBJECT_OT_modifier_move_down(wmOperatorType *ot)
|
||||
|
||||
static int modifier_move_to_index_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Object *ob = context_active_object(C);
|
||||
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
||||
int index = RNA_int_get(op->ptr, "index");
|
||||
char name[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "modifier", name);
|
||||
|
||||
if (!(md && modifier_move_to_index(op->reports, RPT_WARNING, ob, md, index, true))) {
|
||||
const int index = RNA_int_get(op->ptr, "index");
|
||||
|
||||
bool changed = false;
|
||||
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
||||
Object *ob = static_cast<Object *>(ptr.data);
|
||||
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
||||
if (!md) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modifier_move_to_index(op->reports, RPT_WARNING, ob, md, index, true)) {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
@@ -1682,6 +1753,7 @@ void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot)
|
||||
edit_modifier_properties(ot);
|
||||
RNA_def_int(
|
||||
ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the modifier to", 0, INT_MAX);
|
||||
modifier_register_use_selected_objects_prop(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1722,9 +1794,13 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = context_active_object(C);
|
||||
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
||||
Vector<PointerRNA> objects = modifier_get_edit_objects(*C, *op);
|
||||
|
||||
char name[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "modifier", name);
|
||||
|
||||
const bool do_report = RNA_boolean_get(op->ptr, "report");
|
||||
const int reports_len = do_report ? BLI_listbase_count(&op->reports->list) : 0;
|
||||
|
||||
const bool do_single_user = (apply_as == MODIFIER_APPLY_DATA) ?
|
||||
RNA_boolean_get(op->ptr, "single_user") :
|
||||
@@ -1733,40 +1809,43 @@ static int modifier_apply_exec_ex(bContext *C, wmOperator *op, int apply_as, boo
|
||||
RNA_boolean_get(op->ptr, "merge_customdata") :
|
||||
false;
|
||||
|
||||
if (md == nullptr) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
bool changed = false;
|
||||
for (const PointerRNA &ptr : objects) {
|
||||
Object *ob = static_cast<Object *>(ptr.data);
|
||||
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
||||
if (md == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
const ModifierTypeInfo *mti = BKE_modifier_get_info((ModifierType)md->type);
|
||||
|
||||
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
|
||||
single_obdata_user_make(bmain, scene, ob);
|
||||
BKE_main_id_newptr_and_tag_clear(bmain);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
if (do_single_user && ID_REAL_USERS(ob->data) > 1) {
|
||||
single_obdata_user_make(bmain, scene, ob);
|
||||
BKE_main_id_newptr_and_tag_clear(bmain);
|
||||
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
||||
DEG_relations_tag_update(bmain);
|
||||
}
|
||||
|
||||
if (!modifier_apply(bmain, op->reports, depsgraph, scene, ob, md, apply_as, keep_modifier)) {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
|
||||
if (ob->type == OB_MESH && do_merge_customdata &&
|
||||
ELEM(mti->type, ModifierTypeType::Constructive, ModifierTypeType::Nonconstructive))
|
||||
{
|
||||
BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
}
|
||||
|
||||
int reports_len;
|
||||
char name[MAX_NAME];
|
||||
if (do_report) {
|
||||
reports_len = BLI_listbase_count(&op->reports->list);
|
||||
STRNCPY(name, md->name); /* Store name temporarily since the modifier is removed. */
|
||||
}
|
||||
|
||||
if (!modifier_apply(bmain, op->reports, depsgraph, scene, ob, md, apply_as, keep_modifier)) {
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
if (ob->type == OB_MESH && do_merge_customdata &&
|
||||
ELEM(mti->type, ModifierTypeType::Constructive, ModifierTypeType::Nonconstructive))
|
||||
{
|
||||
BKE_mesh_merge_customdata_for_apply_modifier((Mesh *)ob->data);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
|
||||
if (do_report) {
|
||||
/* Only add this report if the operator didn't cause another one. The purpose here is
|
||||
* to alert that something happened, and the previous report will do that anyway. */
|
||||
@@ -1839,6 +1918,7 @@ void OBJECT_OT_modifier_apply(wmOperatorType *ot)
|
||||
"Make Data Single User",
|
||||
"Make the object's data single user if needed");
|
||||
RNA_def_property_flag(prop, (PropertyFlag)(PROP_HIDDEN | PROP_SKIP_SAVE));
|
||||
modifier_register_use_selected_objects_prop(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
@@ -1958,16 +2038,29 @@ static int modifier_copy_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
Scene *scene = CTX_data_scene(C);
|
||||
Object *ob = context_active_object(C);
|
||||
ModifierData *md = edit_modifier_property_get(op, ob, 0);
|
||||
char name[MAX_NAME];
|
||||
RNA_string_get(op->ptr, "modifier", name);
|
||||
|
||||
if (!md || !modifier_copy(op->reports, bmain, scene, ob, md)) {
|
||||
return OPERATOR_CANCELLED;
|
||||
bool changed = false;
|
||||
for (const PointerRNA &ptr : modifier_get_edit_objects(*C, *op)) {
|
||||
Object *ob = static_cast<Object *>(ptr.data);
|
||||
ModifierData *md = BKE_modifiers_findby_name(ob, name);
|
||||
if (!md) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!modifier_copy(op->reports, bmain, scene, ob, md)) {
|
||||
continue;
|
||||
}
|
||||
changed = true;
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
}
|
||||
|
||||
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
|
||||
DEG_relations_tag_update(bmain);
|
||||
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
|
||||
if (!changed) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
@@ -1994,6 +2087,7 @@ void OBJECT_OT_modifier_copy(wmOperatorType *ot)
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
|
||||
edit_modifier_properties(ot);
|
||||
modifier_register_use_selected_objects_prop(ot);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
@@ -265,8 +265,7 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
|
||||
uiItemS(layout);
|
||||
|
||||
/* Move to first. */
|
||||
row = uiLayoutColumn(layout, false);
|
||||
uiItemFullO(row,
|
||||
uiItemFullO(layout,
|
||||
"OBJECT_OT_modifier_move_to_index",
|
||||
IFACE_("Move to First"),
|
||||
ICON_TRIA_UP,
|
||||
@@ -275,13 +274,9 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
|
||||
UI_ITEM_NONE,
|
||||
&op_ptr);
|
||||
RNA_int_set(&op_ptr, "index", 0);
|
||||
if (!md->prev) {
|
||||
uiLayoutSetEnabled(row, false);
|
||||
}
|
||||
|
||||
/* Move to last. */
|
||||
row = uiLayoutColumn(layout, false);
|
||||
uiItemFullO(row,
|
||||
uiItemFullO(layout,
|
||||
"OBJECT_OT_modifier_move_to_index",
|
||||
IFACE_("Move to Last"),
|
||||
ICON_TRIA_DOWN,
|
||||
@@ -290,9 +285,6 @@ static void modifier_ops_extra_draw(bContext *C, uiLayout *layout, void *md_v)
|
||||
UI_ITEM_NONE,
|
||||
&op_ptr);
|
||||
RNA_int_set(&op_ptr, "index", BLI_listbase_count(&ob->modifiers) - 1);
|
||||
if (!md->next) {
|
||||
uiLayoutSetEnabled(row, false);
|
||||
}
|
||||
|
||||
if (md->type == eModifierType_Nodes) {
|
||||
uiItemS(layout);
|
||||
|
||||
Reference in New Issue
Block a user