Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,91 @@
# SPDX-FileCopyrightText: 2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
set(INC
../include
../../ikplugin
../../makesrna
../../modifiers
../../nodes
../../python
../../shader_fx
# RNA_prototypes.hh
${CMAKE_BINARY_DIR}/source/blender/makesrna
)
set(INC_SYS
)
set(SRC
add_modifier_assets.cc
interface_template_shape_key_tree.cc
object_add.cc
object_bake.cc
object_bake_api.cc
object_bake_simulation.cc
object_camera.cc
object_collection.cc
object_constraint.cc
object_data_transfer.cc
object_data_transform.cc
object_edit.cc
object_hook.cc
object_light_linking_ops.cc
object_modes.cc
object_modifier.cc
object_multires_modifier.cc
object_ops.cc
object_random.cc
object_relations.cc
object_remesh.cc
object_select.cc
object_shader_fx.cc
object_shapekey.cc
object_transform.cc
object_utils.cc
object_vgroup.cc
object_visual_geometry_to_objects.cc
object_volume.cc
object_warp.cc
object_intern.hh
)
set(LIB
PRIVATE bf::animrig
PRIVATE bf::asset_system
PRIVATE bf::blenfont
PRIVATE bf::blenkernel
PRIVATE bf::blenlib
PRIVATE bf::blentranslation
PRIVATE bf::bmesh
PRIVATE bf::depsgraph
PRIVATE bf::dna
bf_editor_mesh
bf_editor_metaball
bf_editor_grease_pencil
PRIVATE bf::functions
PRIVATE bf::geometry
PRIVATE bf::gpu
PRIVATE bf::imbuf
PRIVATE bf::intern::clog
PRIVATE bf::intern::guardedalloc
PRIVATE bf::render
PRIVATE bf::windowmanager
)
if(WITH_PYTHON)
add_definitions(-DWITH_PYTHON)
endif()
if(WITH_EXPERIMENTAL_FEATURES)
add_definitions(-DWITH_SIMULATION_DATABLOCK)
endif()
blender_add_lib(bf_editor_object "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
# RNA_prototypes.hh
add_dependencies(bf_editor_object bf_rna)

View File

@@ -0,0 +1,435 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "AS_asset_catalog.hh"
#include "AS_asset_catalog_tree.hh"
#include "AS_asset_library.hh"
#include "AS_asset_representation.hh"
#include "BLI_listbase.h"
#include "BLI_multi_value_map.hh"
#include "BLI_string_utf8.h"
#include "DNA_modifier_types.h"
#include "DNA_screen_types.h"
#include "DNA_space_types.h"
#include "BKE_asset.hh"
#include "BKE_context.hh"
#include "BKE_idprop.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_modifier.hh"
#include "BKE_report.hh"
#include "BKE_screen.hh"
#include "BLT_translation.hh"
#include "RNA_access.hh"
#include "ED_asset.hh"
#include "ED_asset_menu_utils.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "MOD_nodes.hh"
#include "NOD_geometry.hh"
#include "UI_interface.hh"
#include "UI_interface_layout.hh"
#include "WM_api.hh"
#include "object_intern.hh"
namespace blender::ed::object {
static bool all_loading_finished()
{
AssetLibraryReference all_library_ref = asset_system::all_library_reference();
return asset::list::is_loaded(&all_library_ref);
}
static asset::AssetItemTree build_catalog_tree(const bContext &C)
{
asset::AssetFilterSettings type_filter{};
type_filter.id_types = FILTER_ID_NT;
auto meta_data_filter = [&](const AssetMetaData &meta_data) {
const IDProperty *tree_type = BKE_asset_metadata_idprop_find(&meta_data, "type");
if (tree_type == nullptr || IDP_int_get(tree_type) != NTREE_GEOMETRY) {
return false;
}
const IDProperty *traits_flag = BKE_asset_metadata_idprop_find(
&meta_data, "geometry_node_asset_traits_flag");
if (traits_flag == nullptr || !(IDP_int_get(traits_flag) & GEO_NODE_ASSET_MODIFIER)) {
return false;
}
return true;
};
const AssetLibraryReference library = asset_system::all_library_reference();
asset_system::all_library_reload_catalogs_if_dirty();
return asset::build_filtered_all_catalog_tree(
library, C, type_filter, meta_data_filter, ntreeType_Geometry->asset_catalog_path_prefix);
}
static asset::AssetItemTree *get_static_item_tree()
{
static asset::AssetItemTree tree;
return &tree;
}
static void catalog_assets_draw(const bContext *C, Menu *menu)
{
asset::AssetItemTree &tree = *get_static_item_tree();
const std::optional<StringRefNull> menu_path = CTX_data_string_get(C, "asset_catalog_path");
if (!menu_path) {
return;
}
const int skip_essentials = CTX_data_int_get(C, "skip_essentials").value_or(0);
const Span<asset_system::AssetRepresentation *> assets = tree.assets_per_path.lookup(
menu_path->data());
const asset_system::AssetCatalogTreeItem *catalog_item = tree.catalogs.find_item(
menu_path->data());
BLI_assert(catalog_item != nullptr);
if (assets.is_empty() && !catalog_item->has_children()) {
return;
}
ui::Layout &layout = *menu->layout;
bool first = true;
const auto ensure_separator = [&]() {
if (first) {
layout.separator();
first = false;
}
};
for (const asset_system::AssetRepresentation *asset : assets) {
if (skip_essentials) {
if (ELEM(asset->owner_asset_library().library_reference()->type,
ASSET_LIBRARY_ESSENTIALS,
ASSET_LIBRARY_ONLINE_ESSENTIALS))
{
continue;
}
}
ensure_separator();
asset::draw_asset_menu_item(asset, "OBJECT_OT_modifier_add_node_group", layout);
}
catalog_item->foreach_child([&](const asset_system::AssetCatalogTreeItem &item) {
ensure_separator();
asset::draw_menu_for_catalog(item, "OBJECT_MT_add_modifier_catalog_assets", layout);
});
}
static bool unassigned_local_poll(const Main &bmain)
{
for (const bNodeTree &group : bmain.nodetrees) {
/* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
if (group.id.library_weak_reference || group.id.asset_data) {
continue;
}
if (!group.geometry_node_asset_traits ||
!(group.geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
{
continue;
}
return true;
}
return false;
}
static void unassigned_assets_draw(const bContext *C, Menu *menu)
{
Main &bmain = *CTX_data_main(C);
asset::AssetItemTree &tree = *get_static_item_tree();
ui::Layout &layout = *menu->layout;
wmOperatorType *ot = WM_operatortype_find("OBJECT_OT_modifier_add_node_group", true);
for (const asset_system::AssetRepresentation *asset : tree.unassigned_assets) {
asset::draw_asset_menu_item(asset, ot->idname, layout);
}
bool first = true;
bool add_separator = !tree.unassigned_assets.is_empty();
for (const bNodeTree &group : bmain.nodetrees) {
/* Assets are displayed in other menus, and non-local data-blocks aren't added to this menu. */
if (group.id.library_weak_reference || group.id.asset_data) {
continue;
}
if (!group.geometry_node_asset_traits ||
!(group.geometry_node_asset_traits->flag & GEO_NODE_ASSET_MODIFIER))
{
continue;
}
if (add_separator) {
layout.separator();
add_separator = false;
}
if (first) {
layout.label(IFACE_("Non-Assets"), ICON_NONE);
first = false;
}
PointerRNA props_ptr = layout.op(
ot, group.id.name + 2, ICON_NONE, wm::OpCallContext::InvokeDefault, UI_ITEM_NONE);
WM_operator_properties_id_lookup_set_from_id(&props_ptr, &group.id);
}
}
static void root_catalogs_draw(const bContext *C, Menu *menu)
{
const Object *object = context_active_object(C);
if (!object) {
return;
}
ui::Layout &layout = *menu->layout;
const bool loading_finished = all_loading_finished();
asset::AssetItemTree &tree = *get_static_item_tree();
tree = build_catalog_tree(*C);
if (tree.catalogs.is_empty() && loading_finished) {
return;
}
layout.separator();
if (!loading_finished) {
layout.label(IFACE_("Loading Asset Libraries"), ICON_INFO);
}
Set<std::string> all_builtin_menus = [&]() {
Set<std::string> menus;
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE)) {
menus.add_new("Edit");
}
if (ELEM(object->type,
OB_MESH,
OB_CURVES_LEGACY,
OB_FONT,
OB_SURF,
OB_VOLUME,
OB_GREASE_PENCIL))
{
menus.add_new("Generate");
}
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE, OB_VOLUME)) {
menus.add_new("Deform");
}
if (ELEM(object->type, OB_MESH)) {
menus.add_new("Normals");
}
if (ELEM(object->type, OB_MESH, OB_CURVES_LEGACY, OB_FONT, OB_SURF, OB_LATTICE)) {
menus.add_new("Physics");
}
return menus;
}();
tree.catalogs.foreach_root_item([&](const asset_system::AssetCatalogTreeItem &item) {
if (!all_builtin_menus.contains(item.get_name())) {
asset::draw_menu_for_catalog(item, "OBJECT_MT_add_modifier_catalog_assets", layout);
}
});
if (!tree.unassigned_assets.is_empty() || unassigned_local_poll(*CTX_data_main(C))) {
layout.separator();
layout.menu(
"OBJECT_MT_add_modifier_unassigned_assets", IFACE_("Unassigned"), ICON_FILE_HIDDEN);
}
}
static bNodeTree *get_asset_or_local_node_group(const bContext &C,
PointerRNA &ptr,
ReportList *reports)
{
Main &bmain = *CTX_data_main(&C);
if (bNodeTree *group = reinterpret_cast<bNodeTree *>(
WM_operator_properties_id_lookup_from_name_or_session_uid(&bmain, &ptr, ID_NT)))
{
return group;
}
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(C, ptr, reports);
if (!asset) {
return nullptr;
}
return reinterpret_cast<bNodeTree *>(asset::asset_local_id_ensure_imported(bmain, *asset));
}
static bNodeTree *get_node_group(const bContext &C, PointerRNA &ptr, ReportList *reports)
{
bNodeTree *node_group = get_asset_or_local_node_group(C, ptr, reports);
if (!node_group || ID_MISSING(node_group)) {
return nullptr;
}
if (node_group->type != NTREE_GEOMETRY) {
if (reports) {
BKE_report(reports, RPT_ERROR, "Asset is not a geometry node group");
}
return nullptr;
}
return node_group;
}
static wmOperatorStatus modifier_add_asset_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Vector<PointerRNA> objects = modifier_get_edit_objects(*C, *op);
if (objects.is_empty()) {
return OPERATOR_CANCELLED;
}
bNodeTree *node_group = get_node_group(*C, *op->ptr, op->reports);
if (!node_group) {
return OPERATOR_CANCELLED;
}
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);
/* Don't show the data-block selector since it's not usually necessary for assets. */
nmd->flag |= NODES_MODIFIER_HIDE_DATABLOCK_SELECTOR;
SET_FLAG_FROM_TEST(nmd->flag,
node_group->geometry_node_asset_traits &&
(node_group->geometry_node_asset_traits->flag &
GEO_NODE_ASSET_HIDE_MODIFIER_MANAGE_PANEL),
NODES_MODIFIER_HIDE_MANAGE_PANEL);
STRNCPY_UTF8(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);
}
if (!changed) {
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
static wmOperatorStatus modifier_add_asset_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
if (event->modifier & KM_ALT || CTX_wm_view3d(C)) {
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)
{
const asset_system::AssetRepresentation *asset =
asset::operator_asset_reference_props_get_asset_from_all_library(*C, *ptr, nullptr);
if (!asset) {
return "";
}
if (!asset->get_metadata().description) {
return "";
}
return TIP_(asset->get_metadata().description);
}
static void OBJECT_OT_modifier_add_node_group(wmOperatorType *ot)
{
ot->name = "Add Modifier";
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;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
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()
{
MenuType type{};
STRNCPY_UTF8(type.idname, "OBJECT_MT_add_modifier_unassigned_assets");
type.draw = unassigned_assets_draw;
type.listener = asset::list::asset_reading_region_listen_fn;
type.description = N_(
"Modifier node group assets not assigned to a catalog.\n"
"Catalogs can be assigned in the Asset Browser");
return type;
}
static MenuType modifier_add_catalog_assets_menu_type()
{
MenuType type{};
STRNCPY_UTF8(type.idname, "OBJECT_MT_add_modifier_catalog_assets");
type.draw = catalog_assets_draw;
type.listener = asset::list::asset_reading_region_listen_fn;
type.flag = MenuTypeFlag::ContextDependent;
return type;
}
static MenuType modifier_add_root_catalogs_menu_type()
{
MenuType type{};
STRNCPY_UTF8(type.idname, "OBJECT_MT_modifier_add_root_catalogs");
type.draw = root_catalogs_draw;
type.listener = asset::list::asset_reading_region_listen_fn;
type.flag = MenuTypeFlag::ContextDependent;
return type;
}
void object_modifier_add_asset_register()
{
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_catalog_assets_menu_type()));
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_unassigned_assets_menu_type()));
WM_menutype_add(MEM_new<MenuType>(__func__, modifier_add_root_catalogs_menu_type()));
WM_operatortype_append(OBJECT_OT_modifier_add_node_group);
}
void ui_template_modifier_asset_menu_items(ui::Layout &layout,
const StringRef catalog_path,
const bool skip_essentials)
{
asset::AssetItemTree &tree = *get_static_item_tree();
const asset_system::AssetCatalogTreeItem *item = tree.catalogs.find_root_item(catalog_path);
if (!item) {
return;
}
asset_system::AssetLibrary *all_library = asset::list::library_get_once_available(
asset_system::all_library_reference());
if (!all_library) {
return;
}
ui::Layout &col = layout.column(false);
col.context_string_set("asset_catalog_path", item->catalog_path().str());
col.context_int_set("skip_essentials", skip_essentials);
col.menu_contents("OBJECT_MT_add_modifier_catalog_assets");
}
} // namespace blender::ed::object

View File

@@ -0,0 +1,348 @@
/* SPDX-FileCopyrightText: 2025 Blender Foundation
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edinterface
*/
#include <fmt/format.h>
#include "BKE_context.hh"
#include "BKE_key.hh"
#include "BKE_object.hh"
#include "BLI_listbase.h"
#include "BLT_translation.hh"
#include "UI_interface_layout.hh"
#include "UI_tree_view.hh"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DNA_key_types.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_undo.hh"
namespace blender::ed::object::shapekey {
class ShapeKeyTreeView : public ui::AbstractTreeView {
protected:
Object &object_;
public:
ShapeKeyTreeView(Object &ob) : object_(ob)
{
is_flat_ = true;
};
void build_tree() override;
};
struct ShapeKey {
Object *object;
Key *key;
KeyBlock *kb;
int index;
};
class ShapeKeyDragController : public ui::AbstractViewItemDragController {
private:
ShapeKey drag_key_;
public:
ShapeKeyDragController(ShapeKeyTreeView &view, ShapeKey drag_key)
: AbstractViewItemDragController(view), drag_key_(drag_key)
{
}
std::optional<eWM_DragDataType> get_drag_type() const override
{
return WM_DRAG_SHAPE_KEY;
}
void *create_drag_data() const override
{
int selected_count = [&]() -> int {
int count = 0;
for (KeyBlock &kb : drag_key_.key->block) {
count += (kb.flag & KEYBLOCK_SEL) != 0;
}
return count;
}();
/* Allocate one extra element, to use it as null-delimiter. */
KeyBlock **selected_keys_ = MEM_new_array_zeroed<KeyBlock *>(selected_count + 1,
"Selected Key Blocks");
selected_count = 0;
for (const auto [index, kb] : drag_key_.key->block.enumerate()) {
if (index == 0) {
/* Prevent basis shape key from dragging. */
continue;
}
if (kb.flag & KEYBLOCK_SEL) {
selected_keys_[selected_count] = &kb;
selected_count++;
}
}
BLI_assert_msg(selected_keys_[selected_count] == nullptr,
"Expected last element to be null (null-delimiter)");
return selected_keys_;
}
};
class ShapeKeyDropTarget : public ui::TreeViewItemDropTarget {
private:
KeyBlock &drop_kb_;
int drop_index_;
public:
ShapeKeyDropTarget(ui::AbstractTreeViewItem &item,
ui::DropBehavior behavior,
KeyBlock &drop_kb,
int index)
: TreeViewItemDropTarget(item, behavior), drop_kb_(drop_kb), drop_index_(index)
{
}
bool can_drop(const wmDrag &drag, const char ** /*r_disabled_hint*/) const override
{
if (drag.type != WM_DRAG_SHAPE_KEY) {
return false;
}
const KeyBlock **drag_shapekey = static_cast<const KeyBlock **>(drag.poin);
if (!drag_shapekey || !drag_shapekey[0]) {
return false;
}
return true;
}
std::string drop_tooltip(const ui::DragInfo &drag_info) const override
{
const StringRef drag_name = TIP_("Selected Keys");
const StringRef drop_name = drop_kb_.name;
switch (drag_info.drop_location) {
case ui::DropLocation::Into:
BLI_assert_unreachable();
break;
case ui::DropLocation::Before:
if (drop_index_ == 0) {
return TIP_("Cannot move above basis shape key");
}
return fmt::format(fmt::runtime(TIP_("Move {} above {}")), drag_name, drop_name);
case ui::DropLocation::After:
return fmt::format(fmt::runtime(TIP_("Move {} below {}")), drag_name, drop_name);
default:
BLI_assert_unreachable();
break;
}
return "";
}
bool on_drop(bContext *C, const ui::DragInfo &drag_info) const override
{
Object *ob = CTX_data_active_object(C);
Key *key = BKE_key_from_object(ob);
const KeyBlock **drag_shapekey = static_cast<const KeyBlock **>(drag_info.drag_data.poin);
const int first_drag_index = BLI_findindex(&key->block, drag_shapekey[0]);
int drop_index = BLI_findindex(&key->block, &drop_kb_);
switch (drag_info.drop_location) {
case ui::DropLocation::Into:
BLI_assert_unreachable();
break;
case ui::DropLocation::Before:
if (drop_index == 0) {
return false;
}
drop_index -= int(first_drag_index < drop_index);
break;
case ui::DropLocation::After:
drop_index += int(first_drag_index > drop_index);
break;
}
for (int8_t i = 0; drag_shapekey[i] != nullptr; i++) {
const int drag_index = BLI_findindex(&key->block, drag_shapekey[i]);
if (drag_index == -1) {
continue;
}
if (i > 0) {
/* Place subsequent items directly after the previously moved item. */
drop_index += int(drag_index > drop_index);
}
BKE_keyblock_move(ob, drag_index, drop_index);
}
DEG_id_tag_update(ob->data, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
ED_undo_push(C, "Drop Active Shape Key");
return true;
}
};
class ShapeKeyItem : public ui::AbstractTreeViewItem {
private:
ShapeKey shape_key_;
public:
ShapeKeyItem(Object *object, Key *key, KeyBlock *kb, int index)
{
label_ = kb->name;
shape_key_.object = object;
shape_key_.key = key;
shape_key_.kb = kb;
shape_key_.index = index;
};
void build_row(ui::Layout &row) override
{
uiItemL_ex(&row, this->label_, ICON_SHAPEKEY_DATA, false, false);
ui::Layout &sub = row.row(true);
sub.alignment_set(ui::LayoutAlign::Right);
sub.use_property_decorate_set(false);
PointerRNA shapekey_ptr = RNA_pointer_create_discrete(
&shape_key_.key->id, RNA_ShapeKey, shape_key_.kb);
if (shape_key_.key->type == KEY_NORMAL) {
sub.prop(&shapekey_ptr, "frame", ui::ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
}
else {
if (shape_key_.index > 0) {
sub.prop(&shapekey_ptr, "value", ui::ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
}
}
sub.prop(&shapekey_ptr, "mute", ui::ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
sub.prop(&shapekey_ptr, "lock_shape", ui::ITEM_R_ICON_ONLY, std::nullopt, ICON_NONE);
if (shape_key_.kb->flag & KEYBLOCK_MUTE) {
row.active_set(false);
}
}
std::optional<bool> should_be_active() const override
{
return shape_key_.object->shapenr == shape_key_.index + 1;
}
void on_activate(bContext &C) override
{
PointerRNA object_ptr = RNA_pointer_create_discrete(
&shape_key_.object->id, RNA_Object, shape_key_.object);
PropertyRNA *prop = RNA_struct_find_property(&object_ptr, "active_shape_key_index");
RNA_property_int_set(&object_ptr, prop, shape_key_.index);
RNA_property_update(&C, &object_ptr, prop);
ED_undo_push(&C, "Set Active Shape Key");
}
std::optional<bool> should_be_selected() const override
{
return shape_key_.kb->flag & KEYBLOCK_SEL;
}
void set_selected(const bool select) override
{
AbstractViewItem::set_selected(select);
SET_FLAG_FROM_TEST(shape_key_.kb->flag, select, KEYBLOCK_SEL);
}
bool supports_renaming() const override
{
return true;
}
bool rename(const bContext &C, StringRefNull new_name) override
{
PointerRNA shapekey_ptr = RNA_pointer_create_discrete(
&shape_key_.key->id, RNA_ShapeKey, shape_key_.kb);
PropertyRNA *prop = RNA_struct_find_property(&shapekey_ptr, "name");
RNA_property_string_set(&shapekey_ptr, prop, new_name.c_str());
RNA_property_update(&const_cast<bContext &>(C), &shapekey_ptr, prop);
ED_undo_push(const_cast<bContext *>(&C), "Rename shape key");
return true;
}
StringRef get_rename_string() const override
{
return label_;
}
void delete_item(bContext *C) override
{
Main *bmain = CTX_data_main(C);
BKE_object_shapekey_remove(bmain, shape_key_.object, shape_key_.kb);
DEG_id_tag_update(&shape_key_.object->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, nullptr);
ED_undo_grouped_push(C, "Delete Shape Key");
}
void build_context_menu(bContext &C, ui::Layout &layout) const override
{
MenuType *mt = WM_menutype_find("MESH_MT_shape_key_tree_context_menu", true);
if (!mt) {
return;
}
ui::menutype_draw(&C, mt, &layout);
}
std::unique_ptr<ui::AbstractViewItemDragController> create_drag_controller() const override
{
return std::make_unique<ShapeKeyDragController>(
static_cast<ShapeKeyTreeView &>(get_tree_view()), shape_key_);
}
std::unique_ptr<ui::TreeViewItemDropTarget> create_drop_target() override
{
return std::make_unique<ShapeKeyDropTarget>(
*this, ui::DropBehavior::Reorder, *shape_key_.kb, shape_key_.index);
}
};
void ShapeKeyTreeView::build_tree()
{
Key *key = BKE_key_from_object(&object_);
if (key == nullptr) {
return;
}
for (const auto [index, kb] : key->block.enumerate()) {
this->add_tree_item<ShapeKeyItem>(&object_, key, &kb, index);
}
}
void template_tree(ui::Layout *layout, bContext *C)
{
Object *ob = CTX_data_active_object(C);
if (ob == nullptr) {
return;
}
ui::Block *block = layout->block();
ui::AbstractTreeView *tree_view = block_add_view(
*block,
"Shape Key Tree View",
std::make_unique<ed::object::shapekey::ShapeKeyTreeView>(*ob));
tree_view->set_context_menu_title("Shape Key");
tree_view->set_default_rows(4);
tree_view->allow_multiselect_items();
ui::TreeViewBuilder::build_tree_view(*C, *tree_view, *layout);
}
} // namespace blender::ed::object::shapekey

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,573 @@
/* SPDX-FileCopyrightText: 2004 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include <cstring>
#include "MEM_guardedalloc.h"
#include "DNA_layer_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "BLI_listbase.h"
#include "BLI_span.hh"
#include "BLI_utildefines.h"
#include "BLI_vector.hh"
#include "BKE_attribute.hh"
#include "BKE_context.hh"
#include "BKE_customdata.hh"
#include "BKE_global.hh"
#include "BKE_image.hh"
#include "BKE_modifier.hh"
#include "BKE_multires.hh"
#include "BKE_report.hh"
#include "BKE_scene.hh"
#include "RE_multires_bake.h"
#include "RE_pipeline.h"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_screen.hh"
#include "ED_uvedit.hh"
#include "object_intern.hh"
namespace blender::ed::object {
static Image *bake_object_image_get(Object &object, const int mat_nr)
{
Image *image = nullptr;
ED_object_get_active_image(&object, mat_nr + 1, &image, nullptr, nullptr, nullptr);
return image;
}
static Vector<Image *> bake_object_image_get_array(Object &object)
{
Vector<Image *> images;
images.reserve(object.totcol);
for (int i = 0; i < object.totcol; i++) {
images.append(bake_object_image_get(object, i));
}
return images;
}
/* ****************** multires BAKING ********************** */
/* holder of per-object data needed for bake job
* needed to make job totally thread-safe */
struct MultiresBakerJobData {
MultiresBakerJobData *next = nullptr, *prev = nullptr;
/* Material aligned image array (for per-face bake image). */
Vector<Image *> ob_image;
/* Base mesh at the input of the multiresolution modifier. */
Mesh *base_mesh = nullptr;
/* Multi-resolution modifier which is being baked. */
MultiresModifierData *multires_modifier = nullptr;
Set<Image *> images;
};
/* data passing to multires-baker job */
struct MultiresBakeJob {
Scene *scene;
ListBaseT<MultiresBakerJobData> data;
/** Clear the images before baking */
bool bake_clear;
/** Margin size in pixels. */
int bake_margin;
/** margin type */
char bake_margin_type;
/** mode of baking (displacement, normals, AO) */
eBakeType type;
eBakeSpace displacement_space;
/** Use low-resolution mesh when baking displacement maps */
bool use_low_resolution_mesh;
};
static bool multiresbake_check(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob;
Mesh *mesh;
MultiresModifierData *mmd;
bool ok = true;
int a;
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
ob = base->object;
if (ob->type != OB_MESH) {
BKE_report(
op->reports, RPT_ERROR, "Baking of multires data only works with an active mesh object");
ok = false;
break;
}
mesh = id_cast<Mesh *>(ob->data);
mmd = get_multires_modifier(scene, ob, false);
/* Multi-resolution should be and be last in the stack */
if (ok && mmd) {
ModifierData *md;
ok = mmd->totlvl > 0;
for (md = static_cast<ModifierData *>(mmd->modifier.next); md && ok; md = md->next) {
if (BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) {
ok = false;
}
}
}
else {
ok = false;
}
if (!ok) {
BKE_report(op->reports, RPT_ERROR, "Multires data baking requires multi-resolution object");
break;
}
if (mesh->uv_map_names().is_empty()) {
BKE_report(op->reports, RPT_ERROR, "Mesh should be unwrapped before multires data baking");
ok = false;
}
else {
const bke::AttributeAccessor attributes = mesh->attributes();
const VArraySpan material_indices = *attributes.lookup<int>("material_index",
bke::AttrDomain::Face);
a = mesh->faces_num;
while (ok && a--) {
Image *ima = bake_object_image_get(*ob,
material_indices.is_empty() ? 0 : material_indices[a]);
if (!ima) {
BKE_report(
op->reports, RPT_ERROR, "You should have active texture to use multires baker");
ok = false;
}
else {
for (ImageTile &tile : ima->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile.tile_number;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, &iuser, nullptr);
if (!ibuf) {
BKE_report(
op->reports, RPT_ERROR, "Baking should happen to image with image buffer");
ok = false;
}
else {
if (ibuf->byte_data() == nullptr && ibuf->float_data() == nullptr) {
ok = false;
}
if (ibuf->float_data() && !ELEM(ibuf->channels, 0, 4)) {
ok = false;
}
if (!ok) {
BKE_report(op->reports, RPT_ERROR, "Baking to unsupported image type");
}
}
BKE_image_release_ibuf(ima, ibuf, nullptr);
}
}
}
}
if (!ok) {
break;
}
}
CTX_DATA_END;
return ok;
}
enum ClearFlag {
CLEAR_TANGENT_NORMAL = 1,
CLEAR_DISPLACEMENT = 2,
};
static void clear_single_image(Image *image, ClearFlag flag)
{
const float vec_alpha[4] = {0.0f, 0.0f, 0.0f, 0.0f};
const float vec_solid[4] = {0.0f, 0.0f, 0.0f, 1.0f};
const float nor_alpha[4] = {0.5f, 0.5f, 1.0f, 0.0f};
const float nor_solid[4] = {0.5f, 0.5f, 1.0f, 1.0f};
const float disp_alpha[4] = {0.5f, 0.5f, 0.5f, 0.0f};
const float disp_solid[4] = {0.5f, 0.5f, 0.5f, 1.0f};
if ((image->id.tag & ID_TAG_DOIT) == 0) {
for (ImageTile &tile : image->tiles) {
ImageUser iuser;
BKE_imageuser_default(&iuser);
iuser.tile = tile.tile_number;
ImBuf *ibuf = BKE_image_acquire_ibuf(image, &iuser, nullptr);
if (flag == CLEAR_TANGENT_NORMAL) {
IMB_rectfill(ibuf, ibuf->can_contain_alpha() ? nor_alpha : nor_solid);
}
else if (flag == CLEAR_DISPLACEMENT) {
IMB_rectfill(ibuf, ibuf->can_contain_alpha() ? disp_alpha : disp_solid);
}
else {
IMB_rectfill(ibuf, ibuf->can_contain_alpha() ? vec_alpha : vec_solid);
}
image->id.tag |= ID_TAG_DOIT;
BKE_image_release_ibuf(image, ibuf, nullptr);
}
}
}
static void clear_images_poly(const Span<Image *> ob_image_array, const ClearFlag flag)
{
for (Image *image : ob_image_array) {
if (image) {
image->id.tag &= ~ID_TAG_DOIT;
}
}
for (Image *image : ob_image_array) {
if (image) {
clear_single_image(image, flag);
}
}
for (Image *image : ob_image_array) {
if (image) {
image->id.tag &= ~ID_TAG_DOIT;
}
}
}
static wmOperatorStatus multiresbake_image_exec_locked(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
int objects_baked = 0;
if (!multiresbake_check(C, op)) {
return OPERATOR_CANCELLED;
}
if (scene->r.bake.flag & R_BAKE_CLEAR) { /* clear images */
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
Object &object = *base->object;
BLI_assert(object.type == OB_MESH);
ClearFlag clear_flag = ClearFlag(0);
if (scene->r.bake.type == R_BAKE_NORMALS) {
clear_flag = CLEAR_TANGENT_NORMAL;
}
else if (scene->r.bake.type == R_BAKE_DISPLACEMENT) {
clear_flag = CLEAR_DISPLACEMENT;
}
{
const Vector<Image *> ob_image_array = bake_object_image_get_array(object);
clear_images_poly(ob_image_array, clear_flag);
}
}
CTX_DATA_END;
}
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
Object &object = *base->object;
BLI_assert(object.type == OB_MESH);
MultiresBakeRender bake;
multires_flush_sculpt_updates(&object);
/* Copy data stored in job descriptor. */
bake.bake_margin = scene->r.bake.margin;
if (scene->r.bake.type == R_BAKE_NORMALS) {
bake.bake_margin_type = R_BAKE_EXTEND;
}
else {
bake.bake_margin_type = eBakeMarginType(scene->r.bake.margin_type);
}
bake.type = eBakeType(scene->r.bake.type);
bake.displacement_space = eBakeSpace(scene->r.bake.displacement_space);
bake.use_low_resolution_mesh = scene->r.bake.flag & R_BAKE_LORES_MESH;
bake.ob_image = bake_object_image_get_array(object);
bake.base_mesh = id_cast<Mesh *>(object.data);
bake.multires_modifier = get_multires_modifier(scene, &object, false);
RE_multires_bake_images(bake);
objects_baked++;
}
CTX_DATA_END;
if (!objects_baked) {
BKE_report(op->reports, RPT_ERROR, "No objects found to bake from");
}
return OPERATOR_FINISHED;
}
/**
* Multi-resolution-bake adopted for job-system executing.
*/
static void init_multiresbake_job(bContext *C, MultiresBakeJob *bkj)
{
Scene *scene = CTX_data_scene(C);
/* backup scene settings, so their changing in UI would take no effect on baker */
bkj->scene = scene;
bkj->bake_margin = scene->r.bake.margin;
if (scene->r.bake.type == R_BAKE_NORMALS) {
bkj->bake_margin_type = R_BAKE_EXTEND;
}
else {
bkj->bake_margin_type = eBakeMarginType(scene->r.bake.margin_type);
}
bkj->type = eBakeType(scene->r.bake.type);
bkj->displacement_space = eBakeSpace(scene->r.bake.displacement_space);
bkj->use_low_resolution_mesh = scene->r.bake.flag & R_BAKE_LORES_MESH;
bkj->bake_clear = scene->r.bake.flag & R_BAKE_CLEAR;
CTX_DATA_BEGIN (C, Base *, base, selected_editable_bases) {
Object &object = *base->object;
BLI_assert(object.type == OB_MESH);
multires_flush_sculpt_updates(&object);
MultiresBakerJobData *data = MEM_new<MultiresBakerJobData>(__func__);
data->ob_image = bake_object_image_get_array(object);
data->base_mesh = id_cast<Mesh *>(object.data);
data->multires_modifier = get_multires_modifier(scene, &object, false);
BLI_addtail(&bkj->data, data);
}
CTX_DATA_END;
}
static void multiresbake_startjob(void *bkv, wmJobWorkerStatus *worker_status)
{
MultiresBakeJob *bkj = static_cast<MultiresBakeJob *>(bkv);
int baked_objects = 0, tot_obj;
tot_obj = bkj->data.count();
if (bkj->bake_clear) { /* clear images */
for (MultiresBakerJobData &data : bkj->data) {
ClearFlag clear_flag = ClearFlag(0);
if (bkj->type == R_BAKE_NORMALS) {
clear_flag = CLEAR_TANGENT_NORMAL;
}
else if (bkj->type == R_BAKE_DISPLACEMENT) {
clear_flag = CLEAR_DISPLACEMENT;
}
clear_images_poly(data.ob_image, clear_flag);
}
}
for (MultiresBakerJobData &data : bkj->data) {
MultiresBakeRender bake;
/* copy data stored in job descriptor */
bake.bake_margin = bkj->bake_margin;
bake.bake_margin_type = eBakeMarginType(bkj->bake_margin_type);
bake.type = bkj->type;
bake.displacement_space = bkj->displacement_space;
bake.use_low_resolution_mesh = bkj->use_low_resolution_mesh;
bake.ob_image = data.ob_image;
bake.base_mesh = data.base_mesh;
bake.multires_modifier = data.multires_modifier;
/* needed for proper progress bar */
bake.num_total_objects = tot_obj;
bake.num_baked_objects = baked_objects;
bake.stop = &worker_status->stop;
bake.do_update = &worker_status->do_update;
bake.progress = &worker_status->progress;
RE_multires_bake_images(bake);
data.images = bake.images;
baked_objects++;
}
}
static void multiresbake_freejob(void *bkv)
{
MultiresBakeJob *bkj = static_cast<MultiresBakeJob *>(bkv);
MultiresBakerJobData *data, *next;
data = static_cast<MultiresBakerJobData *>(bkj->data.first);
while (data) {
next = data->next;
/* delete here, since this delete will be called from main thread */
for (Image *image : data->images) {
BKE_image_partial_update_mark_full_update(image);
}
MEM_delete(data);
data = next;
}
MEM_delete(bkj);
}
static wmOperatorStatus multiresbake_image_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
if (!multiresbake_check(C, op)) {
return OPERATOR_CANCELLED;
}
MultiresBakeJob *bkr = MEM_new_zeroed<MultiresBakeJob>(__func__);
init_multiresbake_job(C, bkr);
if (!bkr->data.first) {
BKE_report(op->reports, RPT_ERROR, "No objects found to bake from");
MEM_delete(bkr);
return OPERATOR_CANCELLED;
}
/* setup job */
wmJob *wm_job = WM_jobs_get(CTX_wm_manager(C),
CTX_wm_window(C),
scene,
"Baking Multires...",
WM_JOB_EXCL_RENDER | WM_JOB_PRIORITY | WM_JOB_PROGRESS,
WM_JOB_TYPE_OBJECT_BAKE_TEXTURE);
WM_jobs_customdata_set(wm_job, bkr, multiresbake_freejob);
WM_jobs_timer(wm_job, 0.5, NC_IMAGE, 0); /* TODO: only draw bake image, can we enforce this. */
WM_jobs_callbacks(wm_job, multiresbake_startjob, nullptr, nullptr, nullptr);
G.is_break = false;
WM_jobs_start(CTX_wm_manager(C), wm_job);
WM_cursor_wait(false);
/* add modal handler for ESC */
WM_event_add_modal_handler(C, op);
return OPERATOR_RUNNING_MODAL;
}
/* ****************** render BAKING ********************** */
/** Catch escape key to cancel. */
static wmOperatorStatus objects_bake_render_modal(bContext *C,
wmOperator * /*op*/,
const wmEvent *event)
{
/* no running blender, remove handler and pass through */
if (0 == WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C), WM_JOB_TYPE_OBJECT_BAKE_TEXTURE)) {
return OPERATOR_FINISHED | OPERATOR_PASS_THROUGH;
}
/* running render */
switch (event->type) {
case EVT_ESCKEY:
return OPERATOR_RUNNING_MODAL;
default: {
break;
}
}
return OPERATOR_PASS_THROUGH;
}
static bool is_multires_bake(Scene *scene)
{
if (ELEM(scene->r.bake.type,
R_BAKE_NORMALS,
R_BAKE_DISPLACEMENT,
R_BAKE_VECTOR_DISPLACEMENT,
R_BAKE_AO))
{
return scene->r.bake.flag & R_BAKE_MULTIRES;
}
return false;
}
static wmOperatorStatus objects_bake_render_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
Scene *scene = CTX_data_scene(C);
wmOperatorStatus result = OPERATOR_CANCELLED;
result = multiresbake_image_exec(C, op);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene);
return result;
}
static wmOperatorStatus bake_image_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
wmOperatorStatus result = OPERATOR_CANCELLED;
if (!is_multires_bake(scene)) {
BLI_assert(0);
return result;
}
result = multiresbake_image_exec_locked(C, op);
WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene);
return result;
}
void OBJECT_OT_bake_image(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Bake";
ot->description = "Bake image textures of selected objects";
ot->idname = "OBJECT_OT_bake_image";
/* API callbacks. */
ot->exec = bake_image_exec;
ot->invoke = objects_bake_render_invoke;
ot->modal = objects_bake_render_modal;
ot->poll = ED_operator_object_active;
}
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,88 @@
/* SPDX-FileCopyrightText: 2001-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include "object_intern.hh"
#include "DNA_camera_types.h"
#include "BKE_context.hh"
#include "ED_object.hh"
#include "RE_engine.h"
#include "WM_api.hh"
namespace blender::ed::object {
/* -------------------------------------------------------------------- */
/** \name Custom Camera Update
* \{ */
static bool object_camera_custom_update_poll(bContext *C)
{
Object *ob = context_active_object(C);
RenderEngineType *type = CTX_data_engine_type(C);
/* Test if we have a render engine that supports custom cameras. */
if (!(type && type->update_custom_camera)) {
return false;
}
/* See if we have a custom camera in context. */
if (ob == nullptr || ob->type != OB_CAMERA) {
return false;
}
Camera *cam = id_cast<Camera *>(ob->data);
if (cam == nullptr || cam->type != CAM_CUSTOM) {
return false;
}
if (cam->custom_mode == CAM_CUSTOM_SHADER_EXTERNAL) {
return cam->custom_filepath[0] != '\0';
}
else {
return cam->custom_shader != nullptr;
}
}
static wmOperatorStatus object_camera_custom_update_exec(bContext *C, wmOperator *op)
{
Object *ob = context_active_object(C);
RenderEngineType *type = CTX_data_engine_type(C);
Camera *cam = id_cast<Camera *>(ob->data);
/* setup render engine */
RenderEngine *engine = RE_engine_create(type);
engine->reports = op->reports;
type->update_custom_camera(engine, cam);
RE_engine_free(engine);
return OPERATOR_FINISHED;
}
void OBJECT_OT_camera_custom_update(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Custom Camera Update";
ot->description = "Update custom camera with new parameters from the shader";
ot->idname = "OBJECT_OT_camera_custom_update";
/* API callbacks. */
ot->exec = object_camera_custom_update_exec;
ot->poll = object_camera_custom_update_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,969 @@
/* SPDX-FileCopyrightText: 2014 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_math_matrix.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_customdata.hh"
#include "BKE_data_transfer.h"
#include "BKE_deform.hh"
#include "BKE_library.hh"
#include "BKE_mesh_remap.hh"
#include "BKE_object.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "BLT_translation.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "object_intern.hh"
namespace blender::ed::object {
/* All possible data to transfer.
* Note some are 'fake' ones, i.e. they are not hold by real CDLayers. */
/* Not shared with modifier, since we use a usual enum here, not a multi-choice one. */
static const EnumPropertyItem DT_layer_items[] = {
RNA_ENUM_ITEM_HEADING(N_("Vertex Data"), nullptr),
{DT_TYPE_MDEFORMVERT,
"VGROUP_WEIGHTS",
0,
"Vertex Group(s)",
"Transfer active or all vertex groups"},
{DT_TYPE_BWEIGHT_VERT, "BEVEL_WEIGHT_VERT", 0, "Bevel Weight", "Transfer bevel weights"},
{DT_TYPE_MPROPCOL_VERT | DT_TYPE_MLOOPCOL_VERT,
"COLOR_VERTEX",
0,
"Colors",
"Color Attributes"},
RNA_ENUM_ITEM_HEADING(N_("Edge Data"), nullptr),
{DT_TYPE_SHARP_EDGE, "SHARP_EDGE", 0, "Sharp", "Transfer sharp mark"},
{DT_TYPE_SEAM, "SEAM", 0, "UV Seam", "Transfer UV seam mark"},
{DT_TYPE_CREASE, "CREASE", 0, "Subdivision Crease", "Transfer crease values"},
{DT_TYPE_BWEIGHT_EDGE, "BEVEL_WEIGHT_EDGE", 0, "Bevel Weight", "Transfer bevel weights"},
{DT_TYPE_FREESTYLE_EDGE,
"FREESTYLE_EDGE",
0,
"Freestyle Mark",
"Transfer Freestyle edge mark"},
RNA_ENUM_ITEM_HEADING(N_("Face Corner Data"), nullptr),
{DT_TYPE_LNOR, "CUSTOM_NORMAL", 0, "Custom Normals", "Transfer custom normals"},
{DT_TYPE_MPROPCOL_LOOP | DT_TYPE_MLOOPCOL_LOOP,
"COLOR_CORNER",
0,
"Colors",
"Color Attributes"},
{DT_TYPE_UV, "UV", 0, "UVs", "Transfer UV layers"},
RNA_ENUM_ITEM_HEADING(N_("Face Data"), nullptr),
{DT_TYPE_SHARP_FACE, "SMOOTH", 0, "Smooth", "Transfer flat/smooth mark"},
{DT_TYPE_FREESTYLE_FACE,
"FREESTYLE_FACE",
0,
"Freestyle Mark",
"Transfer Freestyle face mark"},
{0, nullptr, 0, nullptr, nullptr},
};
static void dt_add_vcol_layers(const CustomData *cdata,
eCustomDataMask mask,
EnumPropertyItem **r_item,
int *r_totitem)
{
const int types[2] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR};
int idx = 0;
for (int i = 0; i < 2; i++) {
eCustomDataType type = eCustomDataType(types[i]);
if (!(mask & CD_TYPE_AS_MASK(type))) {
continue;
}
int data_num = CustomData_number_of_layers(cdata, type);
RNA_enum_item_add_separator(r_item, r_totitem);
for (int j = 0; j < data_num; j++) {
EnumPropertyItem tmp_item = {0};
tmp_item.value = idx++;
tmp_item.identifier = tmp_item.name = CustomData_get_layer_name(cdata, type, j);
RNA_enum_item_add(r_item, r_totitem, &tmp_item);
}
}
}
/* NOTE: #rna_enum_dt_layers_select_src_items enum is from rna_modifier.cc. */
static const EnumPropertyItem *dt_layers_select_src_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
bool *r_free)
{
if (!C) { /* needed for docs and i18n tools */
return rna_enum_dt_layers_select_src_items;
}
EnumPropertyItem *item = nullptr, tmp_item = {0};
int totitem = 0;
const int data_type = RNA_enum_get(ptr, "data_type");
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_reverse_transfer");
const bool reverse_transfer = prop != nullptr && RNA_property_boolean_get(ptr, prop);
const int layers_select_dst = reverse_transfer ? RNA_enum_get(ptr, "layers_select_src") :
RNA_enum_get(ptr, "layers_select_dst");
if (!reverse_transfer || layers_select_dst == DT_LAYERS_ACTIVE_DST || layers_select_dst >= 0) {
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_ACTIVE_SRC);
}
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_ALL_SRC);
Object *ob_src = context_active_object(C);
if (ob_src == nullptr) {
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
if (data_type == DT_TYPE_MDEFORMVERT && BKE_object_supports_vertex_groups(ob_src)) {
if (BKE_object_pose_armature_get(ob_src)) {
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_SELECT);
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_src_items, DT_LAYERS_VGROUP_SRC_BONE_DEFORM);
}
const bDeformGroup *dg;
int i;
RNA_enum_item_add_separator(&item, &totitem);
const ListBaseT<bDeformGroup> *defbase = BKE_object_defgroup_list(ob_src);
for (i = 0, dg = static_cast<const bDeformGroup *>(defbase->first); dg; i++, dg = dg->next) {
tmp_item.value = i;
tmp_item.identifier = tmp_item.name = dg->name;
RNA_enum_item_add(&item, &totitem, &tmp_item);
}
}
else if (data_type == DT_TYPE_UV) {
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const Object *ob_src_eval = DEG_get_evaluated(depsgraph, ob_src);
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(ob_src_eval);
if (!mesh_eval) {
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
VectorSet<StringRefNull> uv_map_names = mesh_eval->uv_map_names();
RNA_enum_item_add_separator(&item, &totitem);
for (int i = 0; i < uv_map_names.size(); i++) {
tmp_item.value = i;
tmp_item.identifier = tmp_item.name = uv_map_names[i].c_str();
RNA_enum_item_add(&item, &totitem, &tmp_item);
}
}
else if (data_type & DT_TYPE_VCOL_ALL) {
const Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
const Object *ob_src_eval = DEG_get_evaluated(depsgraph, ob_src);
const Mesh *mesh_eval = BKE_object_get_evaluated_mesh_no_subsurf(ob_src_eval);
if (!mesh_eval) {
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
CustomData_MeshMasks cddata_masks = CD_MASK_BAREMESH;
if (data_type & (DT_TYPE_MPROPCOL_VERT)) {
cddata_masks.vmask |= CD_MASK_PROP_COLOR;
}
if (data_type & (DT_TYPE_MLOOPCOL_VERT)) {
cddata_masks.vmask |= CD_MASK_PROP_BYTE_COLOR;
}
if (data_type & (DT_TYPE_MPROPCOL_LOOP)) {
cddata_masks.lmask |= CD_MASK_PROP_COLOR;
}
if (data_type & (DT_TYPE_MLOOPCOL_LOOP)) {
cddata_masks.lmask |= CD_MASK_PROP_BYTE_COLOR;
}
if (data_type & (DT_TYPE_MLOOPCOL_VERT | DT_TYPE_MPROPCOL_VERT)) {
dt_add_vcol_layers(&mesh_eval->vert_data, cddata_masks.vmask, &item, &totitem);
}
if (data_type & (DT_TYPE_MLOOPCOL_LOOP | DT_TYPE_MPROPCOL_LOOP)) {
dt_add_vcol_layers(&mesh_eval->corner_data, cddata_masks.lmask, &item, &totitem);
}
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
/* NOTE: #rna_enum_dt_layers_select_dst_items enum is from `rna_modifier.cc`. */
static const EnumPropertyItem *dt_layers_select_dst_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
bool *r_free)
{
if (!C) { /* needed for docs and i18n tools */
return rna_enum_dt_layers_select_dst_items;
}
EnumPropertyItem *item = nullptr;
int totitem = 0;
PropertyRNA *prop = RNA_struct_find_property(ptr, "use_reverse_transfer");
const bool reverse_transfer = prop != nullptr && RNA_property_boolean_get(ptr, prop);
const int layers_select_src = reverse_transfer ? RNA_enum_get(ptr, "layers_select_dst") :
RNA_enum_get(ptr, "layers_select_src");
if (reverse_transfer || layers_select_src == DT_LAYERS_ACTIVE_SRC || layers_select_src >= 0) {
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_dst_items, DT_LAYERS_ACTIVE_DST);
}
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_dst_items, DT_LAYERS_NAME_DST);
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_layers_select_dst_items, DT_LAYERS_INDEX_DST);
/* No 'specific' to-layers here, since we may transfer to several objects at once! */
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
static const EnumPropertyItem *dt_layers_select_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA *prop,
bool *r_free)
{
const bool reverse_transfer = RNA_boolean_get(ptr, "use_reverse_transfer");
if (STREQ(RNA_property_identifier(prop), "layers_select_dst")) {
if (reverse_transfer) {
return dt_layers_select_src_itemf(C, ptr, prop, r_free);
}
return dt_layers_select_dst_itemf(C, ptr, prop, r_free);
}
if (reverse_transfer) {
return dt_layers_select_dst_itemf(C, ptr, prop, r_free);
}
return dt_layers_select_src_itemf(C, ptr, prop, r_free);
}
/* NOTE: rna_enum_dt_mix_mode_items enum is from `rna_modifier.cc`. */
static const EnumPropertyItem *dt_mix_mode_itemf(bContext *C,
PointerRNA *ptr,
PropertyRNA * /*prop*/,
bool *r_free)
{
EnumPropertyItem *item = nullptr;
int totitem = 0;
const int dtdata_type = RNA_enum_get(ptr, "data_type");
bool support_advanced_mixing, support_threshold;
if (!C) { /* needed for docs and i18n tools */
return rna_enum_dt_mix_mode_items;
}
RNA_enum_items_add_value(&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_TRANSFER);
BKE_object_data_transfer_get_dttypes_capacity(
dtdata_type, &support_advanced_mixing, &support_threshold);
if (support_threshold) {
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_REPLACE_ABOVE_THRESHOLD);
RNA_enum_items_add_value(
&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_REPLACE_BELOW_THRESHOLD);
}
if (support_advanced_mixing) {
RNA_enum_item_add_separator(&item, &totitem);
RNA_enum_items_add_value(&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_MIX);
RNA_enum_items_add_value(&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_ADD);
RNA_enum_items_add_value(&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_SUB);
RNA_enum_items_add_value(&item, &totitem, rna_enum_dt_mix_mode_items, CDT_MIX_MUL);
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
static bool data_transfer_check(bContext * /*C*/, wmOperator *op)
{
const int layers_select_src = RNA_enum_get(op->ptr, "layers_select_src");
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "layers_select_dst");
const int layers_select_dst = RNA_property_enum_get(op->ptr, prop);
/* TODO: check for invalid layers_src select modes too! */
if ((layers_select_src != DT_LAYERS_ACTIVE_SRC) && (layers_select_dst == DT_LAYERS_ACTIVE_DST)) {
RNA_property_enum_set(op->ptr, prop, DT_LAYERS_NAME_DST);
return true;
}
return false;
}
/* Helper, used by both data_transfer_exec and datalayout_transfer_exec. */
static void data_transfer_exec_preprocess_objects(bContext *C,
wmOperator *op,
Object *ob_src,
Vector<PointerRNA> *ctx_objects,
const bool reverse_transfer)
{
CTX_data_selected_editable_objects(C, ctx_objects);
if (reverse_transfer) {
return; /* Nothing else to do in this case... */
}
for (const PointerRNA &ptr : *ctx_objects) {
Object *ob = static_cast<Object *>(ptr.data);
Mesh *mesh;
if ((ob == ob_src) || (ob->type != OB_MESH)) {
continue;
}
mesh = id_cast<Mesh *>(ob->data);
if (!ID_IS_EDITABLE(mesh) || ID_IS_OVERRIDE_LIBRARY(mesh)) {
/* Do not transfer to linked/override data, not supported. */
BKE_reportf(op->reports,
RPT_WARNING,
"Skipping object '%s', linked or override data '%s' cannot be modified",
ob->id.name + 2,
mesh->id.name + 2);
mesh->id.tag &= ~ID_TAG_DOIT;
continue;
}
mesh->id.tag |= ID_TAG_DOIT;
}
}
/* Helper, used by both data_transfer_exec and datalayout_transfer_exec. */
static bool data_transfer_exec_is_object_valid(wmOperator *op,
Object *ob_src,
Object *ob_dst,
const bool reverse_transfer)
{
Mesh *mesh;
if ((ob_dst == ob_src) || (ob_src->type != OB_MESH) || (ob_dst->type != OB_MESH)) {
return false;
}
if (reverse_transfer) {
return true;
}
mesh = id_cast<Mesh *>(ob_dst->data);
if (mesh->id.tag & ID_TAG_DOIT) {
mesh->id.tag &= ~ID_TAG_DOIT;
return true;
}
if (ID_IS_EDITABLE(mesh) && !ID_IS_OVERRIDE_LIBRARY(mesh)) {
/* Do not apply transfer operation more than once. */
/* XXX This is not nice regarding vgroups, which are half-Object data... :/ */
BKE_reportf(
op->reports,
RPT_WARNING,
"Skipping object '%s', data '%s' has already been processed with a previous object",
ob_dst->id.name + 2,
mesh->id.name + 2);
}
return false;
}
static wmOperatorStatus data_transfer_exec(bContext *C, wmOperator *op)
{
Object *ob_src = context_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Vector<PointerRNA> ctx_objects;
bool changed = false;
const bool is_frozen = RNA_boolean_get(op->ptr, "use_freeze");
const bool reverse_transfer = RNA_boolean_get(op->ptr, "use_reverse_transfer");
const int data_type = RNA_enum_get(op->ptr, "data_type");
const bool use_create = RNA_boolean_get(op->ptr, "use_create");
const int map_vert_mode = RNA_enum_get(op->ptr, "vert_mapping");
const int map_edge_mode = RNA_enum_get(op->ptr, "edge_mapping");
const int map_loop_mode = RNA_enum_get(op->ptr, "loop_mapping");
const int map_poly_mode = RNA_enum_get(op->ptr, "poly_mapping");
const bool use_auto_transform = RNA_boolean_get(op->ptr, "use_auto_transform");
const bool use_object_transform = RNA_boolean_get(op->ptr, "use_object_transform");
const bool use_max_distance = RNA_boolean_get(op->ptr, "use_max_distance");
const float max_distance = use_max_distance ? RNA_float_get(op->ptr, "max_distance") : FLT_MAX;
const float ray_radius = RNA_float_get(op->ptr, "ray_radius");
const float islands_precision = RNA_float_get(op->ptr, "islands_precision");
int layers_src = RNA_enum_get(op->ptr, "layers_select_src");
int layers_dst = RNA_enum_get(op->ptr, "layers_select_dst");
int layers_select_src[DT_MULTILAYER_INDEX_MAX] = {0};
int layers_select_dst[DT_MULTILAYER_INDEX_MAX] = {0};
const int fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(data_type);
const int mix_mode = RNA_enum_get(op->ptr, "mix_mode");
const float mix_factor = RNA_float_get(op->ptr, "mix_factor");
SpaceTransform space_transform_data;
SpaceTransform *space_transform = (use_object_transform && !use_auto_transform) ?
&space_transform_data :
nullptr;
if (is_frozen) {
BKE_report(
op->reports,
RPT_INFO,
"Operator is frozen, changes to its settings won't take effect until you unfreeze it");
return OPERATOR_FINISHED;
}
if (reverse_transfer && (!ID_IS_EDITABLE(ob_src->data) || ID_IS_OVERRIDE_LIBRARY(ob_src->data)))
{
/* Do not transfer to linked or override data, not supported. */
return OPERATOR_CANCELLED;
}
if (reverse_transfer) {
std::swap(layers_src, layers_dst);
}
if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) {
layers_select_src[fromto_idx] = layers_src;
layers_select_dst[fromto_idx] = layers_dst;
}
data_transfer_exec_preprocess_objects(C, op, ob_src, &ctx_objects, reverse_transfer);
int invalid_count = 0;
for (const PointerRNA &ptr : ctx_objects) {
Object *ob_dst = static_cast<Object *>(ptr.data);
if (reverse_transfer) {
std::swap(ob_src, ob_dst);
}
if (data_transfer_exec_is_object_valid(op, ob_src, ob_dst, reverse_transfer)) {
Object *ob_src_eval = DEG_get_evaluated(depsgraph, ob_src);
if (space_transform) {
Object *ob_dst_eval = DEG_get_evaluated(depsgraph, ob_dst);
BLI_SPACE_TRANSFORM_SETUP(space_transform, ob_dst_eval, ob_src_eval);
}
if (BKE_object_data_transfer_mesh(depsgraph,
ob_src_eval,
ob_dst,
data_type,
use_create,
map_vert_mode,
map_edge_mode,
map_loop_mode,
map_poly_mode,
space_transform,
use_auto_transform,
max_distance,
ray_radius,
islands_precision,
layers_select_src,
layers_select_dst,
mix_mode,
mix_factor,
nullptr,
false,
op->reports))
{
DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY);
changed = true;
}
}
else {
/* Selected objects contains the active object, in this case `ob_src` is the same as
* `ob_dst`, so we don't treat this case as invalid. */
if (ob_src != ob_dst) {
invalid_count++;
}
}
if (reverse_transfer) {
std::swap(ob_src, ob_dst);
}
}
if (changed) {
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, nullptr);
}
if (invalid_count > 0) {
BKE_reportf(
op->reports, RPT_WARNING, "Failed to transfer mesh data to %d objects", invalid_count);
}
#if 0 /* TODO */
/* NOTE: issue with that is that if canceled, operator cannot be redone... Nasty in our case. */
return changed ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
#else
return OPERATOR_FINISHED;
#endif
}
/** Used by both #OBJECT_OT_data_transfer and #OBJECT_OT_datalayout_transfer. */
static bool data_transfer_poll(bContext *C)
{
/* Note this context poll is only really partial,
* it cannot check for all possible invalid cases. */
Object *ob = context_active_object(C);
ID *data = (ob) ? ob->data : nullptr;
return (ob != nullptr && ob->type == OB_MESH && data != nullptr);
}
/** Used by both #OBJECT_OT_data_transfer and #OBJECT_OT_datalayout_transfer. */
static bool data_transfer_poll_property(const bContext * /*C*/,
wmOperator *op,
const PropertyRNA *prop)
{
PointerRNA *ptr = op->ptr;
PropertyRNA *prop_other;
const char *prop_id = RNA_property_identifier(prop);
const int data_type = RNA_enum_get(ptr, "data_type");
bool use_auto_transform = false;
bool use_max_distance = false;
bool use_modifier = false;
if ((prop_other = RNA_struct_find_property(ptr, "use_auto_transform"))) {
use_auto_transform = RNA_property_boolean_get(ptr, prop_other);
}
if ((prop_other = RNA_struct_find_property(ptr, "use_max_distance"))) {
use_max_distance = RNA_property_boolean_get(ptr, prop_other);
}
if ((prop_other = RNA_struct_find_property(ptr, "modifier"))) {
use_modifier = RNA_property_is_set(ptr, prop_other);
}
if (STREQ(prop_id, "modifier")) {
return use_modifier;
}
if (use_modifier) {
/* Hide everything but 'modifier' property, if set. */
return false;
}
if (STREQ(prop_id, "use_object_transform")) {
if (use_auto_transform) {
return false;
}
}
else if (STREQ(prop_id, "max_distance")) {
if (!use_max_distance) {
return false;
}
}
else if (STREQ(prop_id, "islands_precision")) {
if (!DT_DATATYPE_IS_LOOP(data_type)) {
return false;
}
}
else if (STREQ(prop_id, "vert_mapping")) {
if (!DT_DATATYPE_IS_VERT(data_type)) {
return false;
}
}
else if (STREQ(prop_id, "edge_mapping")) {
if (!DT_DATATYPE_IS_EDGE(data_type)) {
return false;
}
}
else if (STREQ(prop_id, "loop_mapping")) {
if (!DT_DATATYPE_IS_LOOP(data_type)) {
return false;
}
}
else if (STREQ(prop_id, "poly_mapping")) {
if (!DT_DATATYPE_IS_FACE(data_type)) {
return false;
}
}
else if (STR_ELEM(prop_id, "layers_select_src", "layers_select_dst")) {
if (!DT_DATATYPE_IS_MULTILAYERS(data_type)) {
return false;
}
}
/* Else, show it! */
return true;
}
static std::string data_transfer_get_description(bContext * /*C*/,
wmOperatorType * /*ot*/,
PointerRNA *ptr)
{
const bool reverse_transfer = RNA_boolean_get(ptr, "use_reverse_transfer");
if (reverse_transfer) {
return TIP_(
"Transfer data layer(s) (weights, edge sharp, etc.) from selected meshes to active one");
}
return "";
}
void OBJECT_OT_data_transfer(wmOperatorType *ot)
{
PropertyRNA *prop;
/* Identifiers. */
ot->name = "Transfer Mesh Data";
ot->idname = "OBJECT_OT_data_transfer";
ot->description =
"Transfer data layer(s) (weights, edge sharp, etc.) from active to selected meshes";
/* API callbacks. */
ot->poll = data_transfer_poll;
ot->poll_property = data_transfer_poll_property;
ot->invoke = WM_menu_invoke;
ot->exec = data_transfer_exec;
ot->check = data_transfer_check;
ot->get_description = data_transfer_get_description;
/* Flags. */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties. */
prop = RNA_def_boolean(ot->srna,
"use_reverse_transfer",
false,
"Reverse Transfer",
"Transfer from selected objects to active one");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
RNA_def_boolean(ot->srna,
"use_freeze",
false,
"Freeze Operator",
"Prevent changes to settings to re-run the operator, "
"handy to change several things at once with heavy geometry");
/* Data type to transfer. */
ot->prop = RNA_def_enum(ot->srna,
"data_type",
DT_layer_items,
/* The default is not significant, just use a valid value. */
DT_TYPE_MDEFORMVERT,
"Data Type",
"Which data to transfer");
RNA_def_boolean(ot->srna,
"use_create",
true,
"Create Data",
"Add data layers on destination meshes if needed");
/* Mapping methods. */
RNA_def_enum(ot->srna,
"vert_mapping",
rna_enum_dt_method_vertex_items,
MREMAP_MODE_VERT_NEAREST,
"Vertex Mapping",
"Method used to map source vertices to destination ones");
RNA_def_enum(ot->srna,
"edge_mapping",
rna_enum_dt_method_edge_items,
MREMAP_MODE_EDGE_NEAREST,
"Edge Mapping",
"Method used to map source edges to destination ones");
RNA_def_enum(ot->srna,
"loop_mapping",
rna_enum_dt_method_loop_items,
MREMAP_MODE_LOOP_NEAREST_POLYNOR,
"Face Corner Mapping",
"Method used to map source faces' corners to destination ones");
RNA_def_enum(ot->srna,
"poly_mapping",
rna_enum_dt_method_poly_items,
MREMAP_MODE_POLY_NEAREST,
"Face Mapping",
"Method used to map source faces to destination ones");
/* Mapping options and filtering. */
RNA_def_boolean(
ot->srna,
"use_auto_transform",
false,
"Auto Transform",
"Automatically compute transformation to get the best possible match between source and "
"destination meshes.\n"
"Warning: Results will never be as good as manual matching of objects");
RNA_def_boolean(ot->srna,
"use_object_transform",
true,
"Object Transform",
"Evaluate source and destination meshes in global space");
RNA_def_boolean(ot->srna,
"use_max_distance",
false,
"Only Neighbor Geometry",
"Source elements must be closer than given distance from destination one");
prop = RNA_def_float(
ot->srna,
"max_distance",
1.0f,
0.0f,
FLT_MAX,
"Max Distance",
"Maximum allowed distance between source and destination element, for non-topology mappings",
0.0f,
100.0f);
RNA_def_property_subtype(prop, PROP_DISTANCE);
prop = RNA_def_float(
ot->srna,
"ray_radius",
0.0f,
0.0f,
FLT_MAX,
"Ray Radius",
"'Width' of rays (especially useful when raycasting against vertices or edges)",
0.0f,
10.0f);
RNA_def_property_subtype(prop, PROP_DISTANCE);
prop = RNA_def_float(
ot->srna,
"islands_precision",
0.1f,
0.0f,
10.0f,
"Islands Precision",
"Factor controlling precision of islands handling (the higher, the better the results)",
0.0f,
1.0f);
RNA_def_property_subtype(prop, PROP_FACTOR);
/* How to handle multi-layers types of data. */
prop = RNA_def_enum(ot->srna,
"layers_select_src",
rna_enum_dt_layers_select_src_items,
DT_LAYERS_ACTIVE_SRC,
"Source Layers Selection",
"Which layers to transfer, in case of multi-layers types");
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_itemf, nullptr, nullptr);
prop = RNA_def_enum(ot->srna,
"layers_select_dst",
rna_enum_dt_layers_select_dst_items,
DT_LAYERS_ACTIVE_DST,
"Destination Layers Matching",
"How to match source and destination layers");
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_itemf, nullptr, nullptr);
prop = RNA_def_enum(ot->srna,
"mix_mode",
rna_enum_dt_mix_mode_items,
CDT_MIX_TRANSFER,
"Mix Mode",
"How to affect destination elements with source values");
RNA_def_property_enum_funcs_runtime(prop, nullptr, nullptr, dt_mix_mode_itemf, nullptr, nullptr);
RNA_def_float(
ot->srna,
"mix_factor",
1.0f,
0.0f,
1.0f,
"Mix Factor",
"Factor to use when applying data to destination (exact behavior depends on mix mode)",
0.0f,
1.0f);
}
/******************************************************************************/
/* NOTE: This operator is hybrid, it can work as a usual standalone Object operator,
* or as a DataTransfer modifier tool.
*/
static bool datalayout_transfer_poll(bContext *C)
{
return (edit_modifier_poll_generic(C, RNA_DataTransferModifier, (1 << OB_MESH), true, false) ||
data_transfer_poll(C));
}
static wmOperatorStatus datalayout_transfer_exec(bContext *C, wmOperator *op)
{
Object *ob_act = context_active_object(C);
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
DataTransferModifierData *dtmd;
dtmd = reinterpret_cast<DataTransferModifierData *>(
edit_modifier_property_get(op, ob_act, eModifierType_DataTransfer));
/* If we have a modifier, we transfer data layout from this modifier's source object to
* active one. Else, we transfer data layout from active object to all selected ones. */
if (dtmd) {
Object *ob_src = dtmd->ob_source;
Object *ob_dst = ob_act;
const bool use_delete = false; /* Never when used from modifier, for now. */
if (!ob_src || !ID_IS_EDITABLE(ob_dst) || ID_IS_OVERRIDE_LIBRARY(ob_dst)) {
return OPERATOR_CANCELLED;
}
Object *ob_src_eval = DEG_get_evaluated(depsgraph, ob_src);
BKE_object_data_transfer_layout(depsgraph,
ob_src_eval,
ob_dst,
dtmd->data_types,
use_delete,
dtmd->layers_select_src,
dtmd->layers_select_dst);
DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY);
}
else {
Object *ob_src = ob_act;
Vector<PointerRNA> ctx_objects;
const int data_type = RNA_enum_get(op->ptr, "data_type");
const bool use_delete = RNA_boolean_get(op->ptr, "use_delete");
const int layers_src = RNA_enum_get(op->ptr, "layers_select_src");
const int layers_dst = RNA_enum_get(op->ptr, "layers_select_dst");
int layers_select_src[DT_MULTILAYER_INDEX_MAX] = {0};
int layers_select_dst[DT_MULTILAYER_INDEX_MAX] = {0};
const int fromto_idx = BKE_object_data_transfer_dttype_to_srcdst_index(data_type);
if (fromto_idx != DT_MULTILAYER_INDEX_INVALID) {
layers_select_src[fromto_idx] = layers_src;
layers_select_dst[fromto_idx] = layers_dst;
}
Object *ob_src_eval = DEG_get_evaluated(depsgraph, ob_src);
data_transfer_exec_preprocess_objects(C, op, ob_src, &ctx_objects, false);
for (const PointerRNA &ptr : ctx_objects) {
Object *ob_dst = static_cast<Object *>(ptr.data);
if (data_transfer_exec_is_object_valid(op, ob_src, ob_dst, false)) {
BKE_object_data_transfer_layout(depsgraph,
ob_src_eval,
ob_dst,
data_type,
use_delete,
layers_select_src,
layers_select_dst);
}
DEG_id_tag_update(&ob_dst->id, ID_RECALC_GEOMETRY);
}
}
DEG_relations_tag_update(CTX_data_main(C));
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, nullptr);
return OPERATOR_FINISHED;
}
static wmOperatorStatus datalayout_transfer_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
if (edit_modifier_invoke_properties(C, op)) {
return datalayout_transfer_exec(C, op);
}
return WM_menu_invoke(C, op, event);
}
void OBJECT_OT_datalayout_transfer(wmOperatorType *ot)
{
PropertyRNA *prop;
ot->name = "Transfer Mesh Data Layout";
ot->description = "Transfer layout of data layer(s) from active to selected meshes";
ot->idname = "OBJECT_OT_datalayout_transfer";
ot->poll = datalayout_transfer_poll;
ot->poll_property = data_transfer_poll_property;
ot->invoke = datalayout_transfer_invoke;
ot->exec = datalayout_transfer_exec;
ot->check = data_transfer_check;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* Properties. */
edit_modifier_properties(ot);
/* Data type to transfer. */
ot->prop = RNA_def_enum(
ot->srna, "data_type", DT_layer_items, 0, "Data Type", "Which data to transfer");
RNA_def_boolean(ot->srna,
"use_delete",
false,
"Exact Match",
"Also delete some data layers from destination if necessary, so that it matches "
"exactly source");
/* How to handle multi-layers types of data. */
prop = RNA_def_enum(ot->srna,
"layers_select_src",
rna_enum_dt_layers_select_src_items,
DT_LAYERS_ACTIVE_SRC,
"Source Layers Selection",
"Which layers to transfer, in case of multi-layers types");
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_src_itemf, nullptr, nullptr);
prop = RNA_def_enum(ot->srna,
"layers_select_dst",
rna_enum_dt_layers_select_dst_items,
DT_LAYERS_ACTIVE_DST,
"Destination Layers Matching",
"How to match source and destination layers");
RNA_def_property_enum_funcs_runtime(
prop, nullptr, nullptr, dt_layers_select_dst_itemf, nullptr, nullptr);
}
} // namespace blender::ed::object

View File

@@ -0,0 +1,851 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*
* Use to transform object origins only.
*
* This is a small API to store & apply transformations to object data,
* where a transformation matrix can be continually applied on top of the original values
* so we don't lose precision over time.
*/
#include <cstdlib>
#include <cstring>
#include "DNA_armature_types.h"
#include "DNA_gpencil_legacy_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "DNA_pointcloud_types.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
#include "BLI_task.hh"
#include "BKE_armature.hh"
#include "BKE_curve.hh"
#include "BKE_curves_utils.hh"
#include "BKE_editmesh.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_key.hh"
#include "BKE_lattice.hh"
#include "BKE_mball.hh"
#include "BKE_mesh_types.hh"
#include "bmesh.hh"
#include "DEG_depsgraph.hh"
#include "ED_armature.hh"
#include "ED_curves.hh"
#include "ED_mesh.hh"
#include "ED_object.hh"
namespace blender::ed::object {
/* -------------------------------------------------------------------- */
/** \name Internal Transform Get/Apply
*
* Some object data types don't have utility functions to access their transformation data.
* Define these locally.
*
* \{ */
/* Armature */
struct ElemData_Armature {
float tail[3];
float head[3];
float roll;
float arm_tail[3];
float arm_head[3];
float arm_roll;
float rad_tail;
float rad_head;
float dist;
float xwidth;
float zwidth;
};
static ElemData_Armature *armature_coords_and_quats_get_recurse(const ListBaseT<Bone> *bone_base,
ElemData_Armature *elem_array)
{
ElemData_Armature *elem = elem_array;
for (const Bone &bone : *bone_base) {
#define COPY_PTR(member) memcpy(elem->member, bone.member, sizeof(bone.member))
#define COPY_VAL(member) memcpy(&elem->member, &bone.member, sizeof(bone.member))
COPY_PTR(head);
COPY_PTR(tail);
COPY_VAL(roll);
COPY_PTR(arm_head);
COPY_PTR(arm_tail);
COPY_VAL(arm_roll);
COPY_VAL(rad_tail);
COPY_VAL(rad_head);
COPY_VAL(dist);
COPY_VAL(xwidth);
COPY_VAL(zwidth);
#undef COPY_PTR
#undef COPY_VAL
elem = armature_coords_and_quats_get_recurse(&bone.childbase, elem + 1);
}
return elem;
}
static void armature_coords_and_quats_get(const bArmature *arm,
MutableSpan<ElemData_Armature> elem_array)
{
armature_coords_and_quats_get_recurse(&arm->bonebase, elem_array.data());
}
static const ElemData_Armature *armature_coords_and_quats_apply_with_mat4_recurse(
ListBaseT<Bone> *bone_base, const ElemData_Armature *elem_array, const float4x4 &transform)
{
const ElemData_Armature *elem = elem_array;
for (Bone &bone : *bone_base) {
#define COPY_PTR(member) memcpy(bone.member, elem->member, sizeof(bone.member))
#define COPY_VAL(member) memcpy(&bone.member, &elem->member, sizeof(bone.member))
COPY_PTR(head);
COPY_PTR(tail);
COPY_VAL(roll);
COPY_PTR(arm_head);
COPY_PTR(arm_tail);
COPY_VAL(arm_roll);
COPY_VAL(rad_tail);
COPY_VAL(rad_head);
COPY_VAL(dist);
COPY_VAL(xwidth);
COPY_VAL(zwidth);
#undef COPY_PTR
#undef COPY_VAL
elem = armature_coords_and_quats_apply_with_mat4_recurse(&bone.childbase, elem + 1, transform);
}
return elem;
}
static void armature_coords_and_quats_apply_with_mat4(bArmature *arm,
const Span<ElemData_Armature> elem_array,
const float4x4 &transform)
{
armature_coords_and_quats_apply_with_mat4_recurse(&arm->bonebase, elem_array.data(), transform);
BKE_armature_transform(arm, transform.ptr(), true);
}
static void armature_coords_and_quats_apply(bArmature *arm,
const Span<ElemData_Armature> elem_array)
{
/* Avoid code duplication by using a unit matrix. */
armature_coords_and_quats_apply_with_mat4(arm, elem_array, float4x4::identity());
}
/* Edit Armature */
static void edit_armature_coords_and_quats_get(const bArmature *arm,
MutableSpan<ElemData_Armature> elem_array)
{
ElemData_Armature *elem = elem_array.data();
for (EditBone *ebone = static_cast<EditBone *>(arm->edbo->first); ebone;
ebone = ebone->next, elem++)
{
#define COPY_PTR(member) memcpy(elem->member, ebone->member, sizeof(ebone->member))
#define COPY_VAL(member) memcpy(&elem->member, &ebone->member, sizeof(ebone->member))
/* Unused for edit bones: arm_head, arm_tail, arm_roll */
COPY_PTR(head);
COPY_PTR(tail);
COPY_VAL(roll);
COPY_VAL(rad_tail);
COPY_VAL(rad_head);
COPY_VAL(dist);
COPY_VAL(xwidth);
COPY_VAL(zwidth);
#undef COPY_PTR
#undef COPY_VAL
}
}
static void edit_armature_coords_and_quats_apply_with_mat4(
bArmature *arm, const Span<ElemData_Armature> elem_array, const float4x4 &transform)
{
const ElemData_Armature *elem = elem_array.data();
for (EditBone *ebone = static_cast<EditBone *>(arm->edbo->first); ebone;
ebone = ebone->next, elem++)
{
#define COPY_PTR(member) memcpy(ebone->member, elem->member, sizeof(ebone->member))
#define COPY_VAL(member) memcpy(&ebone->member, &elem->member, sizeof(ebone->member))
/* Unused for edit bones: arm_head, arm_tail, arm_roll */
COPY_PTR(head);
COPY_PTR(tail);
COPY_VAL(roll);
COPY_VAL(rad_tail);
COPY_VAL(rad_head);
COPY_VAL(dist);
COPY_VAL(xwidth);
COPY_VAL(zwidth);
#undef COPY_PTR
#undef COPY_VAL
}
ED_armature_edit_transform(arm, transform.ptr(), true);
}
static void edit_armature_coords_and_quats_apply(bArmature *arm,
const Span<ElemData_Armature> elem_array)
{
/* Avoid code duplication by using a unit matrix. */
edit_armature_coords_and_quats_apply_with_mat4(arm, elem_array, float4x4::identity());
}
/* MetaBall */
struct ElemData_MetaBall {
float co[3];
float quat[4];
float exp[3];
float rad;
};
static void metaball_coords_and_quats_get(const MetaBall *mb,
MutableSpan<ElemData_MetaBall> elem_array)
{
ElemData_MetaBall *elem = elem_array.data();
for (const MetaElem *ml = static_cast<const MetaElem *>(mb->elems.first); ml;
ml = ml->next, elem++)
{
copy_v3_v3(elem->co, &ml->x);
copy_qt_qt(elem->quat, ml->quat);
copy_v3_v3(elem->exp, &ml->expx);
elem->rad = ml->rad;
}
}
static void metaball_coords_and_quats_apply_with_mat4(MetaBall *mb,
const Span<ElemData_MetaBall> elem_array,
const float4x4 &transform)
{
const ElemData_MetaBall *elem = elem_array.data();
for (MetaElem *ml = static_cast<MetaElem *>(mb->elems.first); ml; ml = ml->next, elem++) {
copy_v3_v3(&ml->x, elem->co);
copy_qt_qt(ml->quat, elem->quat);
copy_v3_v3(&ml->expx, elem->exp);
ml->rad = elem->rad;
}
BKE_mball_transform(mb, transform.ptr(), true);
}
static void metaball_coords_and_quats_apply(MetaBall *mb, const Span<ElemData_MetaBall> elem_array)
{
/* Avoid code duplication by using a unit matrix. */
metaball_coords_and_quats_apply_with_mat4(mb, elem_array, float4x4::identity());
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Public Object Data Storage API
*
* Used for interactively transforming object data.
*
* Store object data transformation in an opaque struct.
* \{ */
struct XFormObjectData_Mesh : public XFormObjectData {
/* Optional data for shape keys. */
Array<float3> key_data;
Array<float3> positions;
bool is_edit_mode = false;
~XFormObjectData_Mesh() override = default;
};
struct XFormObjectData_Lattice : public XFormObjectData {
/* Optional data for shape keys. */
Array<float3> key_data;
Array<float3> positions;
bool is_edit_mode = false;
~XFormObjectData_Lattice() override = default;
};
struct XFormObjectData_Curve : public XFormObjectData {
/* Optional data for shape keys. */
Array<float3> key_data;
Array<float3> positions;
bool is_edit_mode = false;
~XFormObjectData_Curve() override = default;
};
struct XFormObjectData_Armature : public XFormObjectData {
Array<ElemData_Armature> elems;
bool is_edit_mode = false;
~XFormObjectData_Armature() override = default;
};
struct XFormObjectData_MetaBall : public XFormObjectData {
Array<ElemData_MetaBall> elems;
bool is_edit_mode = false;
~XFormObjectData_MetaBall() override = default;
};
struct XFormObjectData_GreasePencil : public XFormObjectData {
Array<float3> positions;
Array<float> radii;
~XFormObjectData_GreasePencil() override = default;
};
struct XFormObjectData_Curves : public XFormObjectData {
Array<float3> positions;
Array<float> radii;
~XFormObjectData_Curves() override = default;
};
struct XFormObjectData_PointCloud : public XFormObjectData {
Array<float3> positions;
Array<float> radii;
~XFormObjectData_PointCloud() override = default;
};
static std::unique_ptr<XFormObjectData> data_xform_create_ex(ID *id, bool is_edit_mode)
{
if (id == nullptr) {
return {};
}
switch (GS(id->name)) {
case ID_ME: {
Mesh *mesh = id_cast<Mesh *>(id);
Key *key = mesh->key;
const int key_index = -1;
if (is_edit_mode) {
BMesh *bm = mesh->runtime->edit_mesh->bm;
/* Always operate on all keys for the moment. */
// key_index = bm->shapenr - 1;
auto xod = std::make_unique<XFormObjectData_Mesh>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->positions.reinitialize(bm->totvert);
BM_mesh_vert_coords_get(bm, xod->positions.as_mutable_span());
if (key != nullptr) {
const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
if (key_size) {
xod->key_data.reinitialize(key_size);
BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
}
}
return xod;
}
auto xod = std::make_unique<XFormObjectData_Mesh>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->positions = mesh->vert_positions();
if (key != nullptr) {
const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
if (key_size) {
xod->key_data.reinitialize(key_size);
BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
}
}
return xod;
}
case ID_LT: {
Lattice *lt_orig = id_cast<Lattice *>(id);
Lattice *lt = is_edit_mode ? lt_orig->editlatt->latt : lt_orig;
Key *key = lt->key;
const int key_index = -1;
if (is_edit_mode) {
/* Always operate on all keys for the moment. */
// key_index = lt_orig->editlatt->shapenr - 1;
}
auto xod = std::make_unique<XFormObjectData_Lattice>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->positions = BKE_lattice_vert_coords_alloc(lt);
if (key != nullptr) {
const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
if (key_size) {
xod->key_data.reinitialize(key_size);
BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
}
}
return xod;
}
case ID_CU_LEGACY: {
Curve *cu = id_cast<Curve *>(id);
Key *key = cu->key;
if (cu->ob_type == OB_FONT) {
/* We could support translation. */
break;
}
const int key_index = -1;
ListBaseT<Nurb> *nurbs;
if (is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
nurbs = &editnurb->nurbs;
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
nurbs = &cu->nurb;
}
auto xod = std::make_unique<XFormObjectData_Curve>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->positions = BKE_curve_nurbs_vert_coords_alloc(nurbs);
if (key != nullptr) {
const size_t key_size = BKE_keyblock_element_calc_size_from_shape(key, key_index);
if (key_size) {
xod->key_data.reinitialize(key_size);
BKE_keyblock_data_get_from_shape(key, xod->key_data, key_index);
}
}
return xod;
}
case ID_AR: {
bArmature *arm = id_cast<bArmature *>(id);
if (is_edit_mode) {
auto xod = std::make_unique<XFormObjectData_Armature>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->elems.reinitialize(arm->edbo->count());
edit_armature_coords_and_quats_get(arm, xod->elems);
return xod;
}
auto xod = std::make_unique<XFormObjectData_Armature>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->elems.reinitialize(BKE_armature_bonelist_count(&arm->bonebase));
armature_coords_and_quats_get(arm, xod->elems);
return xod;
}
case ID_MB: {
/* Edit mode and object mode are shared. */
MetaBall *mb = id_cast<MetaBall *>(id);
auto xod = std::make_unique<XFormObjectData_MetaBall>();
xod->id = id;
xod->is_edit_mode = is_edit_mode;
xod->elems.reinitialize(mb->elems.count());
metaball_coords_and_quats_get(mb, xod->elems);
return xod;
}
case ID_GP: {
GreasePencil *grease_pencil = id_cast<GreasePencil *>(id);
const int elem_array_len = BKE_grease_pencil_stroke_point_count(*grease_pencil);
auto xod = std::make_unique<XFormObjectData_GreasePencil>();
xod->id = id;
if (!BKE_grease_pencil_has_curve_with_type(*grease_pencil, CURVE_TYPE_BEZIER)) {
xod->positions.reinitialize(elem_array_len);
}
else {
xod->positions.reinitialize(elem_array_len * 3);
}
xod->radii.reinitialize(elem_array_len);
BKE_grease_pencil_point_coords_get(*grease_pencil, xod->positions, xod->radii);
return xod;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(id);
const bke::CurvesGeometry &curves = curves_id->geometry.wrap();
auto xod = std::make_unique<XFormObjectData_Curves>();
xod->id = id;
if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
xod->positions = curves.positions();
}
else {
xod->positions = bke::curves::bezier::retrieve_all_positions(curves,
curves.curves_range());
}
xod->radii.reinitialize(curves.points_num());
curves.radius().materialize(xod->radii);
return xod;
}
case ID_PT: {
PointCloud *pointcloud = reinterpret_cast<PointCloud *>(id);
auto xod = std::make_unique<XFormObjectData_PointCloud>();
xod->id = id;
xod->positions = pointcloud->positions();
xod->radii.reinitialize(pointcloud->totpoint);
pointcloud->radius().materialize(xod->radii);
return xod;
}
default: {
return {};
}
}
return {};
}
std::unique_ptr<XFormObjectData> data_xform_create(ID *id)
{
return data_xform_create_ex(id, false);
}
std::unique_ptr<XFormObjectData> data_xform_create_from_edit_mode(ID *id)
{
return data_xform_create_ex(id, true);
}
static void copy_transformed_radii(const Span<float> src,
const float4x4 &transform,
MutableSpan<float> dst)
{
const float scale = mat4_to_scale(transform.ptr());
threading::parallel_for(src.index_range(), 1024, [&](const IndexRange range) {
for (const int i : range) {
dst[i] = src[i] * scale;
}
});
}
void data_xform_by_mat4(XFormObjectData &xod_base, const float4x4 &transform)
{
switch (GS(xod_base.id->name)) {
case ID_ME: {
Mesh *mesh = id_cast<Mesh *>(xod_base.id);
Key *key = mesh->key;
const int key_index = -1;
const auto &xod = reinterpret_cast<XFormObjectData_Mesh &>(xod_base);
if (xod.is_edit_mode) {
BMesh *bm = mesh->runtime->edit_mesh->bm;
BM_mesh_vert_coords_apply_with_mat4(bm, xod.positions, transform);
/* Always operate on all keys for the moment. */
// key_index = bm->shapenr - 1;
}
else {
math::transform_points(xod.positions, transform, mesh->vert_positions_for_write());
mesh->tag_positions_changed();
}
if (key != nullptr) {
BKE_keyblock_data_set_with_mat4(key, key_index, xod.key_data, transform);
}
break;
}
case ID_LT: {
const auto &xod = reinterpret_cast<XFormObjectData_Lattice &>(xod_base);
Lattice *lt_orig = id_cast<Lattice *>(xod_base.id);
Lattice *lt = xod.is_edit_mode ? lt_orig->editlatt->latt : lt_orig;
Key *key = lt->key;
const int key_index = -1;
BKE_lattice_vert_coords_apply_with_mat4(lt, xod.positions, transform);
if (xod.is_edit_mode) {
/* Always operate on all keys for the moment. */
// key_index = lt_orig->editlatt->shapenr - 1;
}
if ((key != nullptr) && !xod.key_data.is_empty()) {
BKE_keyblock_data_set_with_mat4(key, key_index, xod.key_data, transform);
}
break;
}
case ID_CU_LEGACY: {
const auto &xod = reinterpret_cast<XFormObjectData_Curve &>(xod_base);
BLI_assert(xod.is_edit_mode == false); /* Not used currently. */
Curve *cu = id_cast<Curve *>(xod_base.id);
Key *key = cu->key;
const int key_index = -1;
const ListBaseT<Nurb> *nurb = nullptr;
if (xod.is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
nurb = &editnurb->nurbs;
BKE_curve_nurbs_vert_coords_apply_with_mat4(
&editnurb->nurbs, xod.positions, transform, CU_IS_2D(cu));
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
nurb = &cu->nurb;
BKE_curve_nurbs_vert_coords_apply_with_mat4(
&cu->nurb, xod.positions, transform, CU_IS_2D(cu));
}
if ((key != nullptr) && !xod.key_data.is_empty()) {
BKE_keyblock_curve_data_set_with_mat4(
key, nurb, key_index, xod.key_data.data(), transform);
}
break;
}
case ID_AR: {
const auto &xod = reinterpret_cast<XFormObjectData_Armature &>(xod_base);
BLI_assert(xod.is_edit_mode == false); /* Not used currently. */
bArmature *arm = id_cast<bArmature *>(xod_base.id);
if (xod.is_edit_mode) {
edit_armature_coords_and_quats_apply_with_mat4(arm, xod.elems, transform);
}
else {
armature_coords_and_quats_apply_with_mat4(arm, xod.elems, transform);
}
break;
}
case ID_MB: {
/* Meta-balls are a special case, edit-mode and object mode data is shared. */
MetaBall *mb = id_cast<MetaBall *>(xod_base.id);
const auto &xod = reinterpret_cast<XFormObjectData_MetaBall &>(xod_base);
metaball_coords_and_quats_apply_with_mat4(mb, xod.elems, transform);
break;
}
case ID_GP: {
GreasePencil *grease_pencil = id_cast<GreasePencil *>(xod_base.id);
const auto &xod = reinterpret_cast<XFormObjectData_GreasePencil &>(xod_base);
BKE_grease_pencil_point_coords_apply_with_mat4(
*grease_pencil, xod.positions, xod.radii, transform);
break;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(xod_base.id);
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const auto &xod = reinterpret_cast<const XFormObjectData_Curves &>(xod_base);
if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
math::transform_points(xod.positions, transform, curves.positions_for_write());
}
else {
Array<float3> transformed_positions(xod.positions.size());
math::transform_points(xod.positions, transform, transformed_positions);
bke::curves::bezier::write_all_positions(
curves, curves.curves_range(), transformed_positions);
}
copy_transformed_radii(xod.radii, transform, curves.radius_for_write());
break;
}
case ID_PT: {
PointCloud *pointcloud = reinterpret_cast<PointCloud *>(xod_base.id);
const auto &xod = reinterpret_cast<const XFormObjectData_PointCloud &>(xod_base);
math::transform_points(xod.positions, transform, pointcloud->positions_for_write());
copy_transformed_radii(xod.radii, transform, pointcloud->radius_for_write());
break;
}
default: {
break;
}
}
}
void data_xform_restore(XFormObjectData &xod_base)
{
switch (GS(xod_base.id->name)) {
case ID_ME: {
Mesh *mesh = id_cast<Mesh *>(xod_base.id);
Key *key = mesh->key;
const int key_index = -1;
const auto &xod = reinterpret_cast<XFormObjectData_Mesh &>(xod_base);
if (xod.is_edit_mode) {
BMesh *bm = mesh->runtime->edit_mesh->bm;
BM_mesh_vert_coords_apply(bm, xod.positions);
/* Always operate on all keys for the moment. */
// key_index = bm->shapenr - 1;
}
else {
mesh->vert_positions_for_write().copy_from(xod.positions);
mesh->tag_positions_changed();
}
if ((key != nullptr) && !xod.key_data.is_empty()) {
BKE_keyblock_data_set(key, key_index, xod.key_data.data());
}
break;
}
case ID_LT: {
const auto &xod = reinterpret_cast<XFormObjectData_Lattice &>(xod_base);
Lattice *lt_orig = id_cast<Lattice *>(xod_base.id);
Lattice *lt = xod.is_edit_mode ? lt_orig->editlatt->latt : lt_orig;
Key *key = lt->key;
const int key_index = -1;
BKE_lattice_vert_coords_apply(lt, xod.positions);
if (xod.is_edit_mode) {
/* Always operate on all keys for the moment. */
// key_index = lt_orig->editlatt->shapenr - 1;
}
if ((key != nullptr) && !xod.key_data.is_empty()) {
BKE_keyblock_data_set(key, key_index, xod.key_data.data());
}
break;
}
case ID_CU_LEGACY: {
Curve *cu = id_cast<Curve *>(xod_base.id);
Key *key = cu->key;
const int key_index = -1;
const auto &xod = reinterpret_cast<XFormObjectData_Curve &>(xod_base);
if (xod.is_edit_mode) {
EditNurb *editnurb = cu->editnurb;
BKE_curve_nurbs_vert_coords_apply(&editnurb->nurbs, xod.positions, CU_IS_2D(cu));
/* Always operate on all keys for the moment. */
// key_index = editnurb->shapenr - 1;
}
else {
BKE_curve_nurbs_vert_coords_apply(&cu->nurb, xod.positions, CU_IS_2D(cu));
}
if ((key != nullptr) && !xod.key_data.is_empty()) {
BKE_keyblock_data_set(key, key_index, xod.key_data.data());
}
break;
}
case ID_AR: {
bArmature *arm = id_cast<bArmature *>(xod_base.id);
const auto &xod = reinterpret_cast<XFormObjectData_Armature &>(xod_base);
if (xod.is_edit_mode) {
edit_armature_coords_and_quats_apply(arm, xod.elems);
}
else {
armature_coords_and_quats_apply(arm, xod.elems);
}
break;
}
case ID_MB: {
/* Meta-balls are a special case, edit-mode and object mode data is shared. */
MetaBall *mb = id_cast<MetaBall *>(xod_base.id);
const auto &xod = reinterpret_cast<XFormObjectData_MetaBall &>(xod_base);
metaball_coords_and_quats_apply(mb, xod.elems);
break;
}
case ID_GP: {
GreasePencil *grease_pencil = id_cast<GreasePencil *>(xod_base.id);
const auto &xod = reinterpret_cast<XFormObjectData_GreasePencil &>(xod_base);
BKE_grease_pencil_point_coords_apply(*grease_pencil, xod.positions, xod.radii);
break;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(xod_base.id);
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
const auto &xod = reinterpret_cast<const XFormObjectData_Curves &>(xod_base);
if (!curves.has_curve_with_type(CURVE_TYPE_BEZIER)) {
curves.positions_for_write().copy_from(xod.positions);
}
else {
bke::curves::bezier::write_all_positions(curves, curves.curves_range(), xod.positions);
}
curves.radius_for_write().copy_from(xod.radii);
break;
}
case ID_PT: {
PointCloud *pointcloud = reinterpret_cast<PointCloud *>(xod_base.id);
const auto &xod = reinterpret_cast<const XFormObjectData_PointCloud &>(xod_base);
pointcloud->positions_for_write().copy_from(xod.positions);
pointcloud->radius_for_write().copy_from(xod.radii);
break;
}
default: {
break;
}
}
}
void data_xform_tag_update(XFormObjectData &xod_base)
{
switch (GS(xod_base.id->name)) {
case ID_ME: {
Mesh *mesh = id_cast<Mesh *>(xod_base.id);
const auto &xod = reinterpret_cast<XFormObjectData_Mesh &>(xod_base);
if (xod.is_edit_mode) {
EDBMUpdate_Params params{};
params.calc_looptris = true;
params.calc_normals = true;
params.is_destructive = false;
EDBM_update(mesh, &params);
}
DEG_id_tag_update(&mesh->id, ID_RECALC_GEOMETRY);
break;
}
case ID_LT: {
/* Generic update. */
Lattice *lt = id_cast<Lattice *>(xod_base.id);
DEG_id_tag_update(&lt->id, ID_RECALC_GEOMETRY);
break;
}
case ID_CU_LEGACY: {
/* Generic update. */
Curve *cu = id_cast<Curve *>(xod_base.id);
DEG_id_tag_update(&cu->id, ID_RECALC_GEOMETRY);
break;
}
case ID_AR: {
/* Generic update. */
bArmature *arm = id_cast<bArmature *>(xod_base.id);
/* XXX, zero is needed, no other flags properly update this. */
DEG_id_tag_update(&arm->id, 0);
break;
}
case ID_MB: {
/* Generic update. */
MetaBall *mb = id_cast<MetaBall *>(xod_base.id);
DEG_id_tag_update(&mb->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
break;
}
case ID_GD_LEGACY: {
/* Generic update. */
bGPdata *gpd = id_cast<bGPdata *>(xod_base.id);
DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
break;
}
case ID_GP: {
/* Generic update. */
GreasePencil *grease_pencil = id_cast<GreasePencil *>(xod_base.id);
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
break;
}
case ID_CV: {
Curves *curves_id = reinterpret_cast<Curves *>(xod_base.id);
bke::CurvesGeometry &curves = curves_id->geometry.wrap();
curves.tag_positions_changed();
curves.tag_radii_changed();
DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
break;
}
case ID_PT: {
PointCloud *pointcloud = reinterpret_cast<PointCloud *>(xod_base.id);
pointcloud->tag_positions_changed();
pointcloud->tag_radii_changed();
DEG_id_tag_update(&pointcloud->id, ID_RECALC_GEOMETRY | ID_RECALC_SYNC_TO_EVAL);
break;
}
default: {
break;
}
}
}
/** \} */
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,992 @@
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include <cstdlib>
#include <cstring>
#include "MEM_guardedalloc.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_lattice_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BKE_action.hh"
#include "BKE_context.hh"
#include "BKE_deform.hh"
#include "BKE_editmesh.hh"
#include "BKE_layer.hh"
#include "BKE_mesh_types.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_report.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "DEG_depsgraph_query.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "ED_curve.hh"
#include "ED_mesh.hh"
#include "ED_screen.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "UI_resources.hh"
#include "object_intern.hh"
namespace blender::ed::object {
static int return_editmesh_indexar(BMEditMesh *em,
int *r_indexar_num,
int **r_indexar,
float r_cent[3])
{
BMVert *eve;
BMIter iter;
int *index, nr, indexar_num = 0;
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
indexar_num++;
}
}
if (indexar_num == 0) {
return 0;
}
*r_indexar = index = MEM_new_array_uninitialized<int>(indexar_num, "hook indexar");
*r_indexar_num = indexar_num;
nr = 0;
zero_v3(r_cent);
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
if (BM_elem_flag_test(eve, BM_ELEM_SELECT)) {
*index = nr;
index++;
add_v3_v3(r_cent, eve->co);
}
nr++;
}
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return indexar_num;
}
static bool return_editmesh_vgroup(Object *obedit, BMEditMesh *em, char *r_name, float r_cent[3])
{
const int active_index = BKE_object_defgroup_active_index_get(obedit);
const int cd_dvert_offset = active_index ?
CustomData_get_offset(&em->bm->vdata, CD_MDEFORMVERT) :
-1;
if (cd_dvert_offset != -1) {
const int defgrp_index = active_index - 1;
int indexar_num = 0;
MDeformVert *dvert;
BMVert *eve;
BMIter iter;
/* find the vertices */
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
dvert = static_cast<MDeformVert *>(BM_ELEM_CD_GET_VOID_P(eve, cd_dvert_offset));
if (BKE_defvert_find_weight(dvert, defgrp_index) > 0.0f) {
add_v3_v3(r_cent, eve->co);
indexar_num++;
}
}
if (indexar_num) {
const ListBaseT<bDeformGroup> *defbase = BKE_object_defgroup_list(obedit);
bDeformGroup *dg = static_cast<bDeformGroup *>(BLI_findlink(defbase, defgrp_index));
BLI_strncpy_utf8(r_name, dg->name, sizeof(dg->name));
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return true;
}
}
return false;
}
static void select_editbmesh_hook(Object *ob, HookModifierData *hmd)
{
Mesh *mesh = id_cast<Mesh *>(ob->data);
BMEditMesh *em = mesh->runtime->edit_mesh.get();
BMVert *eve;
BMIter iter;
int index = 0, nr = 0;
if (hmd->indexar == nullptr) {
return;
}
BM_ITER_MESH (eve, &iter, em->bm, BM_VERTS_OF_MESH) {
if (nr == hmd->indexar[index]) {
BM_vert_select_set(em->bm, eve, true);
if (index < hmd->indexar_num - 1) {
index++;
}
}
nr++;
}
EDBM_select_flush_from_verts(em, true);
}
static int return_editlattice_indexar(Lattice *editlatt,
int **r_indexar,
int *r_indexar_num,
float r_cent[3])
{
BPoint *bp;
int *index, nr, indexar_num = 0, a;
/* count */
a = editlatt->pntsu * editlatt->pntsv * editlatt->pntsw;
bp = editlatt->def;
while (a--) {
if (bp->f1 & SELECT) {
if (bp->hide == 0) {
indexar_num++;
}
}
bp++;
}
if (indexar_num == 0) {
return 0;
}
*r_indexar = index = MEM_new_array_uninitialized<int>(indexar_num, "hook indexar");
*r_indexar_num = indexar_num;
nr = 0;
zero_v3(r_cent);
a = editlatt->pntsu * editlatt->pntsv * editlatt->pntsw;
bp = editlatt->def;
while (a--) {
if (bp->f1 & SELECT) {
if (bp->hide == 0) {
*index = nr;
index++;
add_v3_v3(r_cent, bp->vec);
}
}
bp++;
nr++;
}
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return indexar_num;
}
static void select_editlattice_hook(Object *obedit, HookModifierData *hmd)
{
Lattice *lt = id_cast<Lattice *>(obedit->data), *editlt;
BPoint *bp;
int index = 0, nr = 0, a;
editlt = lt->editlatt->latt;
/* count */
a = editlt->pntsu * editlt->pntsv * editlt->pntsw;
bp = editlt->def;
while (a--) {
if (hmd->indexar[index] == nr) {
bp->f1 |= SELECT;
if (index < hmd->indexar_num - 1) {
index++;
}
}
nr++;
bp++;
}
}
static int return_editcurve_indexar(Object *obedit,
int **r_indexar,
int *r_indexar_num,
float r_cent[3])
{
ListBaseT<Nurb> *editnurb = object_editcurve_get(obedit);
BPoint *bp;
BezTriple *bezt;
int *index, a, nr, indexar_num = 0;
for (Nurb &nu : *editnurb) {
if (nu.type == CU_BEZIER) {
bezt = nu.bezt;
a = nu.pntsu;
while (a--) {
if (bezt->f1 & SELECT) {
indexar_num++;
}
if (bezt->f2 & SELECT) {
indexar_num++;
}
if (bezt->f3 & SELECT) {
indexar_num++;
}
bezt++;
}
}
else {
bp = nu.bp;
a = nu.pntsu * nu.pntsv;
while (a--) {
if (bp->f1 & SELECT) {
indexar_num++;
}
bp++;
}
}
}
if (indexar_num == 0) {
return 0;
}
*r_indexar = index = MEM_new_array_uninitialized<int>(indexar_num, "hook indexar");
*r_indexar_num = indexar_num;
nr = 0;
zero_v3(r_cent);
for (Nurb &nu : *editnurb) {
if (nu.type == CU_BEZIER) {
bezt = nu.bezt;
a = nu.pntsu;
while (a--) {
if (bezt->f1 & SELECT) {
*index = nr;
index++;
add_v3_v3(r_cent, bezt->vec[0]);
}
nr++;
if (bezt->f2 & SELECT) {
*index = nr;
index++;
add_v3_v3(r_cent, bezt->vec[1]);
}
nr++;
if (bezt->f3 & SELECT) {
*index = nr;
index++;
add_v3_v3(r_cent, bezt->vec[2]);
}
nr++;
bezt++;
}
}
else {
bp = nu.bp;
a = nu.pntsu * nu.pntsv;
while (a--) {
if (bp->f1 & SELECT) {
*index = nr;
index++;
add_v3_v3(r_cent, bp->vec);
}
nr++;
bp++;
}
}
}
mul_v3_fl(r_cent, 1.0f / float(indexar_num));
return indexar_num;
}
static bool object_hook_index_array(Main *bmain,
Scene *scene,
Object *obedit,
int **r_indexar,
int *r_indexar_num,
char *r_name,
float r_cent[3])
{
*r_indexar = nullptr;
*r_indexar_num = 0;
r_name[0] = 0;
switch (obedit->type) {
case OB_MESH: {
Mesh *mesh = id_cast<Mesh *>(obedit->data);
EDBM_mesh_load(bmain, obedit);
EDBM_mesh_make(obedit, scene->toolsettings->selectmode, true);
DEG_id_tag_update(obedit->data, 0);
BMEditMesh *em = mesh->runtime->edit_mesh.get();
BKE_editmesh_looptris_and_normals_calc(em);
/* check selected vertices first */
if (return_editmesh_indexar(em, r_indexar_num, r_indexar, r_cent) == 0) {
return return_editmesh_vgroup(obedit, em, r_name, r_cent);
}
return true;
}
case OB_CURVES_LEGACY:
case OB_SURF:
ED_curve_editnurb_load(bmain, obedit);
ED_curve_editnurb_make(obedit);
return return_editcurve_indexar(obedit, r_indexar, r_indexar_num, r_cent);
case OB_LATTICE: {
Lattice *lt = id_cast<Lattice *>(obedit->data);
return return_editlattice_indexar(lt->editlatt->latt, r_indexar, r_indexar_num, r_cent);
}
default:
return false;
}
}
static void select_editcurve_hook(Object *obedit, HookModifierData *hmd)
{
ListBaseT<Nurb> *editnurb = object_editcurve_get(obedit);
BPoint *bp;
BezTriple *bezt;
int index = 0, a, nr = 0;
for (Nurb &nu : *editnurb) {
if (nu.type == CU_BEZIER) {
bezt = nu.bezt;
a = nu.pntsu;
while (a--) {
if (nr == hmd->indexar[index]) {
bezt->f1 |= BEZT_FLAG_SELECT;
if (index < hmd->indexar_num - 1) {
index++;
}
}
nr++;
if (nr == hmd->indexar[index]) {
bezt->f2 |= BEZT_FLAG_SELECT;
if (index < hmd->indexar_num - 1) {
index++;
}
}
nr++;
if (nr == hmd->indexar[index]) {
bezt->f3 |= BEZT_FLAG_SELECT;
if (index < hmd->indexar_num - 1) {
index++;
}
}
nr++;
bezt++;
}
}
else {
bp = nu.bp;
a = nu.pntsu * nu.pntsv;
while (a--) {
if (nr == hmd->indexar[index]) {
bp->f1 |= SELECT;
if (index < hmd->indexar_num - 1) {
index++;
}
}
nr++;
bp++;
}
}
}
}
static void object_hook_from_context(
bContext *C, PointerRNA *ptr, const int num, Object **r_ob, HookModifierData **r_hmd)
{
Object *ob;
HookModifierData *hmd;
if (ptr->data) { /* if modifier context is available, use that */
ob = id_cast<Object *>(ptr->owner_id);
hmd = static_cast<HookModifierData *>(ptr->data);
}
else { /* use the provided property */
ob = CTX_data_edit_object(C);
hmd = static_cast<HookModifierData *>(BLI_findlink(&ob->modifiers, num));
}
if (ob && hmd && (hmd->modifier.type == eModifierType_Hook)) {
*r_ob = ob;
*r_hmd = hmd;
}
else {
*r_ob = nullptr;
*r_hmd = nullptr;
}
}
static void object_hook_select(Object *ob, HookModifierData *hmd)
{
if (hmd->indexar == nullptr) {
return;
}
if (ob->type == OB_MESH) {
select_editbmesh_hook(ob, hmd);
}
else if (ob->type == OB_LATTICE) {
select_editlattice_hook(ob, hmd);
}
else if (ob->type == OB_CURVES_LEGACY) {
select_editcurve_hook(ob, hmd);
}
else if (ob->type == OB_SURF) {
select_editcurve_hook(ob, hmd);
}
}
/* special poll operators for hook operators */
/* TODO: check for properties window modifier context too as alternative? */
static bool hook_op_edit_poll(bContext *C)
{
Object *obedit = CTX_data_edit_object(C);
if (obedit) {
if (ED_operator_editmesh(C)) {
return true;
}
if (ED_operator_editsurfcurve(C)) {
return true;
}
if (ED_operator_editlattice(C)) {
return true;
}
// if (ED_operator_editmball(C)) return true;
}
return false;
}
static Object *add_hook_object_new(
Main *bmain, Scene *scene, ViewLayer *view_layer, View3D *v3d, Object *obedit)
{
Base *basedit;
Object *ob;
ob = BKE_object_add(bmain, scene, view_layer, OB_EMPTY, nullptr);
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
Base *basact = BKE_view_layer_active_base_get(view_layer);
BLI_assert(basact->object == ob);
if (v3d && v3d->localvd) {
basact->local_view_bits |= v3d->local_view_uid;
}
/* icky, BKE_object_add sets new base as active.
* so set it back to the original edit object */
basedit = BKE_view_layer_base_find(view_layer, obedit);
view_layer->basact = basedit;
return ob;
}
static int add_hook_object(const bContext *C,
Main *bmain,
Scene *scene,
ViewLayer *view_layer,
View3D *v3d,
Object *obedit,
Object *ob,
int mode,
ReportList *reports)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
HookModifierData *hmd = nullptr;
float cent[3];
float pose_mat[4][4];
int indexar_num, ok, *indexar;
char name[MAX_NAME];
ok = object_hook_index_array(bmain, scene, obedit, &indexar, &indexar_num, name, cent);
if (!ok) {
BKE_report(reports, RPT_ERROR, "Requires selected vertices or active vertex group");
return false;
}
if (mode == OBJECT_ADDHOOK_NEWOB && !ob) {
ob = add_hook_object_new(bmain, scene, view_layer, v3d, obedit);
/* transform cent to global coords for loc */
mul_v3_m4v3(ob->loc, obedit->object_to_world().ptr(), cent);
}
hmd = reinterpret_cast<HookModifierData *>(BKE_modifier_new(eModifierType_Hook));
BKE_modifiers_add_at_end_if_possible(obedit, &hmd->modifier);
SNPRINTF_UTF8(hmd->modifier.name, "Hook-%s", ob->id.name + 2);
BKE_modifier_unique_name(&obedit->modifiers, reinterpret_cast<ModifierData *>(hmd));
BKE_modifiers_persistent_uid_init(*obedit, hmd->modifier);
hmd->object = ob;
hmd->indexar = indexar;
copy_v3_v3(hmd->cent, cent);
hmd->indexar_num = indexar_num;
STRNCPY_UTF8(hmd->name, name);
unit_m4(pose_mat);
invert_m4_m4(obedit->runtime->world_to_object.ptr(), obedit->object_to_world().ptr());
if (mode == OBJECT_ADDHOOK_NEWOB) {
/* pass */
}
else {
/* may overwrite with pose-bone location, below */
mul_v3_m4v3(cent, obedit->world_to_object().ptr(), ob->object_to_world().location());
}
if (mode == OBJECT_ADDHOOK_SELOB_BONE) {
bArmature *arm = id_cast<bArmature *>(ob->data);
BLI_assert(ob->type == OB_ARMATURE);
if (arm->act_bone) {
bPoseChannel *pchan_act;
STRNCPY_UTF8(hmd->subtarget, arm->act_bone->name);
pchan_act = BKE_pose_channel_active_if_bonecoll_visible(ob);
if (LIKELY(pchan_act)) {
invert_m4_m4(pose_mat, pchan_act->pose_mat);
mul_v3_m4v3(cent, ob->object_to_world().ptr(), pchan_act->pose_mat[3]);
mul_v3_m4v3(cent, obedit->world_to_object().ptr(), cent);
}
}
else {
BKE_report(reports, RPT_WARNING, "Armature has no active object bone");
}
}
copy_v3_v3(hmd->cent, cent);
/* matrix calculus */
/* vert x (obmat x hook->world_to_object) x hook->object_to_world().ptr() x ob->world_to_object
*/
/* (parentinv) */
Scene *scene_eval = DEG_get_evaluated_scene(depsgraph);
Object *object_eval = DEG_get_evaluated(depsgraph, ob);
BKE_object_transform_copy(object_eval, ob);
BKE_object_where_is_calc(depsgraph, scene_eval, object_eval);
invert_m4_m4(object_eval->runtime->world_to_object.ptr(), object_eval->object_to_world().ptr());
/* apparently this call goes from right to left... */
mul_m4_series(hmd->parentinv,
pose_mat,
object_eval->world_to_object().ptr(),
obedit->object_to_world().ptr());
DEG_id_tag_update(&obedit->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
return true;
}
static wmOperatorStatus object_add_hook_selob_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 *obedit = CTX_data_edit_object(C);
Object *obsel = nullptr;
const bool use_bone = RNA_boolean_get(op->ptr, "use_bone");
const int mode = use_bone ? OBJECT_ADDHOOK_SELOB_BONE : OBJECT_ADDHOOK_SELOB;
CTX_DATA_BEGIN (C, Object *, ob, selected_objects) {
if (ob != obedit) {
obsel = ob;
break;
}
}
CTX_DATA_END;
if (!obsel) {
BKE_report(op->reports, RPT_ERROR, "Cannot add hook with no other selected objects");
return OPERATOR_CANCELLED;
}
if (use_bone && obsel->type != OB_ARMATURE) {
BKE_report(op->reports, RPT_ERROR, "Cannot add hook bone for a non armature object");
return OPERATOR_CANCELLED;
}
if (add_hook_object(C, bmain, scene, view_layer, nullptr, obedit, obsel, mode, op->reports)) {
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obedit);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_hook_add_selob(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hook to Selected Object";
ot->description = "Hook selected vertices to the first selected object";
ot->idname = "OBJECT_OT_hook_add_selob";
/* API callbacks. */
ot->exec = object_add_hook_selob_exec;
ot->poll = hook_op_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_boolean(ot->srna,
"use_bone",
false,
"Active Bone",
"Assign the hook to the hook object's active bone");
}
static wmOperatorStatus object_add_hook_newob_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);
View3D *v3d = CTX_wm_view3d(C);
Object *obedit = CTX_data_edit_object(C);
if (add_hook_object(
C, bmain, scene, view_layer, v3d, obedit, nullptr, OBJECT_ADDHOOK_NEWOB, op->reports))
{
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, obedit);
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_hook_add_newob(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Hook to New Object";
ot->description = "Hook selected vertices to a newly created object";
ot->idname = "OBJECT_OT_hook_add_newob";
/* API callbacks. */
ot->exec = object_add_hook_newob_exec;
ot->poll = hook_op_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static wmOperatorStatus object_hook_remove_exec(bContext *C, wmOperator *op)
{
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = CTX_data_edit_object(C);
ModifierData *md = static_cast<ModifierData *>(BLI_findlink(&ob->modifiers, num));
if (!(md && md->type == eModifierType_Hook)) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* remove functionality */
BKE_modifier_remove_from_list(ob, md);
BKE_modifier_free(md);
DEG_relations_tag_update(CTX_data_main(C));
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *hook_mod_itemf(bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
Object *ob = (C) ? CTX_data_edit_object(C) : nullptr;
EnumPropertyItem tmp = {0, "", 0, "", ""};
EnumPropertyItem *item = nullptr;
ModifierData *md = nullptr;
int a, totitem = 0;
if (!ob) {
return rna_enum_dummy_NULL_items;
}
for (a = 0, md = static_cast<ModifierData *>(ob->modifiers.first); md; md = md->next, a++) {
if (md->type == eModifierType_Hook) {
tmp.value = a;
tmp.icon = ICON_HOOK;
tmp.identifier = md->name;
tmp.name = md->name;
RNA_enum_item_add(&item, &totitem, &tmp);
}
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
void OBJECT_OT_hook_remove(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Remove Hook";
ot->idname = "OBJECT_OT_hook_remove";
ot->description = "Remove a hook from the active object";
/* API callbacks. */
ot->exec = object_hook_remove_exec;
ot->invoke = WM_menu_invoke;
ot->poll = hook_op_edit_poll;
/* flags */
/* this operator removes modifier which isn't stored in local undo stack, * so redoing it from
* redo panel gives totally weird results. */
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(
ot->srna, "modifier", rna_enum_dummy_NULL_items, 0, "Modifier", "Modifier number to remove");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
ot->prop = prop;
}
static wmOperatorStatus object_hook_reset_exec(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
BKE_object_modifier_hook_reset(ob, hmd);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_reset(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Reset Hook";
ot->description = "Recalculate and clear offset transformation";
ot->idname = "OBJECT_OT_hook_reset";
/* callbacks */
ot->exec = object_hook_reset_exec;
ot->poll = hook_op_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"modifier",
rna_enum_dummy_NULL_items,
0,
"Modifier",
"Modifier number to assign to");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static wmOperatorStatus object_hook_recenter_exec(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
Scene *scene = CTX_data_scene(C);
float bmat[3][3], imat[3][3];
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* recenter functionality */
copy_m3_m4(bmat, ob->object_to_world().ptr());
invert_m3_m3(imat, bmat);
sub_v3_v3v3(hmd->cent, scene->cursor.location, ob->object_to_world().location());
mul_m3_v3(imat, hmd->cent);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_recenter(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Recenter Hook";
ot->description = "Set hook center to cursor position";
ot->idname = "OBJECT_OT_hook_recenter";
/* callbacks */
ot->exec = object_hook_recenter_exec;
ot->poll = hook_op_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"modifier",
rna_enum_dummy_NULL_items,
0,
"Modifier",
"Modifier number to assign to");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static wmOperatorStatus object_hook_assign_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
float cent[3];
char name[MAX_NAME];
int *indexar, indexar_num;
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* assign functionality */
if (!object_hook_index_array(bmain, scene, ob, &indexar, &indexar_num, name, cent)) {
BKE_report(op->reports, RPT_WARNING, "Requires selected vertices or active vertex group");
return OPERATOR_CANCELLED;
}
if (hmd->indexar) {
MEM_delete(hmd->indexar);
}
copy_v3_v3(hmd->cent, cent);
hmd->indexar = indexar;
hmd->indexar_num = indexar_num;
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_assign(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Assign to Hook";
ot->description = "Assign the selected vertices to a hook";
ot->idname = "OBJECT_OT_hook_assign";
/* callbacks */
ot->exec = object_hook_assign_exec;
ot->poll = hook_op_edit_poll;
/* flags */
/* this operator changes data stored in modifier which doesn't get pushed to undo stack, * so
* redoing it from redo panel gives totally weird results. */
ot->flag = /*OPTYPE_REGISTER|*/ OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(ot->srna,
"modifier",
rna_enum_dummy_NULL_items,
0,
"Modifier",
"Modifier number to assign to");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
static wmOperatorStatus object_hook_select_exec(bContext *C, wmOperator *op)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "modifier", RNA_HookModifier);
int num = RNA_enum_get(op->ptr, "modifier");
Object *ob = nullptr;
HookModifierData *hmd = nullptr;
object_hook_from_context(C, &ptr, num, &ob, &hmd);
if (hmd == nullptr) {
BKE_report(op->reports, RPT_ERROR, "Could not find hook modifier");
return OPERATOR_CANCELLED;
}
/* select functionality */
object_hook_select(ob, hmd);
DEG_id_tag_update(ob->data, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data);
return OPERATOR_FINISHED;
}
void OBJECT_OT_hook_select(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Select Hook";
ot->description = "Select affected vertices on mesh";
ot->idname = "OBJECT_OT_hook_select";
/* callbacks */
ot->exec = object_hook_select_exec;
ot->poll = hook_op_edit_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
prop = RNA_def_enum(
ot->srna, "modifier", rna_enum_dummy_NULL_items, 0, "Modifier", "Modifier number to remove");
RNA_def_enum_funcs(prop, hook_mod_itemf);
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
}
} // namespace blender::ed::object

View File

@@ -0,0 +1,394 @@
/* SPDX-FileCopyrightText: 2008 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#pragma once
#include "BLI_vector.hh"
#include "RNA_types.hh"
namespace blender {
struct bContext;
struct ModifierData;
struct Object;
struct StructRNA;
struct wmKeyConfig;
struct wmOperator;
struct wmOperatorType;
namespace ed::object {
/* add hook menu */
enum eObject_Hook_Add_Mode {
OBJECT_ADDHOOK_NEWOB = 1,
OBJECT_ADDHOOK_SELOB,
OBJECT_ADDHOOK_SELOB_BONE,
};
/* internal exports only */
/* object_transform.cc */
void OBJECT_OT_location_clear(wmOperatorType *ot);
void OBJECT_OT_rotation_clear(wmOperatorType *ot);
void OBJECT_OT_scale_clear(wmOperatorType *ot);
void OBJECT_OT_origin_clear(wmOperatorType *ot);
void OBJECT_OT_visual_transform_apply(wmOperatorType *ot);
void OBJECT_OT_transform_apply(wmOperatorType *ot);
void OBJECT_OT_parent_inverse_apply(wmOperatorType *ot);
void OBJECT_OT_transform_axis_target(wmOperatorType *ot);
void object_transform_axis_target_modal_keymap(wmKeyConfig *keyconf);
void OBJECT_OT_origin_set(wmOperatorType *ot);
/* `object_relations.cc` */
void OBJECT_OT_parent_set(wmOperatorType *ot);
void OBJECT_OT_parent_no_inverse_set(wmOperatorType *ot);
void OBJECT_OT_parent_clear(wmOperatorType *ot);
void OBJECT_OT_vertex_parent_set(wmOperatorType *ot);
void OBJECT_OT_track_set(wmOperatorType *ot);
void OBJECT_OT_track_clear(wmOperatorType *ot);
void OBJECT_OT_make_local(wmOperatorType *ot);
void OBJECT_OT_make_single_user(wmOperatorType *ot);
void OBJECT_OT_make_links_scene(wmOperatorType *ot);
void OBJECT_OT_make_links_data(wmOperatorType *ot);
void OBJECT_OT_make_override_library(wmOperatorType *ot);
void OBJECT_OT_reset_override_library(wmOperatorType *ot);
void OBJECT_OT_clear_override_library(wmOperatorType *ot);
/**
* Used for drop-box.
* Assigns to object under cursor, only first material slot.
*/
void OBJECT_OT_drop_named_material(wmOperatorType *ot);
/**
* Used for drop-box.
* Assigns to object under cursor, creates a new geometry nodes modifier.
*/
void OBJECT_OT_drop_geometry_nodes(wmOperatorType *ot);
/**
* \note Only for empty-image objects, this operator is needed
*/
void OBJECT_OT_unlink_data(wmOperatorType *ot);
/* object_edit.cc */
void OBJECT_OT_hide_view_set(wmOperatorType *ot);
void OBJECT_OT_hide_view_clear(wmOperatorType *ot);
void OBJECT_OT_hide_collection(wmOperatorType *ot);
void OBJECT_OT_mode_set(wmOperatorType *ot);
void OBJECT_OT_mode_set_with_submode(wmOperatorType *ot);
void OBJECT_OT_editmode_toggle(wmOperatorType *ot);
void OBJECT_OT_posemode_toggle(wmOperatorType *ot);
void OBJECT_OT_shade_smooth(wmOperatorType *ot);
void OBJECT_OT_shade_smooth_by_angle(wmOperatorType *ot);
void OBJECT_OT_shade_auto_smooth(wmOperatorType *ot);
void OBJECT_OT_shade_flat(wmOperatorType *ot);
void OBJECT_OT_paths_calculate(wmOperatorType *ot);
void OBJECT_OT_paths_update(wmOperatorType *ot);
void OBJECT_OT_paths_clear(wmOperatorType *ot);
void OBJECT_OT_paths_update_visible(wmOperatorType *ot);
void OBJECT_OT_forcefield_toggle(wmOperatorType *ot);
void OBJECT_OT_move_to_collection(wmOperatorType *ot);
void OBJECT_OT_link_to_collection(wmOperatorType *ot);
void move_to_collection_menu_register();
void link_to_collection_menu_register();
void OBJECT_OT_transfer_mode(wmOperatorType *ot);
/* `object_select.cc` */
void OBJECT_OT_select_all(wmOperatorType *ot);
void OBJECT_OT_select_random(wmOperatorType *ot);
void OBJECT_OT_select_by_type(wmOperatorType *ot);
void OBJECT_OT_select_linked(wmOperatorType *ot);
void OBJECT_OT_select_grouped(wmOperatorType *ot);
void OBJECT_OT_select_mirror(wmOperatorType *ot);
void OBJECT_OT_select_more(wmOperatorType *ot);
void OBJECT_OT_select_less(wmOperatorType *ot);
void OBJECT_OT_select_same_collection(wmOperatorType *ot);
/* object_add.cc */
void OBJECT_OT_add(wmOperatorType *ot);
void OBJECT_OT_lattice_add_to_selected(wmOperatorType *ot);
void OBJECT_OT_add_named(wmOperatorType *ot);
void OBJECT_OT_transform_to_mouse(wmOperatorType *ot);
void OBJECT_OT_metaball_add(wmOperatorType *ot);
void OBJECT_OT_text_add(wmOperatorType *ot);
void OBJECT_OT_armature_add(wmOperatorType *ot);
void OBJECT_OT_empty_add(wmOperatorType *ot);
void OBJECT_OT_lightprobe_add(wmOperatorType *ot);
void OBJECT_OT_empty_image_add(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_add(wmOperatorType *ot);
void OBJECT_OT_light_add(wmOperatorType *ot);
void OBJECT_OT_effector_add(wmOperatorType *ot);
void OBJECT_OT_camera_add(wmOperatorType *ot);
void OBJECT_OT_speaker_add(wmOperatorType *ot);
void OBJECT_OT_curves_random_add(wmOperatorType *ot);
void OBJECT_OT_curves_empty_hair_add(wmOperatorType *ot);
void OBJECT_OT_pointcloud_random_add(wmOperatorType *ot);
/**
* Only used as menu.
*/
void OBJECT_OT_collection_instance_add(wmOperatorType *ot);
void OBJECT_OT_collection_external_asset_drop(wmOperatorType *ot);
void OBJECT_OT_data_instance_add(wmOperatorType *ot);
void OBJECT_OT_duplicates_make_real(wmOperatorType *ot);
void OBJECT_OT_duplicate(wmOperatorType *ot);
void OBJECT_OT_delete(wmOperatorType *ot);
void OBJECT_OT_join(wmOperatorType *ot);
void OBJECT_OT_join_shapes(wmOperatorType *ot);
void OBJECT_OT_update_shapes(wmOperatorType *ot);
void OBJECT_OT_convert(wmOperatorType *ot);
/* `object_volume.cc` */
void OBJECT_OT_volume_add(wmOperatorType *ot);
/**
* Called by other space types too.
*/
void OBJECT_OT_volume_import(wmOperatorType *ot);
/* `object_hook.cc` */
void OBJECT_OT_hook_add_selob(wmOperatorType *ot);
void OBJECT_OT_hook_add_newob(wmOperatorType *ot);
void OBJECT_OT_hook_remove(wmOperatorType *ot);
void OBJECT_OT_hook_select(wmOperatorType *ot);
void OBJECT_OT_hook_assign(wmOperatorType *ot);
void OBJECT_OT_hook_reset(wmOperatorType *ot);
void OBJECT_OT_hook_recenter(wmOperatorType *ot);
/* `object_collection.cc` */
void COLLECTION_OT_create(wmOperatorType *ot);
void COLLECTION_OT_objects_remove_all(wmOperatorType *ot);
void COLLECTION_OT_objects_remove(wmOperatorType *ot);
void COLLECTION_OT_objects_add_active(wmOperatorType *ot);
void COLLECTION_OT_objects_remove_active(wmOperatorType *ot);
/* object_light_linking_ops.cc */
void OBJECT_OT_light_linking_receiver_collection_new(wmOperatorType *ot);
void OBJECT_OT_light_linking_receivers_select(wmOperatorType *ot);
void OBJECT_OT_light_linking_receivers_link(wmOperatorType *ot);
void OBJECT_OT_light_linking_blocker_collection_new(wmOperatorType *ot);
void OBJECT_OT_light_linking_blockers_select(wmOperatorType *ot);
void OBJECT_OT_light_linking_blockers_link(wmOperatorType *ot);
void OBJECT_OT_light_linking_unlink_from_collection(wmOperatorType *ot);
/* object_camera.cc */
void OBJECT_OT_camera_custom_update(wmOperatorType *ot);
/* `object_modifier.cc` */
bool edit_modifier_poll_generic(bContext *C,
StructRNA *rna_type,
int obtype_flag,
bool is_editmode_allowed,
bool is_liboverride_allowed);
void edit_modifier_properties(wmOperatorType *ot);
bool edit_modifier_invoke_properties(bContext *C, wmOperator *op);
ModifierData *edit_modifier_property_get(wmOperator *op, Object *ob, int type);
void OBJECT_OT_modifier_add(wmOperatorType *ot);
void OBJECT_OT_modifier_remove(wmOperatorType *ot);
void OBJECT_OT_modifiers_clear(wmOperatorType *ot);
void OBJECT_OT_modifier_move_up(wmOperatorType *ot);
void OBJECT_OT_modifier_move_down(wmOperatorType *ot);
void OBJECT_OT_modifier_move_to_index(wmOperatorType *ot);
void OBJECT_OT_modifier_apply(wmOperatorType *ot);
void OBJECT_OT_modifier_apply_as_shapekey(wmOperatorType *ot);
void OBJECT_OT_modifier_convert(wmOperatorType *ot);
void OBJECT_OT_modifier_copy(wmOperatorType *ot);
void OBJECT_OT_modifier_copy_to_selected(wmOperatorType *ot);
void OBJECT_OT_modifiers_copy_to_selected(wmOperatorType *ot);
void OBJECT_OT_modifier_set_active(wmOperatorType *ot);
void OBJECT_OT_multires_subdivide(wmOperatorType *ot);
void OBJECT_OT_multires_reshape(wmOperatorType *ot);
void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot);
void OBJECT_OT_multires_base_apply(wmOperatorType *ot);
void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot);
void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot);
void OBJECT_OT_multires_external_save(wmOperatorType *ot);
void OBJECT_OT_multires_external_pack(wmOperatorType *ot);
void OBJECT_OT_correctivesmooth_bind(wmOperatorType *ot);
void OBJECT_OT_meshdeform_bind(wmOperatorType *ot);
void OBJECT_OT_explode_refresh(wmOperatorType *ot);
void OBJECT_OT_ocean_bake(wmOperatorType *ot);
void OBJECT_OT_skin_root_mark(wmOperatorType *ot);
void OBJECT_OT_skin_loose_mark_clear(wmOperatorType *ot);
void OBJECT_OT_skin_radii_equalize(wmOperatorType *ot);
void OBJECT_OT_skin_armature_create(wmOperatorType *ot);
void OBJECT_OT_laplaciandeform_bind(wmOperatorType *ot);
void OBJECT_OT_surfacedeform_bind(wmOperatorType *ot);
void OBJECT_OT_geometry_nodes_input_attribute_toggle(wmOperatorType *ot);
void OBJECT_OT_geometry_node_tree_copy_assign(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_dash_modifier_segment_add(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_dash_modifier_segment_remove(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_dash_modifier_segment_move(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_time_modifier_segment_add(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_time_modifier_segment_remove(wmOperatorType *ot);
void OBJECT_OT_grease_pencil_time_modifier_segment_move(wmOperatorType *ot);
/* `object_shader_fx.cc` */
void OBJECT_OT_shaderfx_add(wmOperatorType *ot);
void OBJECT_OT_shaderfx_copy(wmOperatorType *ot);
void OBJECT_OT_shaderfx_remove(wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot);
void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot);
/* `object_constraint.cc` */
void OBJECT_OT_constraint_add(wmOperatorType *ot);
void OBJECT_OT_constraint_add_with_targets(wmOperatorType *ot);
void POSE_OT_constraint_add(wmOperatorType *ot);
void POSE_OT_constraint_add_with_targets(wmOperatorType *ot);
void OBJECT_OT_constraints_copy(wmOperatorType *ot);
void POSE_OT_constraints_copy(wmOperatorType *ot);
void OBJECT_OT_constraints_clear(wmOperatorType *ot);
void POSE_OT_constraints_clear(wmOperatorType *ot);
void POSE_OT_ik_add(wmOperatorType *ot);
void POSE_OT_ik_clear(wmOperatorType *ot);
void CONSTRAINT_OT_delete(wmOperatorType *ot);
void CONSTRAINT_OT_apply(wmOperatorType *ot);
void CONSTRAINT_OT_copy(wmOperatorType *ot);
void CONSTRAINT_OT_copy_to_selected(wmOperatorType *ot);
void CONSTRAINT_OT_move_up(wmOperatorType *ot);
void CONSTRAINT_OT_move_to_index(wmOperatorType *ot);
void CONSTRAINT_OT_move_down(wmOperatorType *ot);
void CONSTRAINT_OT_stretchto_reset(wmOperatorType *ot);
void CONSTRAINT_OT_limitdistance_reset(wmOperatorType *ot);
void CONSTRAINT_OT_childof_set_inverse(wmOperatorType *ot);
void CONSTRAINT_OT_childof_clear_inverse(wmOperatorType *ot);
void CONSTRAINT_OT_objectsolver_set_inverse(wmOperatorType *ot);
void CONSTRAINT_OT_objectsolver_clear_inverse(wmOperatorType *ot);
void CONSTRAINT_OT_followpath_path_animate(wmOperatorType *ot);
/* object_vgroup.cc */
void OBJECT_OT_vertex_group_add(wmOperatorType *ot);
void OBJECT_OT_vertex_group_remove(wmOperatorType *ot);
void OBJECT_OT_vertex_group_assign(wmOperatorType *ot);
void OBJECT_OT_vertex_group_assign_new(wmOperatorType *ot);
void OBJECT_OT_vertex_group_remove_from(wmOperatorType *ot);
void OBJECT_OT_vertex_group_select(wmOperatorType *ot);
void OBJECT_OT_vertex_group_deselect(wmOperatorType *ot);
void OBJECT_OT_vertex_group_copy_to_selected(wmOperatorType *ot);
void OBJECT_OT_vertex_group_copy(wmOperatorType *ot);
void OBJECT_OT_vertex_group_normalize(wmOperatorType *ot);
void OBJECT_OT_vertex_group_normalize_all(wmOperatorType *ot);
void OBJECT_OT_vertex_group_levels(wmOperatorType *ot);
void OBJECT_OT_vertex_group_lock(wmOperatorType *ot);
void OBJECT_OT_vertex_group_invert(wmOperatorType *ot);
void OBJECT_OT_vertex_group_smooth(wmOperatorType *ot);
void OBJECT_OT_vertex_group_clean(wmOperatorType *ot);
void OBJECT_OT_vertex_group_quantize(wmOperatorType *ot);
void OBJECT_OT_vertex_group_limit_total(wmOperatorType *ot);
void OBJECT_OT_vertex_group_mirror(wmOperatorType *ot);
void OBJECT_OT_vertex_group_set_active(wmOperatorType *ot);
void OBJECT_OT_vertex_group_sort(wmOperatorType *ot);
void OBJECT_OT_vertex_group_move(wmOperatorType *ot);
void OBJECT_OT_vertex_weight_paste(wmOperatorType *ot);
void OBJECT_OT_vertex_weight_delete(wmOperatorType *ot);
void OBJECT_OT_vertex_weight_set_active(wmOperatorType *ot);
void OBJECT_OT_vertex_weight_normalize_active_vertex(wmOperatorType *ot);
void OBJECT_OT_vertex_weight_copy(wmOperatorType *ot);
/* `object_warp.cc` */
void TRANSFORM_OT_vertex_warp(wmOperatorType *ot);
/* `object_shapekey.cc` */
void OBJECT_OT_shape_key_add(wmOperatorType *ot);
void OBJECT_OT_shape_key_copy(wmOperatorType *ot);
void OBJECT_OT_shape_key_remove(wmOperatorType *ot);
void OBJECT_OT_shape_key_clear(wmOperatorType *ot);
void OBJECT_OT_shape_key_retime(wmOperatorType *ot);
void OBJECT_OT_shape_key_mirror(wmOperatorType *ot);
void OBJECT_OT_shape_key_move(wmOperatorType *ot);
void OBJECT_OT_shape_key_lock(wmOperatorType *ot);
void OBJECT_OT_shape_key_make_basis(wmOperatorType *ot);
void OBJECT_OT_shape_key_apply_to_basis(wmOperatorType *ot);
/* `object_collection.cc` */
void OBJECT_OT_collection_add(wmOperatorType *ot);
void OBJECT_OT_collection_link(wmOperatorType *ot);
void OBJECT_OT_collection_remove(wmOperatorType *ot);
void OBJECT_OT_collection_unlink(wmOperatorType *ot);
void OBJECT_OT_collection_objects_select(wmOperatorType *ot);
/* `object_bake.cc` */
void OBJECT_OT_bake_image(wmOperatorType *ot);
void OBJECT_OT_bake(wmOperatorType *ot);
/* object_bake_simulation.cc */
namespace bake_simulation {
void OBJECT_OT_simulation_nodes_cache_calculate_to_frame(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_bake(wmOperatorType *ot);
void OBJECT_OT_simulation_nodes_cache_delete(wmOperatorType *ot);
void OBJECT_OT_geometry_node_bake_single(wmOperatorType *ot);
void OBJECT_OT_geometry_node_bake_delete_single(wmOperatorType *ot);
void OBJECT_OT_geometry_node_bake_pack_single(wmOperatorType *ot);
void OBJECT_OT_geometry_node_bake_unpack_single(wmOperatorType *ot);
} // namespace bake_simulation
/* `object_random.cc` */
void TRANSFORM_OT_vertex_random(wmOperatorType *ot);
/* object_remesh.cc */
void OBJECT_OT_voxel_remesh(wmOperatorType *ot);
void OBJECT_OT_voxel_size_edit(wmOperatorType *ot);
void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot);
/* object_data_transfer.cc */
/**
* Transfer mesh data from active to selected objects.
*/
void OBJECT_OT_data_transfer(wmOperatorType *ot);
void OBJECT_OT_datalayout_transfer(wmOperatorType *ot);
void object_modifier_add_asset_register();
void collection_importer_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);
/* object_visual_geometry_to_objects.cc */
void OBJECT_OT_visual_geometry_to_objects(wmOperatorType *ot);
} // namespace ed::object
} // namespace blender

View File

@@ -0,0 +1,300 @@
/* SPDX-FileCopyrightText: 2001-2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include "object_intern.hh"
#include "BKE_context.hh"
#include "BKE_light_linking.h"
#include "BKE_scene.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "UI_resources.hh"
#include "DNA_collection_types.h"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.hh"
namespace blender::ed::object {
/* -------------------------------------------------------------------- */
/** \name Create New Light Linking Receiver/Blocker Collection Operators
* \{ */
template<LightLinkingType link_type>
static wmOperatorStatus light_linking_collection_new_exec(bContext *C, wmOperator * /*op*/)
{
Main *bmain = CTX_data_main(C);
Object *object = context_active_object(C);
BKE_light_linking_collection_new(bmain, object, link_type);
return OPERATOR_FINISHED;
}
void OBJECT_OT_light_linking_receiver_collection_new(wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Light Linking Collection";
ot->description = "Create new light linking collection used by the active emitter";
ot->idname = "OBJECT_OT_light_linking_receiver_collection_new";
/* API callbacks. */
ot->exec = light_linking_collection_new_exec<LIGHT_LINKING_RECEIVER>;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OBJECT_OT_light_linking_blocker_collection_new(wmOperatorType *ot)
{
/* identifiers */
ot->name = "New Light Linking Collection";
ot->description = "Create new light linking collection used by the active emitter";
ot->idname = "OBJECT_OT_light_linking_blocker_collection_new";
/* API callbacks. */
ot->exec = light_linking_collection_new_exec<LIGHT_LINKING_BLOCKER>;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Select Light Linking Receivers/Blockers Operator
* \{ */
template<LightLinkingType link_type>
static wmOperatorStatus light_linking_select_exec(bContext *C, wmOperator * /*op*/)
{
const Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *emitter = context_active_object(C);
BKE_light_linking_select_receivers_of_emitter(*bmain, scene, view_layer, emitter, link_type);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
return OPERATOR_FINISHED;
}
void OBJECT_OT_light_linking_receivers_select(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Light Linking Receivers";
ot->description = "Select all objects which receive light from this emitter";
ot->idname = "OBJECT_OT_light_linking_receivers_select";
/* API callbacks. */
ot->exec = light_linking_select_exec<LIGHT_LINKING_RECEIVER>;
ot->poll = ED_operator_object_active;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
void OBJECT_OT_light_linking_blockers_select(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Select Light Linking Blockers";
ot->description = "Select all objects which block light from this emitter";
ot->idname = "OBJECT_OT_light_linking_blockers_select";
/* API callbacks. */
ot->exec = light_linking_select_exec<LIGHT_LINKING_BLOCKER>;
ot->poll = ED_operator_object_active;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Link Receivers/Blockers to Emitter Operator
* \{ */
template<LightLinkingType link_type>
static wmOperatorStatus light_linking_link_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Scene *scene = CTX_data_scene(C);
Object *emitter = context_active_object(C);
const eCollectionLightLinkingState link_state = eCollectionLightLinkingState(
RNA_enum_get(op->ptr, "link_state"));
Collection *collection = BKE_light_linking_collection_get(emitter, link_type);
if (Scene *collection_scene = BKE_scene_find_from_collection(bmain, collection);
collection_scene != nullptr)
{
BKE_reportf(op->reports,
RPT_ERROR,
"Cannot link to light linking collection '%s' that is used by scene '%s'",
collection->id.name + 2,
collection_scene->id.name + 2);
return OPERATOR_CANCELLED;
}
CTX_DATA_BEGIN (C, Object *, receiver, selected_objects) {
if (receiver == emitter) {
continue;
}
BKE_light_linking_link_receiver_to_emitter(bmain, emitter, receiver, link_type, link_state);
}
CTX_DATA_END;
/* It is possible that the receiver collection is also used by the view layer.
* For this case send a notifier so that the UI is updated for the changes in the collection
* content. */
WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene);
return OPERATOR_FINISHED;
}
void OBJECT_OT_light_linking_receivers_link(wmOperatorType *ot)
{
static const EnumPropertyItem link_state_items[] = {
{COLLECTION_LIGHT_LINKING_STATE_INCLUDE,
"INCLUDE",
ICON_NONE,
"Include",
"Include selected receivers to receive light from the active emitter"},
{COLLECTION_LIGHT_LINKING_STATE_EXCLUDE,
"EXCLUDE",
ICON_NONE,
"Exclude",
"Exclude selected receivers from receiving light from the active emitter"},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Link Receivers to Emitter";
ot->description = "Light link selected receivers to the active emitter object";
ot->idname = "OBJECT_OT_light_linking_receivers_link";
/* API callbacks. */
ot->exec = light_linking_link_exec<LIGHT_LINKING_RECEIVER>;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(ot->srna,
"link_state",
link_state_items,
COLLECTION_LIGHT_LINKING_STATE_INCLUDE,
"Link State",
"State of the light linking");
}
void OBJECT_OT_light_linking_blockers_link(wmOperatorType *ot)
{
static const EnumPropertyItem link_state_items[] = {
{COLLECTION_LIGHT_LINKING_STATE_INCLUDE,
"INCLUDE",
ICON_NONE,
"Include",
"Include selected blockers to cast shadows from the active emitter"},
{COLLECTION_LIGHT_LINKING_STATE_EXCLUDE,
"EXCLUDE",
ICON_NONE,
"Exclude",
"Exclude selected blockers from casting shadows from the active emitter"},
{0, nullptr, 0, nullptr, nullptr},
};
/* identifiers */
ot->name = "Link Blockers to Emitter";
ot->description = "Light link selected blockers to the active emitter object";
ot->idname = "OBJECT_OT_light_linking_blockers_link";
/* API callbacks. */
ot->exec = light_linking_link_exec<LIGHT_LINKING_BLOCKER>;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
ot->prop = RNA_def_enum(ot->srna,
"link_state",
link_state_items,
COLLECTION_LIGHT_LINKING_STATE_INCLUDE,
"Link State",
"State of the shadow linking");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Unlink from the Light Linking Collection Operator
* \{ */
static wmOperatorStatus light_linking_unlink_from_collection_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
ID *id = static_cast<ID *>(CTX_data_pointer_get_type(C, "id", RNA_ID).data);
Collection *collection = static_cast<Collection *>(
CTX_data_pointer_get_type(C, "collection", RNA_Collection).data);
if (!id || !collection) {
return OPERATOR_PASS_THROUGH;
}
if (Scene *scene = BKE_scene_find_from_collection(bmain, collection); scene != nullptr) {
BKE_reportf(op->reports,
RPT_ERROR,
"Cannot unlink from light linking collection '%s' that is used by scene '%s'",
collection->id.name + 2,
scene->id.name + 2);
return OPERATOR_CANCELLED;
}
if (!BKE_light_linking_unlink_id_from_collection(bmain, collection, id, op->reports)) {
return OPERATOR_CANCELLED;
}
/* Copy notifiers from the Outliner's "Unlink" operation for objects and collections. */
WM_event_add_notifier(C, NC_SCENE | ND_LAYER, nullptr);
WM_event_add_notifier(C, NC_ID | NA_EDITED, nullptr);
WM_event_add_notifier(C, NC_SPACE | ND_SPACE_OUTLINER, nullptr);
return OPERATOR_FINISHED;
}
void OBJECT_OT_light_linking_unlink_from_collection(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Remove From Light Linking Collection";
ot->description = "Remove this object or collection from the light linking collection";
ot->idname = "OBJECT_OT_light_linking_unlink_from_collection";
/* API callbacks. */
ot->exec = light_linking_unlink_from_collection_exec;
ot->poll = ED_operator_object_active_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
} // namespace blender::ed::object

View File

@@ -0,0 +1,632 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*
* General utils to handle mode switching,
* actual mode switching logic is per-object type.
*/
#include "DNA_object_enums.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_time.h"
#include "BLI_utildefines.h"
#include "BKE_context.hh"
#include "BKE_idtype.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_library.hh"
#include "BKE_modifier.hh"
#include "BKE_object.hh"
#include "BKE_paint.hh"
#include "BKE_paint_types.hh"
#include "BKE_report.hh"
#include "BLI_math_vector.h"
#include "BLI_string.h"
#include "WM_api.hh"
#include "WM_types.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "ED_armature.hh"
#include "ED_gpencil_legacy.hh"
#include "ED_outliner.hh"
#include "ED_paint.hh"
#include "ED_physics.hh"
#include "ED_sculpt.hh"
#include "ED_undo.hh"
#include "ED_view3d.hh"
#include "WM_toolsystem.hh"
#include "ED_object.hh" /* own include */
#include "object_intern.hh"
namespace blender::ed::object {
/* -------------------------------------------------------------------- */
/** \name High Level Mode Operations
* \{ */
static const char *object_mode_op_string(eObjectMode mode)
{
if (mode & OB_MODE_EDIT) {
return "OBJECT_OT_editmode_toggle";
}
if (mode == OB_MODE_SCULPT) {
return "SCULPT_OT_sculptmode_toggle";
}
if (mode == OB_MODE_VERTEX_PAINT) {
return "PAINT_OT_vertex_paint_toggle";
}
if (mode == OB_MODE_WEIGHT_PAINT) {
return "PAINT_OT_weight_paint_toggle";
}
if (mode == OB_MODE_TEXTURE_PAINT) {
return "PAINT_OT_texture_paint_toggle";
}
if (mode == OB_MODE_PARTICLE_EDIT) {
return "PARTICLE_OT_particle_edit_toggle";
}
if (mode == OB_MODE_POSE) {
return "OBJECT_OT_posemode_toggle";
}
if (mode == OB_MODE_PAINT_GREASE_PENCIL) {
return "GREASE_PENCIL_OT_paintmode_toggle";
}
if (mode == OB_MODE_SCULPT_GREASE_PENCIL) {
return "GREASE_PENCIL_OT_sculptmode_toggle";
}
if (mode == OB_MODE_WEIGHT_GREASE_PENCIL) {
return "GREASE_PENCIL_OT_weightmode_toggle";
}
if (mode == OB_MODE_VERTEX_GREASE_PENCIL) {
return "GREASE_PENCIL_OT_vertexmode_toggle";
}
if (mode == OB_MODE_SCULPT_CURVES) {
return "CURVES_OT_sculptmode_toggle";
}
return nullptr;
}
bool mode_compat_test(const Object *ob, eObjectMode mode)
{
if (mode == OB_MODE_OBJECT) {
return true;
}
switch (ob->type) {
case OB_MESH:
if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT | OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT |
OB_MODE_TEXTURE_PAINT))
{
return true;
}
if (mode & OB_MODE_PARTICLE_EDIT) {
if (ED_object_particle_edit_mode_supported(ob)) {
return true;
}
}
break;
case OB_CURVES_LEGACY:
case OB_SURF:
case OB_FONT:
case OB_MBALL:
case OB_POINTCLOUD:
case OB_LATTICE:
if (mode & OB_MODE_EDIT) {
return true;
}
break;
case OB_ARMATURE:
if (mode & (OB_MODE_EDIT | OB_MODE_POSE)) {
return true;
}
break;
case OB_CURVES:
if (mode & (OB_MODE_EDIT | OB_MODE_SCULPT_CURVES)) {
return true;
}
break;
case OB_GREASE_PENCIL:
if (mode & (OB_MODE_EDIT | OB_MODE_PAINT_GREASE_PENCIL | OB_MODE_SCULPT_GREASE_PENCIL |
OB_MODE_WEIGHT_GREASE_PENCIL | OB_MODE_VERTEX_GREASE_PENCIL))
{
return true;
}
break;
default:
break;
}
return false;
}
bool mode_compat_set(bContext *C, Object *ob, eObjectMode mode, ReportList *reports)
{
bool ok;
if (!ELEM(ob->mode, mode, OB_MODE_OBJECT)) {
const char *opstring = object_mode_op_string(ob->mode);
WM_operator_name_call(C, opstring, wm::OpCallContext::ExecRegionWin, nullptr, nullptr);
ok = ELEM(ob->mode, mode, OB_MODE_OBJECT);
if (!ok) {
wmOperatorType *ot = WM_operatortype_find(opstring, false);
BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
}
}
else {
ok = true;
}
return ok;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic Mode Enter/Exit
*
* Supports exiting a mode without it being in the current context.
* This could be done for entering modes too if it's needed.
*
* \{ */
bool mode_set_ex(bContext *C, eObjectMode mode, bool use_undo, ReportList *reports)
{
wmWindowManager *wm = CTX_wm_manager(C);
const Main *bmain = CTX_data_main(C);
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
if (ob == nullptr) {
return (mode == OB_MODE_OBJECT);
}
if (ob->mode == mode) {
return true;
}
if (!mode_compat_test(ob, mode)) {
return false;
}
const char *opstring = object_mode_op_string((mode == OB_MODE_OBJECT) ? ob->mode : mode);
wmOperatorType *ot = WM_operatortype_find(opstring, false);
if (!use_undo) {
wm->op_undo_depth++;
}
WM_operator_name_call_ptr(C, ot, wm::OpCallContext::ExecRegionWin, nullptr, nullptr);
if (!use_undo) {
wm->op_undo_depth--;
}
if (ob->mode != mode) {
/* Give more specific error messages for cases that are known to fail (like linked and packed
* object-data). */
if (ob->data && !ID_IS_EDITABLE(ob->data)) {
const ID &obdata_id = *ob->data;
char obdata_idtype_name_lower[MAX_ID_NAME];
STRNCPY(obdata_idtype_name_lower, BKE_idtype_idcode_to_name(GS(obdata_id.name)));
BLI_str_tolower_ascii(obdata_idtype_name_lower, strlen(obdata_idtype_name_lower));
if (ID_IS_PACKED(ob->data)) {
BKE_reportf(reports,
RPT_ERROR,
"The '%s' %s data-block is packed and not editable. Use \"Make Local\" to "
"make it editable.",
BKE_id_name(obdata_id),
obdata_idtype_name_lower);
return false;
}
if (ID_IS_LINKED(ob->data)) {
BKE_reportf(reports,
RPT_ERROR,
"The '%s' %s data-block is linked and not editable. Use \"Make Local\" to "
"make it editable.",
BKE_id_name(obdata_id),
obdata_idtype_name_lower);
return false;
}
}
BKE_reportf(reports, RPT_ERROR, "Unable to execute '%s', error changing modes", ot->name);
return false;
}
return true;
}
bool mode_set(bContext *C, eObjectMode mode)
{
/* Don't do undo push by default, since this may be called by lower level code. */
return mode_set_ex(C, mode, true, nullptr);
}
/**
* Use for changing works-paces or changing active object.
* Caller can check #OB_MODE_ALL_MODE_DATA to test if this needs to be run.
*/
static bool ed_object_mode_generic_exit_ex(
Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob, bool only_test)
{
BLI_assert((bmain == nullptr) == only_test);
if (ob->mode & OB_MODE_EDIT) {
if (BKE_object_is_in_editmode(ob)) {
if (only_test) {
return true;
}
editmode_exit_ex(bmain, scene, ob, EM_FREEDATA);
}
}
else if (ob->mode & OB_MODE_VERTEX_PAINT) {
if (ob->runtime->sculpt_session &&
(ob->runtime->sculpt_session->mode_type == OB_MODE_VERTEX_PAINT))
{
if (only_test) {
return true;
}
ED_object_vpaintmode_exit_ex(*ob);
}
}
else if (ob->mode & OB_MODE_WEIGHT_PAINT) {
if (ob->runtime->sculpt_session &&
(ob->runtime->sculpt_session->mode_type == OB_MODE_WEIGHT_PAINT))
{
if (only_test) {
return true;
}
ED_object_wpaintmode_exit_ex(*ob);
}
}
else if (ob->mode & OB_MODE_SCULPT) {
if (ob->runtime->sculpt_session && (ob->runtime->sculpt_session->mode_type == OB_MODE_SCULPT))
{
if (only_test) {
return true;
}
sculpt_paint::object_sculpt_mode_exit(*bmain, *depsgraph, *scene, *ob);
}
}
else if (ob->mode & OB_MODE_POSE) {
if (ob->pose != nullptr) {
if (only_test) {
return true;
}
ED_object_posemode_exit_ex(bmain, ob);
}
}
else if (ob->mode & OB_MODE_TEXTURE_PAINT) {
if (only_test) {
return true;
}
ED_object_texture_paint_mode_exit_ex(*bmain, *scene, *ob);
}
else if (ob->mode & OB_MODE_PARTICLE_EDIT) {
if (only_test) {
return true;
}
ED_object_particle_edit_mode_exit_ex(scene, ob);
}
else if (ob->type == OB_GREASE_PENCIL) {
BLI_assert((ob->mode & OB_MODE_OBJECT) == 0);
if (only_test) {
return true;
}
ob->restore_mode = ob->mode;
ob->mode &= ~(OB_MODE_PAINT_GREASE_PENCIL | OB_MODE_EDIT | OB_MODE_SCULPT_GREASE_PENCIL |
OB_MODE_WEIGHT_GREASE_PENCIL | OB_MODE_VERTEX_GREASE_PENCIL);
/* Inform all evaluated versions that we changed the mode. */
DEG_id_tag_update_ex(bmain, &ob->id, ID_RECALC_SYNC_TO_EVAL);
}
else {
if (only_test) {
return false;
}
BLI_assert((ob->mode & OB_MODE_ALL_MODE_DATA) == 0);
}
return false;
}
/* When locked, it's almost impossible to select the pose-object
* then the mesh-object to enter weight paint mode.
* Even when the object mode is not locked this is inconvenient - so allow in either case.
*
* In this case move our pose object in/out of pose mode.
* This is in fits with the convention of selecting multiple objects and entering a mode.
*/
static void ed_object_posemode_set_for_weight_paint_ex(bContext *C,
Main *bmain,
Object *ob_arm,
const bool is_mode_set)
{
View3D *v3d = CTX_wm_view3d(C);
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
if (ob_arm != nullptr) {
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
const Base *base_arm = BKE_view_layer_base_find(view_layer, ob_arm);
if (base_arm && BASE_VISIBLE(v3d, base_arm)) {
if (is_mode_set) {
if ((ob_arm->mode & OB_MODE_POSE) != 0) {
ED_object_posemode_exit_ex(bmain, ob_arm);
}
}
else {
/* Only check selected status when entering weight-paint mode
* because we may have multiple armature objects.
* Selecting one will de-select the other, which would leave it in pose-mode
* when exiting weight paint mode. While usable, this looks like inconsistent
* behavior from a user perspective. */
if (base_arm->flag & BASE_SELECTED) {
if ((ob_arm->mode & OB_MODE_POSE) == 0) {
ED_object_posemode_enter_ex(bmain, ob_arm);
}
}
}
}
}
}
void posemode_set_for_weight_paint(bContext *C, Main *bmain, Object *ob, const bool is_mode_set)
{
VirtualModifierData virtual_modifier_data;
ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data);
for (; md; md = md->next) {
if (md->type == eModifierType_Armature) {
ArmatureModifierData *amd = reinterpret_cast<ArmatureModifierData *>(md);
Object *ob_arm = amd->object;
ed_object_posemode_set_for_weight_paint_ex(C, bmain, ob_arm, is_mode_set);
}
else if (md->type == eModifierType_GreasePencilArmature) {
GreasePencilArmatureModifierData *amd = reinterpret_cast<GreasePencilArmatureModifierData *>(
md);
Object *ob_arm = amd->object;
ed_object_posemode_set_for_weight_paint_ex(C, bmain, ob_arm, is_mode_set);
}
}
}
void mode_generic_exit(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
{
ed_object_mode_generic_exit_ex(bmain, depsgraph, scene, ob, false);
}
bool mode_generic_has_data(Depsgraph *depsgraph, const Object *ob)
{
return ed_object_mode_generic_exit_ex(
nullptr, depsgraph, nullptr, const_cast<Object *>(ob), true);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Transfer Mode
*
* Enters the same mode of the current active object in another object,
* leaving the mode of the current object.
* \{ */
static bool object_transfer_mode_poll(bContext *C)
{
if (!CTX_wm_region_view3d(C)) {
return false;
}
const Object *ob = CTX_data_active_object(C);
return ob && (ob->mode != OB_MODE_OBJECT);
}
/* Update the viewport rotation origin to the mouse cursor. */
static void object_transfer_mode_reposition_view_pivot(ARegion *region,
Paint *paint,
const int mval[2])
{
float global_loc[3];
if (!ED_view3d_autodist_simple(region, mval, global_loc, 0, nullptr)) {
return;
}
bke::PaintRuntime *paint_runtime = paint->runtime;
copy_v3_v3(paint_runtime->average_stroke_accum, global_loc);
paint_runtime->average_stroke_counter = 1;
paint_runtime->last_stroke_valid = true;
}
constexpr float mode_transfer_flash_length = 0.55f;
static auto &mode_transfer_overlay_start_times()
{
static Map<std::string, double> map;
return map;
}
static float alpha_from_time_get(const float anim_time)
{
if (anim_time < 0.0f) {
return 0.0f;
}
return (1.0f - (anim_time / mode_transfer_flash_length));
}
Map<std::string, float, 1> mode_transfer_overlay_current_state()
{
const double now = BLI_time_now_seconds();
/* Protect against possible concurrent access from multiple renderers or viewports. */
static Mutex mutex;
std::scoped_lock lock(mutex);
/* Remove finished animations form the global map. */
Map<std::string, double> &start_times = mode_transfer_overlay_start_times();
start_times.remove_if(
[&](const auto &item) { return (now - item.value) > mode_transfer_flash_length; });
Map<std::string, float, 1> factors;
for (const auto &item : start_times.items()) {
const float alpha = alpha_from_time_get(now - item.value);
if (alpha > 0.0f) {
factors.add_new(item.key, alpha);
}
}
return factors;
}
static void object_overlay_mode_transfer_animation_start(bContext *C, Object *ob_dst)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *ob_dst_eval = DEG_get_evaluated(depsgraph, ob_dst);
mode_transfer_overlay_start_times().add_as(ob_dst_eval->id.name, BLI_time_now_seconds());
}
static bool object_transfer_mode_to_base(bContext *C,
wmOperator *op,
Scene *scene,
Object * /*ob_src*/,
Object *ob_dst,
const eObjectMode mode_dst)
{
const Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
/* Undo is handled manually here, such that the entry in the user-visible undo history is named
* from the expected mode toggle operator name, and not the 'Transfer Mode' operator itself.
*
* The undo grouping is needed to ensure that only one step is visible, even though there may be
* two undo steps stored when executed successfully (moving source object to Object mode, and
* then target object to the previous mode of source object). */
ED_undo_group_begin(C);
const bool mode_transferred = mode_set_ex(C, OB_MODE_OBJECT, true, op->reports);
if (mode_transferred) {
BKE_view_layer_synced_ensure(*bmain, scene, view_layer);
Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst);
BKE_view_layer_base_deselect_all(*bmain, scene, view_layer);
BKE_view_layer_base_select_and_set_active(view_layer, base_dst);
/* Not entirely clear why, but this extra undo step (the two calls to #mode_set_ex should
* already create their own) is required. Otherwise some mode switching does not work as
* expected on undo/redo (see #130420 with Sculpt mode). */
ED_undo_push(C, "Change Active");
mode_set_ex(C, mode_dst, true, op->reports);
if (RNA_boolean_get(op->ptr, "use_flash_on_transfer")) {
object_overlay_mode_transfer_animation_start(C, ob_dst);
}
}
ED_undo_group_end(C);
return mode_transferred;
}
static wmOperatorStatus object_transfer_mode_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
Scene *scene = CTX_data_scene(C);
ARegion *region = CTX_wm_region(C);
Object *ob_src = CTX_data_active_object(C);
const eObjectMode mode_src = ob_src->mode;
Base *base_dst = ED_view3d_give_base_under_cursor(C, event->mval);
if (!base_dst) {
BKE_reportf(op->reports, RPT_ERROR, "No target object to transfer the mode to");
return OPERATOR_CANCELLED;
}
Object *ob_dst = base_dst->object;
if (ob_src == ob_dst) {
return OPERATOR_CANCELLED;
}
BLI_assert(ob_dst->id.orig_id == nullptr);
if (!ID_IS_EDITABLE(ob_dst) || !ID_IS_EDITABLE(ob_src)) {
BKE_reportf(op->reports,
RPT_ERROR,
"Unable to transfer mode, the source and/or target objects are not editable");
return OPERATOR_CANCELLED;
}
if (ID_IS_OVERRIDE_LIBRARY(ob_dst) && !ELEM(mode_src, OB_MODE_OBJECT, OB_MODE_POSE)) {
BKE_reportf(
op->reports,
RPT_ERROR,
"Current mode of source object '%s' is not compatible with target liboverride object '%s'",
ob_src->id.name + 2,
ob_dst->id.name + 2);
return OPERATOR_CANCELLED;
}
if (!mode_compat_test(ob_dst, mode_src)) {
BKE_reportf(op->reports,
RPT_ERROR,
"Current mode of source object '%s' is not compatible with target object '%s'",
ob_src->id.name + 2,
ob_dst->id.name + 2);
return OPERATOR_CANCELLED;
}
const bool mode_transferred = object_transfer_mode_to_base(
C, op, scene, ob_src, ob_dst, mode_src);
if (!mode_transferred) {
/* Error report should have been set by #object_transfer_mode_to_base call here. */
return OPERATOR_CANCELLED;
}
DEG_id_tag_update(&scene->id, ID_RECALC_SELECT);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
ED_outliner_select_sync_from_object_tag(C);
WM_toolsystem_update_from_context_view3d(C);
if (mode_src & OB_MODE_ALL_PAINT) {
Paint *paint = BKE_paint_get_active_from_context(C);
object_transfer_mode_reposition_view_pivot(region, paint, event->mval);
}
return OPERATOR_FINISHED;
}
void OBJECT_OT_transfer_mode(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Transfer Mode";
ot->idname = "OBJECT_OT_transfer_mode";
ot->description =
"Switches the active object and assigns the same mode to a new one under the mouse cursor, "
"leaving the active mode in the current one";
/* API callbacks. */
ot->invoke = object_transfer_mode_invoke;
ot->poll = object_transfer_mode_poll;
/* Undo push is handled by the operator, see #object_transfer_mode_to_base for details. */
ot->flag = OPTYPE_REGISTER | OPTYPE_DEPENDS_ON_CURSOR;
ot->cursor_pending = WM_CURSOR_EYEDROPPER;
RNA_def_boolean(ot->srna,
"use_flash_on_transfer",
true,
"Flash On Transfer",
"Flash the target object when transferring the mode");
}
/** \} */
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,560 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "object_intern.hh"
#include "DNA_mesh_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "DNA_windowmanager_types.h"
#include "BKE_context.hh"
#include "BKE_customdata.hh"
#include "BKE_main.hh"
#include "BKE_multires.hh"
#include "BKE_paint.hh"
#include "BKE_report.hh"
#include "BLI_path_utils.hh"
#include "BLI_string_utf8.h"
#include "DEG_depsgraph.hh"
#include "ED_object.hh"
#include "ED_sculpt.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_prototypes.hh"
#include "WM_api.hh"
#include "WM_types.hh"
namespace blender::ed::object {
/* ------------------------------------------------------------------- */
/** \name Multires Delete Higher Levels Operator
* \{ */
static bool multires_poll(bContext *C)
{
return edit_modifier_poll_generic(C, RNA_MultiresModifier, (1 << OB_MESH), true, false);
}
static wmOperatorStatus multires_higher_levels_delete_exec(bContext *C, wmOperator *op)
{
Scene *scene = CTX_data_scene(C);
Object *ob = context_active_object(C);
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, ob, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
multiresModifier_del_levels(mmd, scene, ob, 1);
iter_other(CTX_data_main(C), ob, true, multires_update_totlevels, &mmd->totlvl);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_higher_levels_delete_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_higher_levels_delete_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_higher_levels_delete(wmOperatorType *ot)
{
ot->name = "Delete Higher Levels";
ot->description = "Deletes the higher resolution mesh, potential loss of detail";
ot->idname = "OBJECT_OT_multires_higher_levels_delete";
ot->poll = multires_poll;
ot->invoke = multires_higher_levels_delete_invoke;
ot->exec = multires_higher_levels_delete_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Subdivide Operator
* \{ */
static EnumPropertyItem prop_multires_subdivide_mode_type[] = {
{int8_t(MultiresSubdivideModeType::CatmullClark),
"CATMULL_CLARK",
0,
"Catmull-Clark",
"Create a new level using Catmull-Clark subdivisions"},
{int8_t(MultiresSubdivideModeType::Simple),
"SIMPLE",
0,
"Simple",
"Create a new level using simple subdivisions"},
{int8_t(MultiresSubdivideModeType::Linear),
"LINEAR",
0,
"Linear",
"Create a new level using linear interpolation of the sculpted displacement"},
{0, nullptr, 0, nullptr, nullptr},
};
static wmOperatorStatus multires_subdivide_exec(bContext *C, wmOperator *op)
{
Object *object = context_active_object(C);
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, object, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
const MultiresSubdivideModeType subdivide_mode = MultiresSubdivideModeType(
RNA_enum_get(op->ptr, "mode"));
multiresModifier_subdivide(object, mmd, subdivide_mode);
iter_other(CTX_data_main(C), object, true, multires_update_totlevels, &mmd->totlvl);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
if (object->mode & OB_MODE_SCULPT) {
/* ensure that grid paint mask layer is created */
BKE_sculpt_mask_layers_ensure(
CTX_data_ensure_evaluated_depsgraph(C), CTX_data_main(C), object, mmd);
}
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_subdivide_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_subdivide_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_subdivide(wmOperatorType *ot)
{
ot->name = "Multires Subdivide";
ot->description = "Add a new level of subdivision";
ot->idname = "OBJECT_OT_multires_subdivide";
ot->poll = multires_poll;
ot->invoke = multires_subdivide_invoke;
ot->exec = multires_subdivide_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
RNA_def_enum(ot->srna,
"mode",
prop_multires_subdivide_mode_type,
int8_t(MultiresSubdivideModeType::CatmullClark),
"Subdivision Mode",
"How the mesh is going to be subdivided to create a new level");
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Reshape Operator
* \{ */
static wmOperatorStatus multires_reshape_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *ob = context_active_object(C), *secondob = nullptr;
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, ob, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
if (mmd->lvl == 0) {
BKE_report(op->reports, RPT_ERROR, "Reshape can work only with higher levels of subdivisions");
return OPERATOR_CANCELLED;
}
CTX_DATA_BEGIN (C, Object *, selob, selected_editable_objects) {
if (selob->type == OB_MESH && selob != ob) {
secondob = selob;
break;
}
}
CTX_DATA_END;
if (!secondob) {
BKE_report(op->reports, RPT_ERROR, "Second selected mesh object required to copy shape from");
return OPERATOR_CANCELLED;
}
if (!multiresModifier_reshapeFromObject(depsgraph, mmd, ob, secondob)) {
BKE_report(op->reports, RPT_ERROR, "Objects do not have the same number of vertices");
return OPERATOR_CANCELLED;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_reshape_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_reshape_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_reshape(wmOperatorType *ot)
{
ot->name = "Multires Reshape";
ot->description = "Copy vertex coordinates from other object";
ot->idname = "OBJECT_OT_multires_reshape";
ot->poll = multires_poll;
ot->invoke = multires_reshape_invoke;
ot->exec = multires_reshape_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Save External Operator
* \{ */
static wmOperatorStatus multires_external_save_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = context_active_object(C);
Mesh *mesh = (ob) ? id_cast<Mesh *>(ob->data) : static_cast<Mesh *>(op->customdata);
char filepath[FILE_MAX];
const bool relative = RNA_boolean_get(op->ptr, "relative_path");
if (!mesh) {
return OPERATOR_CANCELLED;
}
if (CustomData_external_test(&mesh->corner_data, CD_MDISPS)) {
return OPERATOR_CANCELLED;
}
RNA_string_get(op->ptr, "filepath", filepath);
if (relative) {
BLI_path_rel(filepath, BKE_main_blendfile_path(bmain));
}
CustomData_external_add(&mesh->corner_data, &mesh->id, CD_MDISPS, mesh->corners_num, filepath);
CustomData_external_write(
&mesh->corner_data, &mesh->id, CD_MASK_MESH.lmask, mesh->corners_num, 0);
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_external_save_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
Object *ob = context_active_object(C);
Mesh *mesh = id_cast<Mesh *>(ob->data);
char filepath[FILE_MAX];
if (!edit_modifier_invoke_properties(C, op)) {
return OPERATOR_CANCELLED;
}
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, ob, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
if (CustomData_external_test(&mesh->corner_data, CD_MDISPS)) {
return OPERATOR_CANCELLED;
}
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return multires_external_save_exec(C, op);
}
op->customdata = mesh;
/* While a filename need not be UTF8, at this point the constructed name should be UTF8. */
SNPRINTF_UTF8(filepath, "//%s.btx", mesh->id.name + 2);
RNA_string_set(op->ptr, "filepath", filepath);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
void OBJECT_OT_multires_external_save(wmOperatorType *ot)
{
ot->name = "Multires Save External";
ot->description = "Save displacements to an external file";
ot->idname = "OBJECT_OT_multires_external_save";
/* XXX modifier no longer in context after file browser: `ot->poll = multires_poll;`. */
ot->exec = multires_external_save_exec;
ot->invoke = multires_external_save_invoke;
ot->poll = multires_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_BTX,
FILE_SPECIAL,
FILE_SAVE,
WM_FILESEL_FILEPATH | WM_FILESEL_RELPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Pack Operator
* \{ */
static wmOperatorStatus multires_external_pack_exec(bContext *C, wmOperator * /*op*/)
{
Object *ob = context_active_object(C);
Mesh *mesh = id_cast<Mesh *>(ob->data);
if (!CustomData_external_test(&mesh->corner_data, CD_MDISPS)) {
return OPERATOR_CANCELLED;
}
/* XXX don't remove. */
CustomData_external_remove(&mesh->corner_data, &mesh->id, CD_MDISPS, mesh->corners_num);
return OPERATOR_FINISHED;
}
void OBJECT_OT_multires_external_pack(wmOperatorType *ot)
{
ot->name = "Multires Pack External";
ot->description = "Pack displacements from an external file";
ot->idname = "OBJECT_OT_multires_external_pack";
ot->poll = multires_poll;
ot->exec = multires_external_pack_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Apply Base
* \{ */
static wmOperatorStatus multires_base_apply_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *object = context_active_object(C);
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, object, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
const ApplyBaseMode mode = RNA_boolean_get(op->ptr, "apply_heuristic") ?
ApplyBaseMode::ForSubdivision :
ApplyBaseMode::Base;
ed::sculpt_paint::undo::push_multires_mesh_begin(C, op->type->name);
multiresModifier_base_apply(depsgraph, object, mmd, mode);
ed::sculpt_paint::undo::push_multires_mesh_end(C, op->type->name);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_base_apply_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_base_apply_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_base_apply(wmOperatorType *ot)
{
ot->name = "Multires Apply Base";
ot->description = "Modify the base mesh to conform to the displaced mesh";
ot->idname = "OBJECT_OT_multires_base_apply";
ot->poll = multires_poll;
ot->invoke = multires_base_apply_invoke;
ot->exec = multires_base_apply_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
PropertyRNA *prop = RNA_def_boolean(
ot->srna,
"apply_heuristic",
true,
"Apply Subdivision Heuristic",
"Whether or not the final base mesh positions will be slightly altered to account for a new "
"subdivision modifier being added");
RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Unsubdivide
* \{ */
static wmOperatorStatus multires_unsubdivide_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *object = context_active_object(C);
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, object, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
MultiresUnsubdivideInfo info = {};
int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, 1, true, info);
if (new_levels == 0) {
BKE_report(op->reports, RPT_ERROR, "No valid subdivisions found to rebuild a lower level");
return OPERATOR_CANCELLED;
}
multiresModifier_unsubdivide_report_if_needed(info, op->reports);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_unsubdivide_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_unsubdivide_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_unsubdivide(wmOperatorType *ot)
{
ot->name = "Unsubdivide";
ot->description = "Rebuild a lower subdivision level of the current base mesh";
ot->idname = "OBJECT_OT_multires_unsubdivide";
ot->poll = multires_poll;
ot->invoke = multires_unsubdivide_invoke;
ot->exec = multires_unsubdivide_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
/* ------------------------------------------------------------------- */
/** \name Multires Rebuild Subdivisions
* \{ */
static wmOperatorStatus multires_rebuild_subdiv_exec(bContext *C, wmOperator *op)
{
Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
Object *object = context_active_object(C);
MultiresModifierData *mmd = reinterpret_cast<MultiresModifierData *>(
edit_modifier_property_get(op, object, eModifierType_Multires));
if (!mmd) {
return OPERATOR_CANCELLED;
}
MultiresUnsubdivideInfo info = {};
int new_levels = multiresModifier_rebuild_subdiv(depsgraph, object, mmd, INT_MAX, false, info);
if (new_levels == 0) {
BKE_report(op->reports, RPT_ERROR, "No valid subdivisions found to rebuild lower levels");
return OPERATOR_CANCELLED;
}
multiresModifier_unsubdivide_report_if_needed(info, op->reports);
BKE_reportf(op->reports, RPT_INFO, "%d new levels rebuilt", new_levels);
DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object);
return OPERATOR_FINISHED;
}
static wmOperatorStatus multires_rebuild_subdiv_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (edit_modifier_invoke_properties(C, op)) {
return multires_rebuild_subdiv_exec(C, op);
}
return OPERATOR_CANCELLED;
}
void OBJECT_OT_multires_rebuild_subdiv(wmOperatorType *ot)
{
ot->name = "Rebuild Lower Subdivisions";
ot->description =
"Rebuilds all possible subdivisions levels to generate a lower resolution base mesh";
ot->idname = "OBJECT_OT_multires_rebuild_subdiv";
ot->poll = multires_poll;
ot->invoke = multires_rebuild_subdiv_invoke;
ot->exec = multires_rebuild_subdiv_exec;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_modifier_properties(ot);
}
/** \} */
} // namespace blender::ed::object

View File

@@ -0,0 +1,351 @@
/* SPDX-FileCopyrightText: 2008 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include <cstdlib>
#include "DNA_object_types.h"
#include "DNA_space_types.h"
#include "BKE_context.hh"
#include "RNA_access.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "object_intern.hh"
/* ************************** registration **********************************/
namespace blender::ed::object {
void operatortypes_object()
{
WM_operatortype_append(OBJECT_OT_location_clear);
WM_operatortype_append(OBJECT_OT_rotation_clear);
WM_operatortype_append(OBJECT_OT_scale_clear);
WM_operatortype_append(OBJECT_OT_origin_clear);
WM_operatortype_append(OBJECT_OT_visual_transform_apply);
WM_operatortype_append(OBJECT_OT_transform_apply);
WM_operatortype_append(OBJECT_OT_parent_inverse_apply);
WM_operatortype_append(OBJECT_OT_transform_axis_target);
WM_operatortype_append(OBJECT_OT_origin_set);
WM_operatortype_append(OBJECT_OT_mode_set);
WM_operatortype_append(OBJECT_OT_mode_set_with_submode);
WM_operatortype_append(OBJECT_OT_editmode_toggle);
WM_operatortype_append(OBJECT_OT_posemode_toggle);
WM_operatortype_append(OBJECT_OT_shade_smooth);
WM_operatortype_append(OBJECT_OT_shade_smooth_by_angle);
WM_operatortype_append(OBJECT_OT_shade_auto_smooth);
WM_operatortype_append(OBJECT_OT_shade_flat);
WM_operatortype_append(OBJECT_OT_paths_calculate);
WM_operatortype_append(OBJECT_OT_paths_update);
WM_operatortype_append(OBJECT_OT_paths_clear);
WM_operatortype_append(OBJECT_OT_paths_update_visible);
WM_operatortype_append(OBJECT_OT_forcefield_toggle);
WM_operatortype_append(OBJECT_OT_transfer_mode);
WM_operatortype_append(OBJECT_OT_parent_set);
WM_operatortype_append(OBJECT_OT_parent_no_inverse_set);
WM_operatortype_append(OBJECT_OT_parent_clear);
WM_operatortype_append(OBJECT_OT_vertex_parent_set);
WM_operatortype_append(OBJECT_OT_track_set);
WM_operatortype_append(OBJECT_OT_track_clear);
WM_operatortype_append(OBJECT_OT_make_local);
WM_operatortype_append(OBJECT_OT_make_single_user);
WM_operatortype_append(OBJECT_OT_make_links_scene);
WM_operatortype_append(OBJECT_OT_make_links_data);
WM_operatortype_append(OBJECT_OT_make_override_library);
WM_operatortype_append(OBJECT_OT_reset_override_library);
WM_operatortype_append(OBJECT_OT_clear_override_library);
WM_operatortype_append(OBJECT_OT_select_random);
WM_operatortype_append(OBJECT_OT_select_all);
WM_operatortype_append(OBJECT_OT_select_same_collection);
WM_operatortype_append(OBJECT_OT_select_by_type);
WM_operatortype_append(OBJECT_OT_select_linked);
WM_operatortype_append(OBJECT_OT_select_grouped);
WM_operatortype_append(OBJECT_OT_select_mirror);
WM_operatortype_append(OBJECT_OT_select_more);
WM_operatortype_append(OBJECT_OT_select_less);
WM_operatortype_append(COLLECTION_OT_create);
WM_operatortype_append(COLLECTION_OT_objects_remove_all);
WM_operatortype_append(COLLECTION_OT_objects_remove);
WM_operatortype_append(COLLECTION_OT_objects_add_active);
WM_operatortype_append(COLLECTION_OT_objects_remove_active);
WM_operatortype_append(OBJECT_OT_delete);
WM_operatortype_append(OBJECT_OT_text_add);
WM_operatortype_append(OBJECT_OT_armature_add);
WM_operatortype_append(OBJECT_OT_empty_add);
WM_operatortype_append(OBJECT_OT_lightprobe_add);
WM_operatortype_append(OBJECT_OT_empty_image_add);
WM_operatortype_append(OBJECT_OT_grease_pencil_add);
WM_operatortype_append(OBJECT_OT_light_add);
WM_operatortype_append(OBJECT_OT_camera_add);
WM_operatortype_append(OBJECT_OT_speaker_add);
WM_operatortype_append(OBJECT_OT_curves_random_add);
WM_operatortype_append(OBJECT_OT_curves_empty_hair_add);
WM_operatortype_append(OBJECT_OT_pointcloud_random_add);
WM_operatortype_append(OBJECT_OT_volume_add);
WM_operatortype_append(OBJECT_OT_volume_import);
WM_operatortype_append(OBJECT_OT_add);
WM_operatortype_append(OBJECT_OT_lattice_add_to_selected);
WM_operatortype_append(OBJECT_OT_add_named);
WM_operatortype_append(OBJECT_OT_transform_to_mouse);
WM_operatortype_append(OBJECT_OT_effector_add);
WM_operatortype_append(OBJECT_OT_collection_instance_add);
WM_operatortype_append(OBJECT_OT_collection_external_asset_drop);
WM_operatortype_append(OBJECT_OT_data_instance_add);
WM_operatortype_append(OBJECT_OT_metaball_add);
WM_operatortype_append(OBJECT_OT_duplicates_make_real);
WM_operatortype_append(OBJECT_OT_duplicate);
WM_operatortype_append(OBJECT_OT_join);
WM_operatortype_append(OBJECT_OT_join_shapes);
WM_operatortype_append(OBJECT_OT_update_shapes);
WM_operatortype_append(OBJECT_OT_convert);
WM_operatortype_append(OBJECT_OT_visual_geometry_to_objects);
WM_operatortype_append(OBJECT_OT_modifier_add);
WM_operatortype_append(OBJECT_OT_modifier_remove);
WM_operatortype_append(OBJECT_OT_modifiers_clear);
WM_operatortype_append(OBJECT_OT_modifier_move_up);
WM_operatortype_append(OBJECT_OT_modifier_move_down);
WM_operatortype_append(OBJECT_OT_modifier_move_to_index);
WM_operatortype_append(OBJECT_OT_modifier_apply);
WM_operatortype_append(OBJECT_OT_modifier_apply_as_shapekey);
WM_operatortype_append(OBJECT_OT_modifier_convert);
WM_operatortype_append(OBJECT_OT_modifier_copy);
WM_operatortype_append(OBJECT_OT_modifier_copy_to_selected);
WM_operatortype_append(OBJECT_OT_modifiers_copy_to_selected);
WM_operatortype_append(OBJECT_OT_modifier_set_active);
WM_operatortype_append(OBJECT_OT_multires_subdivide);
WM_operatortype_append(OBJECT_OT_multires_reshape);
WM_operatortype_append(OBJECT_OT_multires_higher_levels_delete);
WM_operatortype_append(OBJECT_OT_multires_base_apply);
WM_operatortype_append(OBJECT_OT_multires_unsubdivide);
WM_operatortype_append(OBJECT_OT_multires_rebuild_subdiv);
WM_operatortype_append(OBJECT_OT_multires_external_save);
WM_operatortype_append(OBJECT_OT_multires_external_pack);
WM_operatortype_append(OBJECT_OT_skin_root_mark);
WM_operatortype_append(OBJECT_OT_skin_loose_mark_clear);
WM_operatortype_append(OBJECT_OT_skin_radii_equalize);
WM_operatortype_append(OBJECT_OT_skin_armature_create);
WM_operatortype_append(OBJECT_OT_geometry_nodes_input_attribute_toggle);
WM_operatortype_append(OBJECT_OT_geometry_node_tree_copy_assign);
WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_add);
WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_remove);
WM_operatortype_append(OBJECT_OT_grease_pencil_dash_modifier_segment_move);
WM_operatortype_append(OBJECT_OT_grease_pencil_time_modifier_segment_add);
WM_operatortype_append(OBJECT_OT_grease_pencil_time_modifier_segment_remove);
WM_operatortype_append(OBJECT_OT_grease_pencil_time_modifier_segment_move);
/* Shader FX. */
WM_operatortype_append(OBJECT_OT_shaderfx_add);
WM_operatortype_append(OBJECT_OT_shaderfx_remove);
WM_operatortype_append(OBJECT_OT_shaderfx_move_up);
WM_operatortype_append(OBJECT_OT_shaderfx_move_down);
WM_operatortype_append(OBJECT_OT_shaderfx_move_to_index);
WM_operatortype_append(OBJECT_OT_shaderfx_copy);
WM_operatortype_append(OBJECT_OT_correctivesmooth_bind);
WM_operatortype_append(OBJECT_OT_meshdeform_bind);
WM_operatortype_append(OBJECT_OT_explode_refresh);
WM_operatortype_append(OBJECT_OT_ocean_bake);
WM_operatortype_append(OBJECT_OT_constraint_add);
WM_operatortype_append(OBJECT_OT_constraint_add_with_targets);
WM_operatortype_append(POSE_OT_constraint_add);
WM_operatortype_append(POSE_OT_constraint_add_with_targets);
WM_operatortype_append(OBJECT_OT_constraints_copy);
WM_operatortype_append(POSE_OT_constraints_copy);
WM_operatortype_append(OBJECT_OT_constraints_clear);
WM_operatortype_append(POSE_OT_constraints_clear);
WM_operatortype_append(POSE_OT_ik_add);
WM_operatortype_append(POSE_OT_ik_clear);
WM_operatortype_append(CONSTRAINT_OT_delete);
WM_operatortype_append(CONSTRAINT_OT_apply);
WM_operatortype_append(CONSTRAINT_OT_copy);
WM_operatortype_append(CONSTRAINT_OT_copy_to_selected);
WM_operatortype_append(CONSTRAINT_OT_move_up);
WM_operatortype_append(CONSTRAINT_OT_move_down);
WM_operatortype_append(CONSTRAINT_OT_move_to_index);
WM_operatortype_append(CONSTRAINT_OT_stretchto_reset);
WM_operatortype_append(CONSTRAINT_OT_limitdistance_reset);
WM_operatortype_append(CONSTRAINT_OT_childof_set_inverse);
WM_operatortype_append(CONSTRAINT_OT_childof_clear_inverse);
WM_operatortype_append(CONSTRAINT_OT_objectsolver_set_inverse);
WM_operatortype_append(CONSTRAINT_OT_objectsolver_clear_inverse);
WM_operatortype_append(CONSTRAINT_OT_followpath_path_animate);
WM_operatortype_append(OBJECT_OT_vertex_group_add);
WM_operatortype_append(OBJECT_OT_vertex_group_remove);
WM_operatortype_append(OBJECT_OT_vertex_group_assign);
WM_operatortype_append(OBJECT_OT_vertex_group_assign_new);
WM_operatortype_append(OBJECT_OT_vertex_group_remove_from);
WM_operatortype_append(OBJECT_OT_vertex_group_select);
WM_operatortype_append(OBJECT_OT_vertex_group_deselect);
WM_operatortype_append(OBJECT_OT_vertex_group_copy_to_selected);
WM_operatortype_append(OBJECT_OT_vertex_group_copy);
WM_operatortype_append(OBJECT_OT_vertex_group_normalize);
WM_operatortype_append(OBJECT_OT_vertex_group_normalize_all);
WM_operatortype_append(OBJECT_OT_vertex_group_lock);
WM_operatortype_append(OBJECT_OT_vertex_group_invert);
WM_operatortype_append(OBJECT_OT_vertex_group_levels);
WM_operatortype_append(OBJECT_OT_vertex_group_smooth);
WM_operatortype_append(OBJECT_OT_vertex_group_clean);
WM_operatortype_append(OBJECT_OT_vertex_group_quantize);
WM_operatortype_append(OBJECT_OT_vertex_group_limit_total);
WM_operatortype_append(OBJECT_OT_vertex_group_mirror);
WM_operatortype_append(OBJECT_OT_vertex_group_set_active);
WM_operatortype_append(OBJECT_OT_vertex_group_sort);
WM_operatortype_append(OBJECT_OT_vertex_group_move);
WM_operatortype_append(OBJECT_OT_vertex_weight_paste);
WM_operatortype_append(OBJECT_OT_vertex_weight_delete);
WM_operatortype_append(OBJECT_OT_vertex_weight_set_active);
WM_operatortype_append(OBJECT_OT_vertex_weight_normalize_active_vertex);
WM_operatortype_append(OBJECT_OT_vertex_weight_copy);
WM_operatortype_append(TRANSFORM_OT_vertex_warp);
WM_operatortype_append(OBJECT_OT_move_to_collection);
WM_operatortype_append(OBJECT_OT_link_to_collection);
move_to_collection_menu_register();
link_to_collection_menu_register();
WM_operatortype_append(OBJECT_OT_shape_key_add);
WM_operatortype_append(OBJECT_OT_shape_key_copy);
WM_operatortype_append(OBJECT_OT_shape_key_remove);
WM_operatortype_append(OBJECT_OT_shape_key_clear);
WM_operatortype_append(OBJECT_OT_shape_key_retime);
WM_operatortype_append(OBJECT_OT_shape_key_mirror);
WM_operatortype_append(OBJECT_OT_shape_key_move);
WM_operatortype_append(OBJECT_OT_shape_key_lock);
WM_operatortype_append(OBJECT_OT_shape_key_make_basis);
WM_operatortype_append(OBJECT_OT_shape_key_apply_to_basis);
WM_operatortype_append(OBJECT_OT_collection_add);
WM_operatortype_append(OBJECT_OT_collection_link);
WM_operatortype_append(OBJECT_OT_collection_remove);
WM_operatortype_append(OBJECT_OT_collection_unlink);
WM_operatortype_append(OBJECT_OT_collection_objects_select);
WM_operatortype_append(OBJECT_OT_hook_add_selob);
WM_operatortype_append(OBJECT_OT_hook_add_newob);
WM_operatortype_append(OBJECT_OT_hook_remove);
WM_operatortype_append(OBJECT_OT_hook_select);
WM_operatortype_append(OBJECT_OT_hook_assign);
WM_operatortype_append(OBJECT_OT_hook_reset);
WM_operatortype_append(OBJECT_OT_hook_recenter);
WM_operatortype_append(OBJECT_OT_bake_image);
WM_operatortype_append(OBJECT_OT_bake);
WM_operatortype_append(bake_simulation::OBJECT_OT_simulation_nodes_cache_calculate_to_frame);
WM_operatortype_append(bake_simulation::OBJECT_OT_simulation_nodes_cache_bake);
WM_operatortype_append(bake_simulation::OBJECT_OT_simulation_nodes_cache_delete);
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_single);
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_delete_single);
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_pack_single);
WM_operatortype_append(bake_simulation::OBJECT_OT_geometry_node_bake_unpack_single);
WM_operatortype_append(OBJECT_OT_drop_named_material);
WM_operatortype_append(OBJECT_OT_drop_geometry_nodes);
WM_operatortype_append(OBJECT_OT_unlink_data);
WM_operatortype_append(OBJECT_OT_laplaciandeform_bind);
WM_operatortype_append(TRANSFORM_OT_vertex_random);
WM_operatortype_append(OBJECT_OT_data_transfer);
WM_operatortype_append(OBJECT_OT_datalayout_transfer);
WM_operatortype_append(OBJECT_OT_surfacedeform_bind);
WM_operatortype_append(OBJECT_OT_hide_view_clear);
WM_operatortype_append(OBJECT_OT_hide_view_set);
WM_operatortype_append(OBJECT_OT_hide_collection);
WM_operatortype_append(OBJECT_OT_voxel_remesh);
WM_operatortype_append(OBJECT_OT_voxel_size_edit);
WM_operatortype_append(OBJECT_OT_quadriflow_remesh);
/* Light linking. */
WM_operatortype_append(OBJECT_OT_light_linking_receiver_collection_new);
WM_operatortype_append(OBJECT_OT_light_linking_receivers_select);
WM_operatortype_append(OBJECT_OT_light_linking_receivers_link);
WM_operatortype_append(OBJECT_OT_light_linking_blocker_collection_new);
WM_operatortype_append(OBJECT_OT_light_linking_blockers_select);
WM_operatortype_append(OBJECT_OT_light_linking_blockers_link);
WM_operatortype_append(OBJECT_OT_light_linking_unlink_from_collection);
/* Custom cameras. */
WM_operatortype_append(OBJECT_OT_camera_custom_update);
object_modifier_add_asset_register();
collection_importer_register();
collection_exporter_register();
}
void operatormacros_object()
{
wmOperatorType *ot;
wmOperatorTypeMacro *otmacro;
ot = WM_operatortype_append_macro("OBJECT_OT_duplicate_move",
"Duplicate Objects",
"Duplicate the selected objects and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
if (ot) {
WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate");
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
}
ot = WM_operatortype_append_macro(
"OBJECT_OT_duplicate_move_linked",
"Duplicate Linked",
"Duplicate the selected objects, but not their object data, and move them",
OPTYPE_UNDO | OPTYPE_REGISTER);
if (ot) {
otmacro = WM_operatortype_macro_define(ot, "OBJECT_OT_duplicate");
RNA_boolean_set(otmacro->ptr, "linked", true);
otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
}
}
static bool object_mode_poll(bContext *C)
{
Object *ob = CTX_data_active_object(C);
return (!ob || ob->mode == OB_MODE_OBJECT);
}
void keymap_object(wmKeyConfig *keyconf)
{
wmKeyMap *keymap;
/* Objects, Regardless of Mode -------------------------------------------------- */
keymap = WM_keymap_ensure(keyconf, "Object Non-modal", SPACE_EMPTY, RGN_TYPE_WINDOW);
/* Object Mode ---------------------------------------------------------------- */
/* NOTE: this keymap gets disabled in non-object-mode. */
keymap = WM_keymap_ensure(keyconf, "Object Mode", SPACE_EMPTY, RGN_TYPE_WINDOW);
keymap->poll = object_mode_poll;
/* Modal keymaps. */
object_transform_axis_target_modal_keymap(keyconf);
}
} // namespace blender::ed::object

View File

@@ -0,0 +1,179 @@
/* SPDX-FileCopyrightText: 2014 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
#include "BLI_ghash.h"
#include "BLI_math_vector.h"
#include "BLI_rand.hh"
#include "BKE_context.hh"
#include "BKE_layer.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "DEG_depsgraph_query.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "ED_transverts.hh"
#include "object_intern.hh"
namespace blender::ed::object {
/**
* Generic randomize vertices function
*/
static bool object_rand_transverts(TransVertStore *tvs,
const float offset,
const float uniform,
const float normal_factor,
const uint seed)
{
bool use_normal = (normal_factor != 0.0f);
TransVert *tv;
int a;
if (!tvs || !(tvs->transverts)) {
return false;
}
RandomNumberGenerator rng(seed);
tv = tvs->transverts;
for (a = 0; a < tvs->transverts_tot; a++, tv++) {
const float t = max_ff(0.0f, uniform + ((1.0f - uniform) * rng.get_float()));
float3 vec = rng.get_unit_float3();
if (use_normal && (tv->flag & TX_VERT_USE_NORMAL)) {
float no[3];
/* avoid >90d rotation to align with normal */
if (dot_v3v3(vec, tv->normal) < 0.0f) {
negate_v3_v3(no, tv->normal);
}
else {
copy_v3_v3(no, tv->normal);
}
interp_v3_v3v3_slerp_safe(vec, vec, no, normal_factor);
}
madd_v3_v3fl(tv->loc, vec, offset * t);
}
return true;
}
static wmOperatorStatus object_rand_verts_exec(bContext *C, wmOperator *op)
{
const Main *bmain = CTX_data_main(C);
const Scene *scene = CTX_data_scene(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob_active = CTX_data_edit_object(C);
const int ob_mode = ob_active->mode;
const float offset = RNA_float_get(op->ptr, "offset");
const float uniform = RNA_float_get(op->ptr, "uniform");
const float normal_factor = RNA_float_get(op->ptr, "normal");
const uint seed = RNA_int_get(op->ptr, "seed");
bool changed_multi = false;
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Vector<Object *> objects = BKE_view_layer_array_from_objects_in_mode_unique_data(
*bmain, scene, view_layer, CTX_wm_view3d(C), eObjectMode(ob_mode));
for (const int ob_index : objects.index_range()) {
Object *ob_iter = objects[ob_index];
TransVertStore tvs = {nullptr};
if (ob_iter) {
int mode = TM_ALL_JOINTS;
if (normal_factor != 0.0f) {
mode |= TX_VERT_USE_NORMAL;
}
if (shape_key_report_if_locked(ob_iter, op->reports)) {
continue;
}
const Object *ob_iter_eval = DEG_get_evaluated(depsgraph, ob_iter);
ED_transverts_create_from_obedit(&tvs, ob_iter_eval, mode);
if (tvs.transverts_tot == 0) {
continue;
}
int seed_iter = seed;
/* This gives a consistent result regardless of object order. */
if (ob_index) {
seed_iter += BLI_ghashutil_strhash_p(ob_iter->id.name);
}
object_rand_transverts(&tvs, offset, uniform, normal_factor, seed_iter);
ED_transverts_update_obedit(&tvs, ob_iter);
ED_transverts_free(&tvs);
WM_event_add_notifier(C, NC_OBJECT | ND_TRANSFORM, ob_iter);
changed_multi = true;
}
}
return changed_multi ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void TRANSFORM_OT_vertex_random(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Randomize";
ot->description = "Randomize vertices";
ot->idname = "TRANSFORM_OT_vertex_random";
/* API callbacks. */
ot->exec = object_rand_verts_exec;
ot->poll = ED_transverts_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
ot->prop = RNA_def_float_distance(
ot->srna, "offset", 0.0f, -FLT_MAX, FLT_MAX, "Amount", "Distance to offset", -10.0f, 10.0f);
RNA_def_float_factor(ot->srna,
"uniform",
0.0f,
0.0f,
1.0f,
"Uniform",
"Increase for uniform offset distance",
0.0f,
1.0f);
RNA_def_float_factor(ot->srna,
"normal",
0.0f,
0.0f,
1.0f,
"Normal",
"Align offset direction to normals",
0.0f,
1.0f);
RNA_def_int(
ot->srna, "seed", 0, 0, 10000, "Random Seed", "Seed for the random number generator", 0, 50);
/* Set generic modal callbacks. */
WM_operator_type_modal_from_exec_for_object_edit_coords(ot);
}
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,727 @@
/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include <cstdlib>
#include <cstring>
#include "DNA_grease_pencil_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_shader_fx_types.h"
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_string_utf8.h"
#include "BLI_utildefines.h"
#include "BLT_translation.hh"
#include "BKE_context.hh"
#include "BKE_lib_id.hh"
#include "BKE_object.hh"
#include "BKE_report.hh"
#include "BKE_shader_fx.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "RNA_enum_types.hh"
#include "RNA_prototypes.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "UI_interface.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "object_intern.hh"
namespace blender::ed::object {
/* -------------------------------------------------------------------- */
/** \name Public API
* \{ */
ShaderFxData *shaderfx_add(
ReportList *reports, Main *bmain, Scene * /*scene*/, Object *ob, const char *name, int type)
{
ShaderFxData *new_fx = nullptr;
const ShaderFxTypeInfo *fxi = BKE_shaderfx_get_info(ShaderFxType(type));
if (!ELEM(ob->type, OB_GREASE_PENCIL)) {
BKE_reportf(reports, RPT_WARNING, "Effect cannot be added to object '%s'", ob->id.name + 2);
return nullptr;
}
if (fxi->flags & eShaderFxTypeFlag_Single) {
if (BKE_shaderfx_findby_type(ob, ShaderFxType(type))) {
BKE_report(reports, RPT_WARNING, "Only one Effect of this type is allowed");
return nullptr;
}
}
/* get new effect data to add */
new_fx = BKE_shaderfx_new(ShaderFxType(type));
BLI_addtail(&ob->shader_fx, new_fx);
if (name) {
STRNCPY_UTF8(new_fx->name, name);
}
/* make sure effect data has unique name */
BKE_shaderfx_unique_name(&ob->shader_fx, new_fx);
BLI_assert(ob->type == OB_GREASE_PENCIL);
GreasePencil *grease_pencil = id_cast<GreasePencil *>(ob->data);
DEG_id_tag_update(&grease_pencil->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
return new_fx;
}
/* Return true if the object has a effect of type 'type' other than
* the shaderfx pointed to be 'exclude', otherwise returns false. */
static bool UNUSED_FUNCTION(object_has_shaderfx)(const Object *ob,
const ShaderFxData *exclude,
ShaderFxType type)
{
for (ShaderFxData &fx : ob->shader_fx) {
if ((&fx != exclude) && (fx.type == type)) {
return true;
}
}
return false;
}
static bool object_shaderfx_remove(Main *bmain,
Object *ob,
ShaderFxData *fx,
bool * /*r_sort_depsgraph*/)
{
/* It seems on rapid delete it is possible to
* get called twice on same effect, so make
* sure it is in list. */
if (BLI_findindex(&ob->shader_fx, fx) == -1) {
return false;
}
DEG_relations_tag_update(bmain);
BLI_remlink(&ob->shader_fx, fx);
BKE_shaderfx_free(fx);
BKE_object_free_derived_caches(ob);
return true;
}
bool shaderfx_remove(ReportList *reports, Main *bmain, Object *ob, ShaderFxData *fx)
{
bool sort_depsgraph = false;
bool ok;
ok = object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph);
if (!ok) {
BKE_reportf(reports, RPT_ERROR, "Effect '%s' not in object '%s'", fx->name, ob->id.name);
return false;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
return true;
}
void shaderfx_clear(Main *bmain, Object *ob)
{
ShaderFxData *fx = static_cast<ShaderFxData *>(ob->shader_fx.first);
bool sort_depsgraph = false;
if (!fx) {
return;
}
while (fx) {
ShaderFxData *next_fx;
next_fx = fx->next;
object_shaderfx_remove(bmain, ob, fx, &sort_depsgraph);
fx = next_fx;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
DEG_relations_tag_update(bmain);
}
int shaderfx_move_up(ReportList * /*reports*/, Object *ob, ShaderFxData *fx)
{
if (fx->prev) {
BLI_remlink(&ob->shader_fx, fx);
BLI_insertlinkbefore(&ob->shader_fx, fx->prev, fx);
}
return 1;
}
int shaderfx_move_down(ReportList * /*reports*/, Object *ob, ShaderFxData *fx)
{
if (fx->next) {
BLI_remlink(&ob->shader_fx, fx);
BLI_insertlinkafter(&ob->shader_fx, fx->next, fx);
}
return 1;
}
bool shaderfx_move_to_index(ReportList *reports, Object *ob, ShaderFxData *fx, const int index)
{
BLI_assert(fx != nullptr);
BLI_assert(index >= 0);
if (index >= ob->shader_fx.count()) {
BKE_report(reports, RPT_WARNING, "Cannot move effect beyond the end of the stack");
return false;
}
int fx_index = BLI_findindex(&ob->shader_fx, fx);
BLI_assert(fx_index != -1);
if (fx_index < index) {
/* Move shaderfx down in list. */
for (; fx_index < index; fx_index++) {
if (!shaderfx_move_down(reports, ob, fx)) {
break;
}
}
}
else {
/* Move shaderfx up in list. */
for (; fx_index > index; fx_index--) {
if (!shaderfx_move_up(reports, ob, fx)) {
break;
}
}
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ob);
return true;
}
void shaderfx_link(Object *dst, Object *src)
{
dst->shader_fx.free_no_destruct();
BKE_shaderfx_copy(&dst->shader_fx, &src->shader_fx);
DEG_id_tag_update(&dst->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, dst);
}
void shaderfx_copy(Object *dst, ShaderFxData *fx)
{
ShaderFxData *nfx = BKE_shaderfx_new(fx->type);
STRNCPY_UTF8(nfx->name, fx->name);
BKE_shaderfx_copydata(fx, nfx);
BLI_addtail(&dst->shader_fx, nfx);
DEG_id_tag_update(&dst->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, dst);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic Poll Callback Helpers
* \{ */
static bool edit_shaderfx_poll_generic(bContext *C,
StructRNA *rna_type,
int obtype_flag,
const bool is_liboverride_allowed)
{
PointerRNA ptr = CTX_data_pointer_get_type(C, "shaderfx", rna_type);
Object *ob = (ptr.owner_id) ? id_cast<Object *>(ptr.owner_id) : context_active_object(C);
ShaderFxData *fx = static_cast<ShaderFxData *>(ptr.data); /* May be nullptr. */
if (!ED_operator_object_active_editable_ex(C, ob)) {
return false;
}
/* NOTE: Temporary 'forbid all' for overrides, until we implement support to add shaderfx to
* overrides. */
if (ID_IS_OVERRIDE_LIBRARY(ob)) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit shaderfxs in a library override");
return false;
}
if (obtype_flag != 0 && ((1 << ob->type) & obtype_flag) == 0) {
CTX_wm_operator_poll_msg_set(C, "Object type is not supported");
return false;
}
if (ptr.owner_id != nullptr && !BKE_id_is_editable(CTX_data_main(C), ptr.owner_id)) {
CTX_wm_operator_poll_msg_set(C, "Cannot edit library or override data");
return false;
}
if (!is_liboverride_allowed && BKE_shaderfx_is_nonlocal_in_liboverride(ob, fx)) {
CTX_wm_operator_poll_msg_set(
C, "Cannot edit shaderfxs coming from linked data in a library override");
return false;
}
return true;
}
static bool edit_shaderfx_poll(bContext *C)
{
return edit_shaderfx_poll_generic(C, RNA_ShaderFx, 0, false);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Add Effect Operator
* \{ */
static wmOperatorStatus shaderfx_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 (!shaderfx_add(op->reports, bmain, scene, ob, nullptr, type)) {
return OPERATOR_CANCELLED;
}
WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
static const EnumPropertyItem *shaderfx_add_itemf(bContext *C,
PointerRNA * /*ptr*/,
PropertyRNA * /*prop*/,
bool *r_free)
{
Object *ob = (C) ? context_active_object(C) : nullptr;
EnumPropertyItem *item = nullptr;
const EnumPropertyItem *fx_item, *group_item = nullptr;
const ShaderFxTypeInfo *mti;
int totitem = 0, a;
if (!ob) {
return rna_enum_object_shaderfx_type_items;
}
for (a = 0; rna_enum_object_shaderfx_type_items[a].identifier; a++) {
fx_item = &rna_enum_object_shaderfx_type_items[a];
if (fx_item->identifier[0]) {
mti = BKE_shaderfx_get_info(ShaderFxType(fx_item->value));
if (mti->flags & eShaderFxTypeFlag_NoUserAdd) {
continue;
}
}
else {
group_item = fx_item;
fx_item = nullptr;
continue;
}
if (group_item) {
RNA_enum_item_add(&item, &totitem, group_item);
group_item = nullptr;
}
RNA_enum_item_add(&item, &totitem, fx_item);
}
RNA_enum_item_end(&item, &totitem);
*r_free = true;
return item;
}
void OBJECT_OT_shaderfx_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Effect";
ot->description = "Add a visual effect to the active object";
ot->idname = "OBJECT_OT_shaderfx_add";
/* API callbacks. */
ot->invoke = WM_menu_invoke;
ot->exec = shaderfx_add_exec;
ot->poll = edit_shaderfx_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
ot->prop = RNA_def_enum(
ot->srna, "type", rna_enum_object_shaderfx_type_items, eShaderFxType_Blur, "Type", "");
RNA_def_enum_funcs(ot->prop, shaderfx_add_itemf);
/* Abused, for "Light"... */
RNA_def_property_translation_context(ot->prop, BLT_I18NCONTEXT_ID_ID);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Generic Functions for Operators Using Names and Data Context
* \{ */
static void edit_shaderfx_properties(wmOperatorType *ot)
{
PropertyRNA *prop = RNA_def_string(
ot->srna, "shaderfx", nullptr, MAX_NAME, "Shader", "Name of the shaderfx to edit");
RNA_def_property_flag(prop, PROP_HIDDEN);
}
static void edit_shaderfx_report_property(wmOperatorType *ot)
{
PropertyRNA *prop = RNA_def_boolean(
ot->srna, "report", false, "Report", "Create a notification after the operation");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/**
* \param event: If this isn't nullptr, the operator will also look for panels underneath
* the cursor with custom-data set to a modifier.
* \param r_retval: This should be used if #event is used in order to return
* #OPERATOR_PASS_THROUGH to check other operators with the same key set.
*/
static bool edit_shaderfx_invoke_properties(bContext *C,
wmOperator *op,
const wmEvent *event,
wmOperatorStatus *r_retval)
{
if (RNA_struct_property_is_set(op->ptr, "shaderfx")) {
return true;
}
PointerRNA ctx_ptr = CTX_data_pointer_get_type(C, "shaderfx", RNA_ShaderFx);
if (ctx_ptr.data != nullptr) {
ShaderFxData *fx = static_cast<ShaderFxData *>(ctx_ptr.data);
RNA_string_set(op->ptr, "shaderfx", fx->name);
return true;
}
if (event == nullptr) {
if (r_retval != nullptr) {
*r_retval = OPERATOR_CANCELLED;
}
return false;
}
/* Check the custom data of panels under the mouse for an effect. */
PointerRNA *panel_ptr = ui::region_panel_custom_data_under_cursor(C, event);
if (panel_ptr == nullptr || RNA_pointer_is_null(panel_ptr)) {
/* The operators using this function can typically be called from UIs that aren't related to
* the ShaderFx UI at all. So include #OPERATOR_PASS_THROUGH to not block events from reaching
* other operators/handlers. */
*r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
return false;
}
if (!RNA_struct_is_a(panel_ptr->type, RNA_ShaderFx)) {
/* Work around multiple operators using the same shortcut. The operators for the other
* stacks in the property editor use the same key, and will not run after these return
* OPERATOR_CANCELLED. */
*r_retval = (OPERATOR_PASS_THROUGH | OPERATOR_CANCELLED);
return false;
}
ShaderFxData *fx = static_cast<ShaderFxData *>(panel_ptr->data);
RNA_string_set(op->ptr, "shaderfx", fx->name);
return true;
}
static ShaderFxData *edit_shaderfx_property_get(wmOperator *op, Object *ob, int type)
{
char shaderfx_name[MAX_NAME];
ShaderFxData *fx;
RNA_string_get(op->ptr, "shaderfx", shaderfx_name);
fx = BKE_shaderfx_findby_name(ob, shaderfx_name);
if (fx && type != 0 && fx->type != type) {
fx = nullptr;
}
return fx;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Remove ShaderFX Operator
* \{ */
static wmOperatorStatus shaderfx_remove_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
Object *ob = context_active_object(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
if (!fx) {
return OPERATOR_CANCELLED;
}
/* Store name temporarily for report. */
char name[MAX_NAME];
STRNCPY_UTF8(name, fx->name);
if (!shaderfx_remove(op->reports, bmain, ob, fx)) {
return OPERATOR_CANCELLED;
}
if (RNA_boolean_get(op->ptr, "report")) {
BKE_reportf(op->reports, RPT_INFO, "Removed effect: %s", name);
}
WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus shaderfx_remove_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmOperatorStatus retval;
if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_remove_exec(C, op);
}
return retval;
}
void OBJECT_OT_shaderfx_remove(wmOperatorType *ot)
{
ot->name = "Remove Grease Pencil Effect";
ot->description = "Remove a effect from the active Grease Pencil object";
ot->idname = "OBJECT_OT_shaderfx_remove";
ot->invoke = shaderfx_remove_invoke;
ot->exec = shaderfx_remove_exec;
ot->poll = edit_shaderfx_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
edit_shaderfx_report_property(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Move up ShaderFX Operator
* \{ */
static wmOperatorStatus shaderfx_move_up_exec(bContext *C, wmOperator *op)
{
Object *ob = context_active_object(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
if (!fx || !shaderfx_move_up(op->reports, ob, fx)) {
return OPERATOR_CANCELLED;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus shaderfx_move_up_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmOperatorStatus retval;
if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_up_exec(C, op);
}
return retval;
}
void OBJECT_OT_shaderfx_move_up(wmOperatorType *ot)
{
ot->name = "Move Up Effect";
ot->description = "Move effect up in the stack";
ot->idname = "OBJECT_OT_shaderfx_move_up";
ot->invoke = shaderfx_move_up_invoke;
ot->exec = shaderfx_move_up_exec;
ot->poll = edit_shaderfx_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Move Down ShaderFX Operator
* \{ */
static wmOperatorStatus shaderfx_move_down_exec(bContext *C, wmOperator *op)
{
Object *ob = context_active_object(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
if (!fx || !shaderfx_move_down(op->reports, ob, fx)) {
return OPERATOR_CANCELLED;
}
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_event_add_notifier(C, NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus shaderfx_move_down_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
wmOperatorStatus retval;
if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_down_exec(C, op);
}
return retval;
}
void OBJECT_OT_shaderfx_move_down(wmOperatorType *ot)
{
ot->name = "Move Down Effect";
ot->description = "Move effect down in the stack";
ot->idname = "OBJECT_OT_shaderfx_move_down";
ot->invoke = shaderfx_move_down_invoke;
ot->exec = shaderfx_move_down_exec;
ot->poll = edit_shaderfx_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Move ShaderFX to Index Operator
* \{ */
static wmOperatorStatus shaderfx_move_to_index_exec(bContext *C, wmOperator *op)
{
Object *ob = context_active_object(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
int index = RNA_int_get(op->ptr, "index");
if (!fx || !shaderfx_move_to_index(op->reports, ob, fx, index)) {
return OPERATOR_CANCELLED;
}
return OPERATOR_FINISHED;
}
static wmOperatorStatus shaderfx_move_to_index_invoke(bContext *C,
wmOperator *op,
const wmEvent *event)
{
wmOperatorStatus retval;
if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_move_to_index_exec(C, op);
}
return retval;
}
void OBJECT_OT_shaderfx_move_to_index(wmOperatorType *ot)
{
ot->name = "Move Effect to Index";
ot->idname = "OBJECT_OT_shaderfx_move_to_index";
ot->description =
"Change the effect's position in the list so it evaluates after the set number of "
"others";
ot->invoke = shaderfx_move_to_index_invoke;
ot->exec = shaderfx_move_to_index_exec;
ot->poll = edit_shaderfx_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
RNA_def_int(
ot->srna, "index", 0, 0, INT_MAX, "Index", "The index to move the effect to", 0, INT_MAX);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Copy Shader Operator
* \{ */
static wmOperatorStatus shaderfx_copy_exec(bContext *C, wmOperator *op)
{
Object *ob = context_active_object(C);
ShaderFxData *fx = edit_shaderfx_property_get(op, ob, 0);
if (!fx) {
return OPERATOR_CANCELLED;
}
ShaderFxData *nfx = BKE_shaderfx_new(fx->type);
if (!nfx) {
return OPERATOR_CANCELLED;
}
STRNCPY_UTF8(nfx->name, fx->name);
/* Make sure effect data has unique name. */
BKE_shaderfx_unique_name(&ob->shader_fx, nfx);
BKE_shaderfx_copydata(fx, nfx);
BLI_insertlinkafter(&ob->shader_fx, fx, nfx);
DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
WM_main_add_notifier(NC_OBJECT | ND_SHADERFX, ob);
return OPERATOR_FINISHED;
}
static wmOperatorStatus shaderfx_copy_invoke(bContext *C, wmOperator *op, const wmEvent *event)
{
wmOperatorStatus retval;
if (edit_shaderfx_invoke_properties(C, op, event, &retval)) {
return shaderfx_copy_exec(C, op);
}
return retval;
}
void OBJECT_OT_shaderfx_copy(wmOperatorType *ot)
{
ot->name = "Copy Effect";
ot->description = "Duplicate effect at the same position in the stack";
ot->idname = "OBJECT_OT_shaderfx_copy";
ot->invoke = shaderfx_copy_invoke;
ot->exec = shaderfx_copy_exec;
ot->poll = edit_shaderfx_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
edit_shaderfx_properties(ot);
}
/** \} */
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,480 @@
/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include <cstdlib>
#include <cstring>
#include "DNA_armature_types.h"
#include "DNA_curve_types.h"
#include "DNA_lattice_types.h"
#include "DNA_meta_types.h"
#include "DNA_object_types.h"
#include "BLI_listbase.h"
#include "BLI_math_matrix.h"
#include "BLI_math_vector.h"
#include "BKE_action.hh"
#include "BKE_armature.hh"
#include "BKE_editmesh.hh"
#include "BKE_lattice.hh"
#include "BKE_layer.hh"
#include "BKE_object.hh"
#include "BKE_scene.hh"
#include "DEG_depsgraph_query.hh"
#include "ED_curve.hh"
#include "ED_object.hh" /* own include */
#include "WM_api.hh"
#include "MEM_guardedalloc.h"
namespace blender::ed::object {
/* -------------------------------------------------------------------- */
/** \name Material Functions
* \{ */
bool material_active_index_set(Object *ob, const int index)
{
if (ob->totcol > 0) {
const short actcol_test = std::clamp(index + 1, 1, ob->totcol);
if (ob->actcol != actcol_test) {
ob->actcol = actcol_test;
WM_main_add_notifier(NC_MATERIAL | ND_SHADING_LINKS, nullptr);
return true;
}
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Active Element Center
* \{ */
bool calc_active_center_for_editmode(Object *obedit, const bool select_only, float r_center[3])
{
switch (obedit->type) {
case OB_MESH: {
BMEditMesh *em = BKE_editmesh_from_object(obedit);
BMEditSelection ese;
if (BM_select_history_active_get(em->bm, &ese)) {
BM_editselection_center(&ese, r_center);
return true;
}
break;
}
case OB_ARMATURE: {
bArmature *arm = id_cast<bArmature *>(obedit->data);
EditBone *ebo = arm->act_edbone;
if (ebo && (!select_only || (ebo->flag & (BONE_SELECTED | BONE_ROOTSEL)))) {
copy_v3_v3(r_center, ebo->head);
return true;
}
break;
}
case OB_CURVES_LEGACY:
case OB_SURF: {
Curve *cu = id_cast<Curve *>(obedit->data);
if (ED_curve_active_center(cu, r_center)) {
return true;
}
break;
}
case OB_MBALL: {
MetaBall *mb = id_cast<MetaBall *>(obedit->data);
MetaElem *ml_act = mb->lastelem;
if (ml_act && (!select_only || (ml_act->flag & SELECT))) {
copy_v3_v3(r_center, &ml_act->x);
return true;
}
break;
}
case OB_LATTICE: {
BPoint *actbp = BKE_lattice_active_point_get(id_cast<Lattice *>(obedit->data));
if (actbp) {
copy_v3_v3(r_center, actbp->vec);
return true;
}
break;
}
case OB_GREASE_PENCIL: {
copy_v3_v3(r_center, obedit->loc);
mul_m4_v3(obedit->world_to_object().ptr(), r_center);
return true;
}
default:
break;
}
return false;
}
bool calc_active_center_for_posemode(Object *ob, const bool select_only, float r_center[3])
{
bPoseChannel *pchan = BKE_pose_channel_active_if_bonecoll_visible(ob);
if (pchan && (!select_only || (pchan->flag & POSE_SELECTED))) {
const bArmature *arm = id_cast<bArmature *>(ob->data);
BKE_pose_channel_transform_location(arm, pchan, r_center);
return true;
}
return false;
}
bool calc_active_center(Object *ob, const bool select_only, float r_center[3])
{
if (ob->mode & OB_MODE_EDIT) {
if (calc_active_center_for_editmode(ob, select_only, r_center)) {
mul_m4_v3(ob->object_to_world().ptr(), r_center);
return true;
}
return false;
}
if (ob->mode & OB_MODE_POSE) {
if (calc_active_center_for_posemode(ob, select_only, r_center)) {
mul_m4_v3(ob->object_to_world().ptr(), r_center);
return true;
}
return false;
}
if (!select_only || (ob->base_flag & BASE_SELECTED)) {
copy_v3_v3(r_center, ob->object_to_world().location());
return true;
}
return false;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Object Child Skip
*
* Don't transform unselected children, this is done using the parent inverse matrix.
*
* \note The complex logic here is caused by mixed selection within a single selection chain,
* otherwise we only need #XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM for single objects.
*
* \{ */
struct XFormObjectSkipChild {
float obmat_orig[4][4] = {};
float parent_obmat_orig[4][4] = {};
float parent_obmat_inv_orig[4][4] = {};
float parent_recurse_obmat_orig[4][4] = {};
float parentinv_orig[4][4] = {};
Object *ob_parent_recurse = nullptr;
int mode = OB_MODE_OBJECT;
};
struct XFormObjectSkipChild_Container {
Map<Object *, std::unique_ptr<XFormObjectSkipChild>> obchild_in_obmode_map;
};
XFormObjectSkipChild_Container *xform_skip_child_container_create()
{
XFormObjectSkipChild_Container *xcs = MEM_new<XFormObjectSkipChild_Container>(__func__);
return xcs;
}
void xform_skip_child_container_item_ensure_from_array(XFormObjectSkipChild_Container *xcs,
const Main &bmain,
const Scene *scene,
ViewLayer *view_layer,
Object **objects,
uint objects_len)
{
Set<Object *> objects_in_transdata(Span(objects, objects_len));
BKE_view_layer_synced_ensure(bmain, scene, view_layer);
ListBaseT<Base> *object_bases = BKE_view_layer_object_bases_get(view_layer);
for (Base &base : *object_bases) {
Object *ob = base.object;
if (ob->parent != nullptr) {
if (!objects_in_transdata.contains(ob)) {
if (objects_in_transdata.contains(ob->parent)) {
object_xform_skip_child_container_item_ensure(
xcs, ob, nullptr, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM);
}
}
else {
if (!objects_in_transdata.contains(ob->parent)) {
Object *ob_parent_recurse = ob->parent;
if (ob_parent_recurse != nullptr) {
while (ob_parent_recurse != nullptr) {
if (objects_in_transdata.contains(ob_parent_recurse)) {
break;
}
ob_parent_recurse = ob_parent_recurse->parent;
}
if (ob_parent_recurse) {
object_xform_skip_child_container_item_ensure(
xcs, ob, ob_parent_recurse, XFORM_OB_SKIP_CHILD_PARENT_APPLY);
}
}
}
}
}
}
for (Base &base : *object_bases) {
Object *ob = base.object;
if (objects_in_transdata.contains(ob)) {
/* pass. */
}
else if (ob->parent != nullptr) {
if (objects_in_transdata.contains(ob->parent)) {
if (!objects_in_transdata.contains(ob)) {
object_xform_skip_child_container_item_ensure(
xcs, ob, nullptr, XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM);
}
}
}
}
}
void object_xform_skip_child_container_destroy(XFormObjectSkipChild_Container *xcs)
{
MEM_delete(xcs);
}
void object_xform_skip_child_container_item_ensure(XFormObjectSkipChild_Container *xcs,
Object *ob,
Object *ob_parent_recurse,
int mode)
{
xcs->obchild_in_obmode_map.lookup_or_add_cb(ob, [&]() {
std::unique_ptr<XFormObjectSkipChild> xf = std::make_unique<XFormObjectSkipChild>();
copy_m4_m4(xf->parentinv_orig, ob->parentinv);
copy_m4_m4(xf->obmat_orig, ob->object_to_world().ptr());
copy_m4_m4(xf->parent_obmat_orig, ob->parent->object_to_world().ptr());
invert_m4_m4(xf->parent_obmat_inv_orig, ob->parent->object_to_world().ptr());
if (ob_parent_recurse) {
copy_m4_m4(xf->parent_recurse_obmat_orig, ob_parent_recurse->object_to_world().ptr());
}
xf->mode = mode;
xf->ob_parent_recurse = ob_parent_recurse;
return xf;
});
}
void object_xform_skip_child_container_update_all(XFormObjectSkipChild_Container *xcs,
Main *bmain,
Depsgraph *depsgraph)
{
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
for (auto item : xcs->obchild_in_obmode_map.items()) {
Object *ob = item.key;
XFormObjectSkipChild *xf = item.value.get();
/* The following blocks below assign 'dmat'. */
float dmat[4][4];
if (xf->mode == XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM) {
/* Parent is transformed, this isn't so compensate. */
Object *ob_parent_eval = DEG_get_evaluated(depsgraph, ob->parent);
mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, ob_parent_eval->object_to_world().ptr());
invert_m4(dmat);
}
else if (xf->mode == XFORM_OB_SKIP_CHILD_PARENT_IS_XFORM_INDIRECT) {
/* Calculate parent matrix (from the root transform). */
Object *ob_parent_recurse_eval = DEG_get_evaluated(depsgraph, xf->ob_parent_recurse);
float parent_recurse_obmat_inv[4][4];
invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->object_to_world().ptr());
mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv);
invert_m4(dmat);
float parent_obmat_calc[4][4];
mul_m4_m4m4(parent_obmat_calc, dmat, xf->parent_obmat_orig);
/* Apply to the parent inverse matrix. */
mul_m4_m4m4(dmat, xf->parent_obmat_inv_orig, parent_obmat_calc);
invert_m4(dmat);
}
else {
BLI_assert(xf->mode == XFORM_OB_SKIP_CHILD_PARENT_APPLY);
/* Transform this - without transform data. */
Object *ob_parent_recurse_eval = DEG_get_evaluated(depsgraph, xf->ob_parent_recurse);
float parent_recurse_obmat_inv[4][4];
invert_m4_m4(parent_recurse_obmat_inv, ob_parent_recurse_eval->object_to_world().ptr());
mul_m4_m4m4(dmat, xf->parent_recurse_obmat_orig, parent_recurse_obmat_inv);
invert_m4(dmat);
float obmat_calc[4][4];
mul_m4_m4m4(obmat_calc, dmat, xf->obmat_orig);
/* obmat_calc is just obmat. */
/* Get the matrices relative to the parent. */
float obmat_parent_relative_orig[4][4];
float obmat_parent_relative_calc[4][4];
float obmat_parent_relative_inv_orig[4][4];
mul_m4_m4m4(obmat_parent_relative_orig, xf->parent_obmat_inv_orig, xf->obmat_orig);
mul_m4_m4m4(obmat_parent_relative_calc, xf->parent_obmat_inv_orig, obmat_calc);
invert_m4_m4(obmat_parent_relative_inv_orig, obmat_parent_relative_orig);
/* Apply to the parent inverse matrix. */
mul_m4_m4m4(dmat, obmat_parent_relative_calc, obmat_parent_relative_inv_orig);
}
mul_m4_m4m4(ob->parentinv, dmat, xf->parentinv_orig);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Object Data Transform Container
*
* Use to implement 'Affect Only Origins' feature.
*
* \{ */
struct XFormObjectData_Extra {
Object *ob = nullptr;
float obmat_orig[4][4] = {};
std::unique_ptr<XFormObjectData> xod;
};
struct XFormObjectData_Container {
Map<ID *, std::unique_ptr<XFormObjectData_Extra>> obdata_in_obmode_map;
};
void data_xform_container_item_ensure(XFormObjectData_Container *xds, Object *ob)
{
xds->obdata_in_obmode_map.lookup_or_add_cb(ob->data, [&]() {
auto xf = std::make_unique<XFormObjectData_Extra>();
copy_m4_m4(xf->obmat_orig, ob->object_to_world().ptr());
xf->ob = ob;
/* Result may be nullptr, that's OK. */
xf->xod = data_xform_create(ob->data);
return xf;
});
}
void data_xform_container_update_all(XFormObjectData_Container *xds,
Main *bmain,
Depsgraph *depsgraph)
{
if (xds->obdata_in_obmode_map.is_empty()) {
return;
}
BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
for (const auto &item : xds->obdata_in_obmode_map.items()) {
ID *id = item.key;
XFormObjectData_Extra *xf = item.value.get();
if (!xf->xod) {
continue;
}
Object *ob_eval = DEG_get_evaluated(depsgraph, xf->ob);
float4x4 imat, dmat;
invert_m4_m4(imat.ptr(), xf->obmat_orig);
mul_m4_m4m4(dmat.ptr(), imat.ptr(), ob_eval->object_to_world().ptr());
invert_m4(dmat.ptr());
data_xform_by_mat4(*xf->xod, dmat);
if (xf->ob->type == OB_ARMATURE) {
/* TODO: none of the current flags properly update armatures, needs investigation. */
DEG_id_tag_update(id, 0);
}
else {
DEG_id_tag_update(id, ID_RECALC_GEOMETRY);
}
}
}
XFormObjectData_Container *data_xform_container_create()
{
return MEM_new<XFormObjectData_Container>(__func__);
}
void data_xform_container_destroy(XFormObjectData_Container *xds)
{
MEM_delete(xds);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Transform Object Array
*
* Low level object transform function, transforming objects by `matrix`.
* Simple alternative to full transform logic.
* \{ */
static bool object_parent_in_set(const Set<Object *> &objects_set, Object *ob)
{
for (Object *parent = ob->parent; parent; parent = parent->parent) {
if (objects_set.contains(parent)) {
return true;
}
}
return false;
}
void object_xform_array_m4(Object **objects, uint objects_len, const float matrix[4][4])
{
/* Filter out objects that have parents in `objects_set`. */
{
Set<Object *> objects_set(Span(objects, objects_len));
for (uint i = 0; i < objects_len;) {
if (object_parent_in_set(objects_set, objects[i])) {
objects[i] = objects[--objects_len];
}
else {
i++;
}
}
}
/* Detect translation only matrix, prevent rotation/scale channels from being touched at all. */
bool is_translation_only;
{
float test_m4_a[4][4], test_m4_b[4][4];
unit_m4(test_m4_a);
copy_m4_m4(test_m4_b, matrix);
zero_v3(test_m4_b[3]);
is_translation_only = equals_m4m4(test_m4_a, test_m4_b);
}
if (is_translation_only) {
for (uint i = 0; i < objects_len; i++) {
Object *ob = objects[i];
add_v3_v3(ob->loc, matrix[3]);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
}
else {
for (uint i = 0; i < objects_len; i++) {
float m4[4][4];
Object *ob = objects[i];
BKE_object_to_mat4(ob, m4);
mul_m4_m4m4(m4, matrix, m4);
BKE_object_apply_mat4(ob, m4, true, true);
DEG_id_tag_update(&ob->id, ID_RECALC_TRANSFORM);
}
}
}
/** \} */
} // namespace blender::ed::object

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,540 @@
/* SPDX-FileCopyrightText: 2025 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "BKE_context.hh"
#include "BKE_curves.hh"
#include "BKE_geometry_set.hh"
#include "BKE_geometry_set_instances.hh"
#include "BKE_grease_pencil.hh"
#include "BKE_instances.hh"
#include "BKE_layer.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_material.hh"
#include "BKE_mesh.h"
#include "BKE_mesh.hh"
#include "BKE_multires.hh"
#include "BKE_object.hh"
#include "BKE_pointcloud.hh"
#include "DEG_depsgraph_query.hh"
#include "DNA_collection_types.h"
#include "DNA_curves_types.h"
#include "DNA_grease_pencil_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_pointcloud_types.h"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "BLI_listbase.h"
#include "BLI_map.hh"
#include "BLI_math_matrix.h"
#include "BLI_math_matrix.hh"
#include "object_intern.hh"
namespace blender::ed::object {
/** Sets the transform of the object to a specific matrix. */
static void set_local_object_transform(Object &ob, const float4x4 &transform)
{
float3 location;
math::EulerXYZ rotation;
float3 scale;
math::to_loc_rot_scale_safe<true>(transform, location, rotation, scale);
copy_v3_v3(ob.loc, location);
copy_v3_v3(ob.rot, float3(rotation.x().radian(), rotation.y().radian(), rotation.z().radian()));
copy_v3_v3(ob.scale, scale);
ob.rotmode = ROT_MODE_EUL;
}
/** Objects for a #GeometrySet. */
struct ComponentObjects {
Object *mesh_ob = nullptr;
Object *curves_ob = nullptr;
Object *pointcloud_ob = nullptr;
Object *greasepencil_ob = nullptr;
Vector<Object *> instance_objects;
Vector<Object *> all_objects() const
{
Vector<Object *> objects;
if (mesh_ob) {
objects.append(mesh_ob);
}
if (curves_ob) {
objects.append(curves_ob);
}
if (pointcloud_ob) {
objects.append(pointcloud_ob);
}
if (greasepencil_ob) {
objects.append(greasepencil_ob);
}
for (Object *instance_object : instance_objects) {
objects.append(instance_object);
}
return objects;
}
};
static void copy_materials_to_new_geometry_object(const Object &src_ob_eval,
const ID &src_data_eval,
Object &dst_ob_orig,
ID &dst_data_orig)
{
const int materials_num = BKE_id_material_used_eval(src_data_eval);
if (materials_num == 0) {
return;
}
*BKE_id_material_len_p(&dst_data_orig) = materials_num;
dst_ob_orig.totcol = materials_num;
dst_ob_orig.matbits = MEM_new_array_zeroed<char>(materials_num, __func__);
dst_ob_orig.mat = MEM_new_array_zeroed<Material *>(materials_num, __func__);
Material ***dst_materials = BKE_id_material_array_p(&dst_data_orig);
*dst_materials = MEM_new_array_zeroed<Material *>(materials_num, __func__);
for (int i = 0; i < materials_num; i++) {
const Material *material_eval = BKE_object_material_get_eval(
src_ob_eval, src_data_eval, i + 1);
Material *material_orig = const_cast<Material *>(DEG_get_original(material_eval));
if (material_orig) {
(*dst_materials)[i] = material_orig;
id_us_plus(&material_orig->id);
}
}
}
struct CollectionWithTransform {
/* A collection that should be instanced. */
Collection *collection = nullptr;
/* A transform that needs to be applied to instances of that collection. */
float4x4 transform = float4x4::identity();
};
/** Utility class to build objects for a #GeometrySet recursively. */
class GeometryToObjectsBuilder {
private:
Main &bmain_;
Map<const ID *, Object *> new_object_by_generated_geometry_;
Map<bke::InstanceReference, CollectionWithTransform> collection_by_instance_;
Vector<Collection *> new_instance_collections_;
public:
GeometryToObjectsBuilder(Main &bmain) : bmain_(bmain) {}
Collection *build_collection_for_geometry(const Object &src_ob_eval,
const bke::GeometrySet &geometry)
{
ComponentObjects component_objects = this->get_objects_for_geometry(src_ob_eval, geometry);
const StringRefNull name = geometry.name().is_empty() ?
StringRefNull(BKE_id_name(src_ob_eval.id)) :
StringRefNull(geometry.name());
return this->collection_from_component_objects(component_objects, name);
}
ComponentObjects get_objects_for_geometry(const Object &src_ob_eval,
const bke::GeometrySet &geometry)
{
const StringRefNull name = geometry.name().is_empty() ?
StringRefNull(BKE_id_name(src_ob_eval.id)) :
StringRefNull(geometry.name());
ComponentObjects objects;
if (const Mesh *mesh = geometry.get_mesh()) {
if (mesh->verts_num > 0) {
objects.mesh_ob = this->ensure_object_for_mesh(src_ob_eval, *mesh, name);
}
}
if (const Curves *curves = geometry.get_curves()) {
if (curves->geometry.curve_num > 0) {
objects.curves_ob = this->ensure_object_for_curves(src_ob_eval, *curves, name);
}
}
if (const PointCloud *pointcloud = geometry.get_pointcloud()) {
if (pointcloud->totpoint > 0) {
objects.pointcloud_ob = this->ensure_object_for_pointcloud(src_ob_eval, *pointcloud, name);
}
}
if (const GreasePencil *greasepencil = geometry.get_grease_pencil()) {
if (greasepencil->layers().size() > 0) {
objects.greasepencil_ob = this->ensure_object_for_grease_pencil(
src_ob_eval, *greasepencil, name);
}
}
if (const bke::Instances *instances = geometry.get_instances()) {
objects.instance_objects = this->create_objects_for_instances(src_ob_eval, *instances);
}
return objects;
}
Span<Collection *> new_instance_collections() const
{
return new_instance_collections_;
}
private:
Collection *collection_from_component_objects(const ComponentObjects &component_objects,
const StringRefNull name)
{
Collection *collection = BKE_collection_add(&bmain_, nullptr, name.c_str());
for (Object *object : component_objects.all_objects()) {
BKE_collection_object_add(&bmain_, collection, object);
}
return collection;
}
Object *ensure_object_for_mesh(const Object &src_ob_eval,
const Mesh &src_mesh,
const StringRefNull name)
{
return new_object_by_generated_geometry_.lookup_or_add_cb(&src_mesh.id, [&]() {
Mesh *new_mesh = BKE_id_new<Mesh>(&bmain_, name.c_str());
Object *new_ob = BKE_object_add_only_object(&bmain_, OB_MESH, name.c_str());
new_ob->data = id_cast<ID *>(new_mesh);
BKE_mesh_nomain_to_mesh(BKE_mesh_copy_for_eval(src_mesh), new_mesh, new_ob);
new_mesh->attributes_for_write().remove_anonymous();
copy_materials_to_new_geometry_object(src_ob_eval, src_mesh.id, *new_ob, new_mesh->id);
bke::mesh_remove_invalid_attribute_strings(*new_mesh);
multires_customdata_delete(new_mesh);
return new_ob;
});
}
Object *ensure_object_for_curves(const Object &src_ob_eval,
const Curves &src_curves,
const StringRefNull name)
{
return new_object_by_generated_geometry_.lookup_or_add_cb(&src_curves.id, [&]() {
Curves *new_curves = BKE_id_new<Curves>(&bmain_, name.c_str());
Object *new_ob = BKE_object_add_only_object(&bmain_, OB_CURVES, name.c_str());
new_ob->data = id_cast<ID *>(new_curves);
new_curves->geometry.wrap() = src_curves.geometry.wrap();
new_curves->geometry.wrap().attributes_for_write().remove_anonymous();
copy_materials_to_new_geometry_object(src_ob_eval, src_curves.id, *new_ob, new_curves->id);
return new_ob;
});
}
Object *ensure_object_for_pointcloud(const Object &src_ob_eval,
const PointCloud &src_pointcloud,
const StringRefNull name)
{
return new_object_by_generated_geometry_.lookup_or_add_cb(&src_pointcloud.id, [&]() {
PointCloud *new_pointcloud = BKE_id_new<PointCloud>(&bmain_, name.c_str());
Object *new_ob = BKE_object_add_only_object(&bmain_, OB_POINTCLOUD, name.c_str());
new_ob->data = id_cast<ID *>(new_pointcloud);
BKE_pointcloud_nomain_to_pointcloud(BKE_pointcloud_copy_for_eval(&src_pointcloud),
new_pointcloud);
new_pointcloud->attributes_for_write().remove_anonymous();
copy_materials_to_new_geometry_object(
src_ob_eval, src_pointcloud.id, *new_ob, new_pointcloud->id);
return new_ob;
});
}
Object *ensure_object_for_grease_pencil(const Object &src_ob_eval,
const GreasePencil &src_grease_pencil,
const StringRefNull name)
{
return new_object_by_generated_geometry_.lookup_or_add_cb(&src_grease_pencil.id, [&]() {
GreasePencil *new_grease_pencil = BKE_id_new<GreasePencil>(&bmain_, name.c_str());
Object *new_ob = BKE_object_add_only_object(&bmain_, OB_GREASE_PENCIL, name.c_str());
new_ob->data = id_cast<ID *>(new_grease_pencil);
GreasePencil *greasepencil_to_move_from = BKE_grease_pencil_copy_for_eval(
&src_grease_pencil);
BKE_grease_pencil_nomain_to_grease_pencil(greasepencil_to_move_from, new_grease_pencil);
new_grease_pencil->attributes_for_write().remove_anonymous();
for (GreasePencilDrawingBase *base : new_grease_pencil->drawings()) {
if (base->type != GP_DRAWING) {
continue;
}
bke::greasepencil::Drawing &drawing =
reinterpret_cast<GreasePencilDrawing *>(base)->wrap();
drawing.strokes_for_write().attributes_for_write().remove_anonymous();
}
copy_materials_to_new_geometry_object(
src_ob_eval, src_grease_pencil.id, *new_ob, new_grease_pencil->id);
return new_ob;
});
}
Vector<Object *> create_objects_for_instances(const Object &src_ob_eval,
const bke::Instances &src_instances)
{
if (std::optional<Vector<Object *>> simple_objects = this->create_objects_for_instances_simple(
src_ob_eval, src_instances))
{
return *simple_objects;
}
bke::Instances instances = src_instances;
instances.remove_unused_references();
/* Each instance will be a collection instance, so we need to get the collection for each
* #InstanceReference that is instanced. */
Vector<CollectionWithTransform> data_by_handle;
for (const bke::InstanceReference &reference : instances.references()) {
data_by_handle.append(
this->get_or_create_collection_for_instance_reference(src_ob_eval, reference));
}
const Span<int> handles = instances.reference_handles();
const Span<float4x4> transforms = instances.transforms();
Vector<Object *> objects;
for (const int instance_i : IndexRange(instances.instances_num())) {
const int handle = handles[instance_i];
if (!data_by_handle.index_range().contains(handle)) {
continue;
}
const CollectionWithTransform &instance = data_by_handle[handle];
if (!instance.collection) {
continue;
}
/* Create an empty object that then instances the collection. */
Object *instance_object = BKE_object_add_only_object(
&bmain_, OB_EMPTY, BKE_id_name(instance.collection->id));
instance_object->transflag = OB_DUPLICOLLECTION;
instance_object->instance_collection = instance.collection;
id_us_plus(&instance.collection->id);
const float4x4 &transform = transforms[instance_i];
set_local_object_transform(*instance_object, transform * instance.transform);
objects.append(instance_object);
}
return objects;
}
/**
* Under some circumstances, additional nested collection instances can be avoided and objects
* can be instanced directly. This is the case when the instances have the identity transform.
* If nullopt is returned, a fallback method has to be used that creates additional collections.
*/
std::optional<Vector<Object *>> create_objects_for_instances_simple(
const Object &src_ob_eval, const bke::Instances &src_instances)
{
const Span<float4x4> transforms = src_instances.transforms();
const Span<int> handles = src_instances.reference_handles();
const Span<bke::InstanceReference> references = src_instances.references();
Vector<Object *> objects;
for (const int i : IndexRange(src_instances.instances_num())) {
const float4x4 &transform = transforms[i];
if (transform != float4x4::identity()) {
return std::nullopt;
}
const int handle = handles[i];
if (handle < 0 || handle >= references.size()) {
return std::nullopt;
}
const bke::InstanceReference &reference = references[handle];
switch (reference.type()) {
case bke::InstanceReference::Type::None: {
break;
}
case bke::InstanceReference::Type::Object: {
Object &object_eval = reference.object();
Object *object_orig = DEG_get_original(&object_eval);
if (ELEM(object_orig, &src_ob_eval, nullptr)) {
return std::nullopt;
}
objects.append(object_orig);
break;
}
case bke::InstanceReference::Type::Collection: {
return std::nullopt;
}
case bke::InstanceReference::Type::GeometrySet: {
const ComponentObjects component_objects = this->get_objects_for_geometry(
src_ob_eval, reference.geometry_set());
objects.extend(component_objects.all_objects());
break;
}
}
}
return objects;
}
CollectionWithTransform get_or_create_collection_for_instance_reference(
const Object &src_ob_eval, const bke::InstanceReference &reference)
{
if (const CollectionWithTransform *instance = collection_by_instance_.lookup_ptr(reference)) {
return *instance;
}
CollectionWithTransform instance;
switch (reference.type()) {
case bke::InstanceReference::Type::None: {
break;
}
case bke::InstanceReference::Type::Object: {
/* Create a collection for the object because we can't instance objects directly. */
Object &object_eval = reference.object();
Object *object_orig = DEG_get_original(&object_eval);
if (object_orig->type == OB_EMPTY && object_orig->instance_collection) {
instance.collection = object_orig->instance_collection;
}
else {
instance.collection = BKE_collection_add(&bmain_, nullptr, BKE_id_name(object_orig->id));
new_instance_collections_.append(instance.collection);
BKE_collection_object_add(&bmain_, instance.collection, object_orig);
/* Handle the object transform because it may not be the identity matrix. The location is
* handled by setting the collection instance offset to it. The rotation and scale are
* handled by offsetting the instance using the collection by the inverse amount. */
float4x4 object_transform;
BKE_object_to_mat4(object_orig, object_transform.ptr());
instance.transform = float4x4(math::invert(float3x3(object_transform)));
copy_v3_v3(instance.collection->instance_offset, object_transform.location());
}
break;
}
case bke::InstanceReference::Type::Collection: {
/* For collections, we don't need to create a new wrapper collection, we can just create
* objects that instance the existing collection. */
Collection &collection_eval = reference.collection();
Collection *collection_orig = DEG_get_original(&collection_eval);
instance.collection = collection_orig;
break;
}
case bke::InstanceReference::Type::GeometrySet: {
instance.collection = this->build_collection_for_geometry(src_ob_eval,
reference.geometry_set());
new_instance_collections_.append(instance.collection);
break;
}
}
collection_by_instance_.add(reference, instance);
return instance;
}
};
static Vector<Collection *> find_collections_containing_object(Main &bmain,
Scene *scene,
Object &object)
{
VectorSet<Collection *> collections;
FOREACH_COLLECTION_BEGIN (&bmain, scene, Collection *, collection) {
if (BKE_collection_has_object(collection, &object)) {
collections.add(collection);
}
}
FOREACH_COLLECTION_END;
return collections.extract_vector();
}
static wmOperatorStatus visual_geometry_to_objects_exec(bContext *C, wmOperator * /*op*/)
{
Main &bmain = *CTX_data_main(C);
Scene &scene = *CTX_data_scene(C);
Depsgraph &depsgraph = *CTX_data_ensure_evaluated_depsgraph(C);
ViewLayer &active_view_layer = *CTX_data_view_layer(C);
Vector<Object *> selected_objects_orig;
CTX_DATA_BEGIN (C, Object *, src_ob_orig, selected_objects) {
selected_objects_orig.append(src_ob_orig);
}
CTX_DATA_END;
/* Create all required objects and collections and add them to bmain. However, so far nothing is
* linked to the scene or view layer. That happens below. */
GeometryToObjectsBuilder op(bmain);
Vector<Object *> all_new_top_level_objects;
for (Object *src_ob_orig : selected_objects_orig) {
Object *src_ob_eval = DEG_get_evaluated(&depsgraph, src_ob_orig);
bke::GeometrySet geometry_eval = bke::object_get_evaluated_geometry_set(*src_ob_eval);
const ComponentObjects new_component_objects = op.get_objects_for_geometry(*src_ob_eval,
geometry_eval);
const Vector<Object *> top_level_objects = new_component_objects.all_objects();
all_new_top_level_objects.extend(top_level_objects);
/* Find the collections that the active object is in, because we want to add the new objects
* in the same place. */
const Vector<Collection *> collections_to_add_to = find_collections_containing_object(
bmain, &scene, *src_ob_orig);
float4x4 src_ob_local_transform;
BKE_object_to_mat4(src_ob_eval, src_ob_local_transform.ptr());
for (Object *object : top_level_objects) {
/* Link the new objects into some collections. */
for (Collection *collection_to_add_to : collections_to_add_to) {
BKE_collection_object_add(&bmain, collection_to_add_to, object);
}
/* Transform and parent the objects so that they align with the source object. */
float4x4 old_transform;
BKE_object_to_mat4(object, old_transform.ptr());
set_local_object_transform(*object, src_ob_local_transform * old_transform);
object->parent = src_ob_orig->parent;
copy_m4_m4(object->parentinv, src_ob_orig->parentinv);
}
}
const Span<Collection *> new_instance_collections = op.new_instance_collections();
for (Collection *new_collection : new_instance_collections) {
/* Add the new collections to the scene collection. This makes them more visible to the user,
* compared to having collection instances which use collections that are not in the scene. */
BKE_collection_child_add(&bmain, scene.master_collection, new_collection);
}
/* Ensure that the #Base for objects and #LayerCollection for collections are created. */
BKE_scene_view_layers_synced_ensure(bmain, &scene);
/* Deselect everything so that we can select the new objects. */
BKE_view_layer_base_deselect_all(bmain, &scene, &active_view_layer);
/* Select the new objects. */
for (Object *object : all_new_top_level_objects) {
Base *base = BKE_view_layer_base_find(&active_view_layer, object);
base->flag |= BASE_SELECTED;
}
/* Make one of the new objects active. */
if (!all_new_top_level_objects.is_empty()) {
Base *first_base = BKE_view_layer_base_find(&active_view_layer, all_new_top_level_objects[0]);
BKE_view_layer_base_select_and_set_active(&active_view_layer, first_base);
base_active_refresh(&bmain, &scene, &active_view_layer);
}
/* Exclude the new collections. This is done because they are only instanced by other objects but
* should not be visible by themselves. */
for (ViewLayer &view_layer : scene.view_layers) {
for (Collection *new_collection : new_instance_collections) {
LayerCollection *new_layer_collection = BKE_layer_collection_first_from_scene_collection(
&view_layer, new_collection);
BKE_layer_collection_set_flag(new_layer_collection, LAYER_COLLECTION_EXCLUDE, true);
}
}
BKE_view_layer_need_resync_tag(&active_view_layer);
DEG_id_tag_update(&scene.id, ID_RECALC_BASE_FLAGS);
DEG_relations_tag_update(&bmain);
WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, &scene);
WM_main_add_notifier(NC_SCENE | ND_LAYER, nullptr);
WM_main_add_notifier(NC_SCENE | ND_LAYER_CONTENT, nullptr);
WM_main_add_notifier(NC_OBJECT | ND_DRAW, nullptr);
return OPERATOR_FINISHED;
}
void OBJECT_OT_visual_geometry_to_objects(wmOperatorType *ot)
{
ot->name = "Visual Geometry to Objects";
ot->description = "Convert geometry and instances into editable objects and collections";
ot->idname = "OBJECT_OT_visual_geometry_to_objects";
ot->exec = visual_geometry_to_objects_exec;
ot->poll = ED_operator_object_active;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
} // namespace blender::ed::object

View File

@@ -0,0 +1,183 @@
/* SPDX-FileCopyrightText: 2008 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include <cstring>
#include "BLI_listbase.h"
#include "BLI_math_constants.h"
#include "BLI_path_utils.hh"
#include "BLI_string.h"
#include "DNA_object_types.h"
#include "DNA_volume_types.h"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "BKE_context.hh"
#include "BKE_lib_id.hh"
#include "BKE_main.hh"
#include "BKE_report.hh"
#include "BKE_volume.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_image.hh"
#include "ED_object.hh"
#include "ED_screen.hh"
#include "object_intern.hh"
namespace blender::ed::object {
/* Volume Add */
static Object *object_volume_add(bContext *C, wmOperator *op, const char *name)
{
ushort local_view_bits;
float loc[3], rot[3];
add_generic_get_opts(C, op, 'Z', loc, rot, nullptr, nullptr, &local_view_bits, nullptr);
return add_type(C, OB_VOLUME, name, loc, rot, false, local_view_bits);
}
static wmOperatorStatus object_volume_add_exec(bContext *C, wmOperator *op)
{
return (object_volume_add(C, op, nullptr) != nullptr) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
void OBJECT_OT_volume_add(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Add Volume";
ot->description = "Add a volume object to the scene";
ot->idname = "OBJECT_OT_volume_add";
/* API callbacks. */
ot->exec = object_volume_add_exec;
ot->poll = ED_operator_objectmode;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
add_generic_props(ot, false);
}
/* Volume Import */
static wmOperatorStatus volume_import_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
bool imported = false;
const char *blendfile_path = BKE_main_blendfile_path(bmain);
ListBaseT<ImageFrameRange> ranges = ED_image_filesel_detect_sequences(
blendfile_path, blendfile_path, op, false);
for (ImageFrameRange &range : ranges) {
char filename[FILE_MAX];
BLI_path_split_file_part(range.filepath, filename, sizeof(filename));
BLI_path_extension_strip(filename);
Object *object = object_volume_add(C, op, filename);
Volume *volume = id_cast<Volume *>(object->data);
STRNCPY(volume->filepath, range.filepath);
if (!BKE_volume_load(volume, bmain)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Volume \"%s\" failed to load: %s",
filename,
BKE_volume_grids_error_msg(volume));
BKE_id_delete(bmain, &object->id);
BKE_id_delete(bmain, &volume->id);
continue;
}
if (BKE_volume_is_points_only(volume)) {
BKE_reportf(op->reports,
RPT_WARNING,
"Volume \"%s\" contains points, only voxel grids are supported",
filename);
BKE_id_delete(bmain, &object->id);
BKE_id_delete(bmain, &volume->id);
continue;
}
/* Set sequence parameters after trying to load the first frame, for file validation we want
* to use a consistent frame rather than whatever corresponds to the current scene frame. */
volume->is_sequence = (range.length > 1);
volume->frame_duration = (volume->is_sequence) ? range.length : 0;
volume->frame_start = 1;
volume->frame_offset = (volume->is_sequence) ? range.offset - 1 : 0;
if (BKE_volume_is_y_up(volume)) {
object->rot[0] += M_PI_2;
}
BKE_volume_unload(volume);
imported = true;
range.frames.free_no_destruct();
}
ranges.free_no_destruct();
return (imported) ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
}
static wmOperatorStatus volume_import_invoke(bContext *C,
wmOperator *op,
const wmEvent * /*event*/)
{
if (RNA_struct_property_is_set(op->ptr, "filepath")) {
return volume_import_exec(C, op);
}
RNA_string_set(op->ptr, "filepath", U.textudir);
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
void OBJECT_OT_volume_import(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Import OpenVDB Volume";
ot->description = "Import OpenVDB volume file";
ot->idname = "OBJECT_OT_volume_import";
/* API callbacks. */
ot->exec = volume_import_exec;
ot->invoke = volume_import_invoke;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
WM_operator_properties_filesel(ot,
FILE_TYPE_FOLDER | FILE_TYPE_VOLUME,
FILE_SPECIAL,
FILE_OPENFILE,
WM_FILESEL_FILEPATH | WM_FILESEL_DIRECTORY | WM_FILESEL_FILES |
WM_FILESEL_RELPATH,
FILE_DEFAULTDISPLAY,
FILE_SORT_DEFAULT);
RNA_def_boolean(
ot->srna,
"use_sequence_detection",
true,
"Detect Sequences",
"Automatically detect animated sequences in selected volume files (based on file names)");
add_generic_props(ot, false);
}
} // namespace blender::ed::object

View File

@@ -0,0 +1,311 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup edobj
*/
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
#include "BKE_context.hh"
#include "BLI_math_matrix.h"
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"
#include "DEG_depsgraph_query.hh"
#include "RNA_access.hh"
#include "RNA_define.hh"
#include "WM_api.hh"
#include "WM_types.hh"
#include "ED_object.hh"
#include "ED_transverts.hh"
#include "object_intern.hh"
namespace blender::ed::object {
static void object_warp_calc_view_matrix(float r_mat_view[4][4],
float r_center_view[3],
Object *obedit,
const float viewmat[4][4],
const float center[3],
const float offset_angle)
{
float mat_offset[4][4];
float viewmat_roll[4][4];
/* apply the rotation offset by rolling the view */
axis_angle_to_mat4_single(mat_offset, 'Z', offset_angle);
mul_m4_m4m4(viewmat_roll, mat_offset, viewmat);
/* apply the view and the object matrix */
mul_m4_m4m4(r_mat_view, viewmat_roll, obedit->object_to_world().ptr());
/* get the view-space cursor */
mul_v3_m4v3(r_center_view, viewmat_roll, center);
}
static void object_warp_transverts_minmax_x(TransVertStore *tvs,
const float mat_view[4][4],
const float center_view[3],
float *r_min,
float *r_max)
{
/* no need to apply translation and cursor offset for every vertex, delay this */
const float x_ofs = (mat_view[3][0] - center_view[0]);
float min = FLT_MAX, max = -FLT_MAX;
TransVert *tv = tvs->transverts;
for (int i = 0; i < tvs->transverts_tot; i++, tv++) {
float val;
/* Convert object-space to view-space. */
val = dot_m4_v3_row_x(mat_view, tv->loc);
min = min_ff(min, val);
max = max_ff(max, val);
}
*r_min = min + x_ofs;
*r_max = max + x_ofs;
}
static void object_warp_transverts(TransVertStore *tvs,
const float mat_view[4][4],
const float center_view[3],
const float angle_,
const float min,
const float max)
{
TransVert *tv;
const float angle = -angle_;
/* cache vars for tiny speedup */
#if 1
const float range = max - min;
const float range_inv = 1.0f / range;
const float min_ofs = min + (0.5f * range);
#endif
float dir_min[2], dir_max[2];
float imat_view[4][4];
invert_m4_m4(imat_view, mat_view);
/* calculate the direction vectors outside min/max range */
{
const float phi = angle * 0.5f;
dir_max[0] = cosf(phi);
dir_max[1] = sinf(phi);
dir_min[0] = -dir_max[0];
dir_min[1] = dir_max[1];
}
tv = tvs->transverts;
for (int i = 0; i < tvs->transverts_tot; i++, tv++) {
float co[3], co_add[2];
float val, phi;
/* Convert object-space to view-space. */
mul_v3_m4v3(co, mat_view, tv->loc);
sub_v2_v2(co, center_view);
val = co[0];
/* is overwritten later anyway */
// co[0] = 0.0f;
if (val < min) {
mul_v2_v2fl(co_add, dir_min, min - val);
val = min;
}
else if (val > max) {
mul_v2_v2fl(co_add, dir_max, val - max);
val = max;
}
else {
zero_v2(co_add);
}
/* map from x axis to (-0.5 - 0.5) */
#if 0
val = ((val - min) / (max - min)) - 0.5f;
#else
val = (val - min_ofs) * range_inv;
#endif
/* convert the x axis into a rotation */
phi = val * angle;
co[0] = -sinf(phi) * co[1];
co[1] = cosf(phi) * co[1];
add_v2_v2(co, co_add);
/* Convert view-space to object-space. */
add_v2_v2(co, center_view);
mul_v3_m4v3(tv->loc, imat_view, co);
}
}
static wmOperatorStatus object_warp_verts_exec(bContext *C, wmOperator *op)
{
const float warp_angle = RNA_float_get(op->ptr, "warp_angle");
const float offset_angle = RNA_float_get(op->ptr, "offset_angle");
TransVertStore tvs = {nullptr};
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obedit = CTX_data_edit_object(C);
/* typically from 'rv3d' and 3d cursor */
float viewmat[4][4];
float center[3];
/* 'viewmat' relative vars */
float mat_view[4][4];
float center_view[3];
float min, max;
if (shape_key_report_if_locked(obedit, op->reports)) {
return OPERATOR_CANCELLED;
}
if (ED_transverts_check_obedit(obedit)) {
const Object *obedit_eval = DEG_get_evaluated(depsgraph, obedit);
ED_transverts_create_from_obedit(&tvs, obedit_eval, TM_ALL_JOINTS | TM_SKIP_HANDLES);
}
if (tvs.transverts == nullptr) {
return OPERATOR_CANCELLED;
}
/* Get view-matrix. */
{
PropertyRNA *prop_viewmat = RNA_struct_find_property(op->ptr, "viewmat");
if (RNA_property_is_set(op->ptr, prop_viewmat)) {
RNA_property_float_get_array(op->ptr, prop_viewmat, reinterpret_cast<float *>(viewmat));
}
else {
RegionView3D *rv3d = CTX_wm_region_view3d(C);
if (rv3d) {
copy_m4_m4(viewmat, rv3d->viewmat);
}
else {
unit_m4(viewmat);
}
RNA_property_float_set_array(op->ptr, prop_viewmat, reinterpret_cast<float *>(viewmat));
}
}
/* get center */
{
PropertyRNA *prop_center = RNA_struct_find_property(op->ptr, "center");
if (RNA_property_is_set(op->ptr, prop_center)) {
RNA_property_float_get_array(op->ptr, prop_center, center);
}
else {
const Scene *scene = CTX_data_scene(C);
copy_v3_v3(center, scene->cursor.location);
RNA_property_float_set_array(op->ptr, prop_center, center);
}
}
object_warp_calc_view_matrix(mat_view, center_view, obedit, viewmat, center, offset_angle);
/* get minmax */
{
PropertyRNA *prop_min = RNA_struct_find_property(op->ptr, "min");
PropertyRNA *prop_max = RNA_struct_find_property(op->ptr, "max");
if (RNA_property_is_set(op->ptr, prop_min) || RNA_property_is_set(op->ptr, prop_max)) {
min = RNA_property_float_get(op->ptr, prop_min);
max = RNA_property_float_get(op->ptr, prop_max);
}
else {
/* handy to set the bounds of the mesh */
object_warp_transverts_minmax_x(&tvs, mat_view, center_view, &min, &max);
RNA_property_float_set(op->ptr, prop_min, min);
RNA_property_float_set(op->ptr, prop_max, max);
}
if (min > max) {
std::swap(min, max);
}
}
if (min != max) {
object_warp_transverts(&tvs, mat_view, center_view, warp_angle, min, max);
}
ED_transverts_update_obedit(&tvs, obedit);
ED_transverts_free(&tvs);
WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, obedit);
return OPERATOR_FINISHED;
}
void TRANSFORM_OT_vertex_warp(wmOperatorType *ot)
{
PropertyRNA *prop;
/* identifiers */
ot->name = "Warp";
ot->description = "Warp vertices around the cursor";
ot->idname = "TRANSFORM_OT_vertex_warp";
/* API callbacks. */
ot->exec = object_warp_verts_exec;
ot->poll = ED_transverts_poll;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* props */
prop = RNA_def_float(ot->srna,
"warp_angle",
DEG2RADF(360.0f),
-FLT_MAX,
FLT_MAX,
"Warp Angle",
"Amount to warp about the cursor",
DEG2RADF(-360.0f),
DEG2RADF(360.0f));
RNA_def_property_subtype(prop, PROP_ANGLE);
prop = RNA_def_float(ot->srna,
"offset_angle",
DEG2RADF(0.0f),
-FLT_MAX,
FLT_MAX,
"Offset Angle",
"Angle to use as the basis for warping",
DEG2RADF(-360.0f),
DEG2RADF(360.0f));
RNA_def_property_subtype(prop, PROP_ANGLE);
prop = RNA_def_float(ot->srna, "min", -1.0f, -FLT_MAX, FLT_MAX, "Min", "", -100.0, 100.0);
prop = RNA_def_float(ot->srna, "max", 1.0f, -FLT_MAX, FLT_MAX, "Max", "", -100.0, 100.0);
/* hidden props */
prop = RNA_def_float_matrix(
ot->srna, "viewmat", 4, 4, nullptr, 0.0f, 0.0f, "Matrix", "", 0.0f, 0.0f);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
prop = RNA_def_float_vector_xyz(
ot->srna, "center", 3, nullptr, -FLT_MAX, FLT_MAX, "Center", "", -FLT_MAX, FLT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
} // namespace blender::ed::object