Modifiers: Affect all selected objects when holding alt #120695
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue