Add Chromium-only Blender WebEngine parity work
This commit is contained in:
91
blender-5.2.0/source/blender/editors/object/CMakeLists.txt
Normal file
91
blender-5.2.0/source/blender/editors/object/CMakeLists.txt
Normal 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)
|
||||
@@ -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
|
||||
@@ -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
|
||||
5640
blender-5.2.0/source/blender/editors/object/object_add.cc
Normal file
5640
blender-5.2.0/source/blender/editors/object/object_add.cc
Normal file
File diff suppressed because it is too large
Load Diff
573
blender-5.2.0/source/blender/editors/object/object_bake.cc
Normal file
573
blender-5.2.0/source/blender/editors/object/object_bake.cc
Normal 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
|
||||
2372
blender-5.2.0/source/blender/editors/object/object_bake_api.cc
Normal file
2372
blender-5.2.0/source/blender/editors/object/object_bake_api.cc
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
88
blender-5.2.0/source/blender/editors/object/object_camera.cc
Normal file
88
blender-5.2.0/source/blender/editors/object/object_camera.cc
Normal 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
|
||||
1283
blender-5.2.0/source/blender/editors/object/object_collection.cc
Normal file
1283
blender-5.2.0/source/blender/editors/object/object_collection.cc
Normal file
File diff suppressed because it is too large
Load Diff
2776
blender-5.2.0/source/blender/editors/object/object_constraint.cc
Normal file
2776
blender-5.2.0/source/blender/editors/object/object_constraint.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
@@ -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, ¶ms);
|
||||
}
|
||||
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(<->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
|
||||
2564
blender-5.2.0/source/blender/editors/object/object_edit.cc
Normal file
2564
blender-5.2.0/source/blender/editors/object/object_edit.cc
Normal file
File diff suppressed because it is too large
Load Diff
992
blender-5.2.0/source/blender/editors/object/object_hook.cc
Normal file
992
blender-5.2.0/source/blender/editors/object/object_hook.cc
Normal 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
|
||||
394
blender-5.2.0/source/blender/editors/object/object_intern.hh
Normal file
394
blender-5.2.0/source/blender/editors/object/object_intern.hh
Normal 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
|
||||
@@ -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
|
||||
632
blender-5.2.0/source/blender/editors/object/object_modes.cc
Normal file
632
blender-5.2.0/source/blender/editors/object/object_modes.cc
Normal 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
|
||||
4089
blender-5.2.0/source/blender/editors/object/object_modifier.cc
Normal file
4089
blender-5.2.0/source/blender/editors/object/object_modifier.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
351
blender-5.2.0/source/blender/editors/object/object_ops.cc
Normal file
351
blender-5.2.0/source/blender/editors/object/object_ops.cc
Normal 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
|
||||
179
blender-5.2.0/source/blender/editors/object/object_random.cc
Normal file
179
blender-5.2.0/source/blender/editors/object/object_random.cc
Normal 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
|
||||
3290
blender-5.2.0/source/blender/editors/object/object_relations.cc
Normal file
3290
blender-5.2.0/source/blender/editors/object/object_relations.cc
Normal file
File diff suppressed because it is too large
Load Diff
1247
blender-5.2.0/source/blender/editors/object/object_remesh.cc
Normal file
1247
blender-5.2.0/source/blender/editors/object/object_remesh.cc
Normal file
File diff suppressed because it is too large
Load Diff
1483
blender-5.2.0/source/blender/editors/object/object_select.cc
Normal file
1483
blender-5.2.0/source/blender/editors/object/object_select.cc
Normal file
File diff suppressed because it is too large
Load Diff
727
blender-5.2.0/source/blender/editors/object/object_shader_fx.cc
Normal file
727
blender-5.2.0/source/blender/editors/object/object_shader_fx.cc
Normal 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
|
||||
1067
blender-5.2.0/source/blender/editors/object/object_shapekey.cc
Normal file
1067
blender-5.2.0/source/blender/editors/object/object_shapekey.cc
Normal file
File diff suppressed because it is too large
Load Diff
2480
blender-5.2.0/source/blender/editors/object/object_transform.cc
Normal file
2480
blender-5.2.0/source/blender/editors/object/object_transform.cc
Normal file
File diff suppressed because it is too large
Load Diff
480
blender-5.2.0/source/blender/editors/object/object_utils.cc
Normal file
480
blender-5.2.0/source/blender/editors/object/object_utils.cc
Normal 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
|
||||
4458
blender-5.2.0/source/blender/editors/object/object_vgroup.cc
Normal file
4458
blender-5.2.0/source/blender/editors/object/object_vgroup.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -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
|
||||
183
blender-5.2.0/source/blender/editors/object/object_volume.cc
Normal file
183
blender-5.2.0/source/blender/editors/object/object_volume.cc
Normal 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
|
||||
311
blender-5.2.0/source/blender/editors/object/object_warp.cc
Normal file
311
blender-5.2.0/source/blender/editors/object/object_warp.cc
Normal 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
|
||||
Reference in New Issue
Block a user