diff options
Diffstat (limited to 'source/blender/editors/space_view3d/space_view3d.cc')
-rw-r--r-- | source/blender/editors/space_view3d/space_view3d.cc | 2150 |
1 files changed, 2150 insertions, 0 deletions
diff --git a/source/blender/editors/space_view3d/space_view3d.cc b/source/blender/editors/space_view3d/space_view3d.cc new file mode 100644 index 00000000000..635fbd75d74 --- /dev/null +++ b/source/blender/editors/space_view3d/space_view3d.cc @@ -0,0 +1,2150 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later + * Copyright 2008 Blender Foundation. All rights reserved. */ + +/** \file + * \ingroup spview3d + */ + +/* Allow using deprecated functionality for .blend file I/O. */ +#define DNA_DEPRECATED_ALLOW + +#include <cstdio> +#include <cstring> + +#include "DNA_collection_types.h" +#include "DNA_defaults.h" +#include "DNA_gpencil_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_material_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BLT_translation.h" + +#include "BKE_asset.h" +#include "BKE_context.h" +#include "BKE_curve.h" +#include "BKE_global.h" +#include "BKE_gpencil.h" +#include "BKE_icons.h" +#include "BKE_idprop.h" +#include "BKE_lattice.h" +#include "BKE_layer.h" +#include "BKE_lib_remap.h" +#include "BKE_main.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_object.h" +#include "BKE_scene.h" +#include "BKE_screen.h" +#include "BKE_viewer_path.h" +#include "BKE_workspace.h" + +#include "ED_object.h" +#include "ED_outliner.h" +#include "ED_render.h" +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_transform.h" +#include "ED_undo.h" +#include "ED_viewer_path.hh" + +#include "GPU_matrix.h" + +#include "DRW_engine.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "RE_engine.h" +#include "RE_pipeline.h" + +#include "RNA_access.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "BLO_read_write.h" + +#ifdef WITH_PYTHON +# include "BPY_extern.h" +#endif + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + +#include "view3d_intern.h" /* own include */ +#include "view3d_navigate.h" + +/* ******************** manage regions ********************* */ + +RegionView3D *ED_view3d_context_rv3d(bContext *C) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + + if (rv3d == nullptr) { + ScrArea *area = CTX_wm_area(C); + if (area && area->spacetype == SPACE_VIEW3D) { + ARegion *region = BKE_area_find_region_active_win(area); + if (region) { + rv3d = static_cast<RegionView3D *>(region->regiondata); + } + } + } + return rv3d; +} + +bool ED_view3d_context_user_region(bContext *C, View3D **r_v3d, ARegion **r_region) +{ + ScrArea *area = CTX_wm_area(C); + + *r_v3d = nullptr; + *r_region = nullptr; + + if (area && area->spacetype == SPACE_VIEW3D) { + ARegion *region = CTX_wm_region(C); + View3D *v3d = (View3D *)area->spacedata.first; + + if (region) { + RegionView3D *rv3d; + if ((region->regiontype == RGN_TYPE_WINDOW) && + (rv3d = static_cast<RegionView3D *>(region->regiondata)) && + (rv3d->viewlock & RV3D_LOCK_ROTATION) == 0) { + *r_v3d = v3d; + *r_region = region; + return true; + } + + if (ED_view3d_area_user_region(area, v3d, r_region)) { + *r_v3d = v3d; + return true; + } + } + } + + return false; +} + +bool ED_view3d_area_user_region(const ScrArea *area, const View3D *v3d, ARegion **r_region) +{ + RegionView3D *rv3d = nullptr; + ARegion *region_unlock_user = nullptr; + ARegion *region_unlock = nullptr; + const ListBase *region_list = (v3d == area->spacedata.first) ? &area->regionbase : + &v3d->regionbase; + + BLI_assert(v3d->spacetype == SPACE_VIEW3D); + + LISTBASE_FOREACH (ARegion *, region, region_list) { + /* find the first unlocked rv3d */ + if (region->regiondata && region->regiontype == RGN_TYPE_WINDOW) { + rv3d = static_cast<RegionView3D *>(region->regiondata); + if ((rv3d->viewlock & RV3D_LOCK_ROTATION) == 0) { + region_unlock = region; + if (ELEM(rv3d->persp, RV3D_PERSP, RV3D_CAMOB)) { + region_unlock_user = region; + break; + } + } + } + } + + /* camera/perspective view get priority when the active region is locked */ + if (region_unlock_user) { + *r_region = region_unlock_user; + return true; + } + + if (region_unlock) { + *r_region = region_unlock; + return true; + } + + return false; +} + +void ED_view3d_init_mats_rv3d(const struct Object *ob, struct RegionView3D *rv3d) +{ + /* local viewmat and persmat, to calculate projections */ + mul_m4_m4m4(rv3d->viewmatob, rv3d->viewmat, ob->obmat); + mul_m4_m4m4(rv3d->persmatob, rv3d->persmat, ob->obmat); + + /* initializes object space clipping, speeds up clip tests */ + ED_view3d_clipping_local(rv3d, ob->obmat); +} + +void ED_view3d_init_mats_rv3d_gl(const struct Object *ob, struct RegionView3D *rv3d) +{ + ED_view3d_init_mats_rv3d(ob, rv3d); + + /* We have to multiply instead of loading `viewmatob` to make + * it work with duplis using display-lists, otherwise it will + * override the dupli-matrix. */ + GPU_matrix_mul(ob->obmat); +} + +#ifdef DEBUG +void ED_view3d_clear_mats_rv3d(struct RegionView3D *rv3d) +{ + zero_m4(rv3d->viewmatob); + zero_m4(rv3d->persmatob); +} + +void ED_view3d_check_mats_rv3d(struct RegionView3D *rv3d) +{ + BLI_ASSERT_ZERO_M4(rv3d->viewmatob); + BLI_ASSERT_ZERO_M4(rv3d->persmatob); +} +#endif + +void ED_view3d_stop_render_preview(wmWindowManager *wm, ARegion *region) +{ + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); + + if (rv3d->render_engine) { +#ifdef WITH_PYTHON + BPy_BEGIN_ALLOW_THREADS; +#endif + + WM_jobs_kill_type(wm, region, WM_JOB_TYPE_RENDER_PREVIEW); + +#ifdef WITH_PYTHON + BPy_END_ALLOW_THREADS; +#endif + + RE_engine_free(rv3d->render_engine); + rv3d->render_engine = nullptr; + } + + /* A bit overkill but this make sure the viewport is reset completely. (fclem) */ + WM_draw_region_free(region, false); +} + +void ED_view3d_shade_update(Main *bmain, View3D *v3d, ScrArea *area) +{ + wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first); + + if (v3d->shading.type != OB_RENDER) { + ARegion *region; + + for (region = static_cast<ARegion *>(area->regionbase.first); region; region = region->next) { + if ((region->regiontype == RGN_TYPE_WINDOW) && region->regiondata) { + ED_view3d_stop_render_preview(wm, region); + } + } + } +} + +/* ******************** default callbacks for view3d space ***************** */ + +static SpaceLink *view3d_create(const ScrArea * /*area*/, const Scene *scene) +{ + ARegion *region; + View3D *v3d; + RegionView3D *rv3d; + + v3d = DNA_struct_default_alloc(View3D); + + if (scene) { + v3d->camera = scene->camera; + } + + /* header */ + region = MEM_cnew<ARegion>("header for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + + /* tool header */ + region = MEM_cnew<ARegion>("tool header for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_TOOL_HEADER; + region->alignment = (U.uiflag & USER_HEADER_BOTTOM) ? RGN_ALIGN_BOTTOM : RGN_ALIGN_TOP; + region->flag = RGN_FLAG_HIDDEN | RGN_FLAG_HIDDEN_BY_USER; + + /* tool shelf */ + region = MEM_cnew<ARegion>("toolshelf for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_TOOLS; + region->alignment = RGN_ALIGN_LEFT; + region->flag = RGN_FLAG_HIDDEN; + + /* buttons/list view */ + region = MEM_cnew<ARegion>("buttons for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_UI; + region->alignment = RGN_ALIGN_RIGHT; + region->flag = RGN_FLAG_HIDDEN; + + /* main region */ + region = MEM_cnew<ARegion>("main region for view3d"); + + BLI_addtail(&v3d->regionbase, region); + region->regiontype = RGN_TYPE_WINDOW; + + region->regiondata = MEM_cnew<RegionView3D>("region view3d"); + rv3d = static_cast<RegionView3D *>(region->regiondata); + rv3d->viewquat[0] = 1.0f; + rv3d->persp = RV3D_PERSP; + rv3d->view = RV3D_VIEW_USER; + rv3d->dist = 10.0; + + return (SpaceLink *)v3d; +} + +/* not spacelink itself */ +static void view3d_free(SpaceLink *sl) +{ + View3D *vd = (View3D *)sl; + + if (vd->localvd) { + MEM_freeN(vd->localvd); + } + + MEM_SAFE_FREE(vd->runtime.local_stats); + + if (vd->runtime.properties_storage) { + MEM_freeN(vd->runtime.properties_storage); + } + + if (vd->shading.prop) { + IDP_FreeProperty(vd->shading.prop); + vd->shading.prop = nullptr; + } + + BKE_viewer_path_clear(&vd->viewer_path); +} + +/* spacetype; init callback */ +static void view3d_init(wmWindowManager * /*wm*/, ScrArea * /*area*/) +{ +} + +static void view3d_exit(wmWindowManager * /*wm*/, ScrArea *area) +{ + BLI_assert(area->spacetype == SPACE_VIEW3D); + View3D *v3d = static_cast<View3D *>(area->spacedata.first); + MEM_SAFE_FREE(v3d->runtime.local_stats); +} + +static SpaceLink *view3d_duplicate(SpaceLink *sl) +{ + View3D *v3do = (View3D *)sl; + View3D *v3dn = static_cast<View3D *>(MEM_dupallocN(sl)); + + memset(&v3dn->runtime, 0x0, sizeof(v3dn->runtime)); + + /* clear or remove stuff from old */ + + if (v3dn->localvd) { + v3dn->localvd = nullptr; + } + + v3dn->local_collections_uuid = 0; + v3dn->flag &= ~(V3D_LOCAL_COLLECTIONS | V3D_XR_SESSION_MIRROR); + + if (v3dn->shading.type == OB_RENDER) { + v3dn->shading.type = OB_SOLID; + } + + if (v3dn->shading.prop) { + v3dn->shading.prop = IDP_CopyProperty(v3do->shading.prop); + } + + BKE_viewer_path_copy(&v3dn->viewer_path, &v3do->viewer_path); + + /* copy or clear inside new stuff */ + + return (SpaceLink *)v3dn; +} + +/* add handlers, stuff you only do once or on area/region changes */ +static void view3d_main_region_init(wmWindowManager *wm, ARegion *region) +{ + ListBase *lb; + wmKeyMap *keymap; + + /* object ops. */ + + /* important to be before Pose keymap since they can both be enabled at once */ + keymap = WM_keymap_ensure(wm->defaultconf, "Paint Face Mask (Weight, Vertex, Texture)", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Paint Vertex Selection (Weight, Vertex)", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + /* Before 'Weight/Vertex Paint' so adding curve points is not overridden. */ + keymap = WM_keymap_ensure(wm->defaultconf, "Paint Curve", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + /* Before 'Pose' so weight paint menus aren't overridden by pose menus. */ + keymap = WM_keymap_ensure(wm->defaultconf, "Weight Paint", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Vertex Paint", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + /* pose is not modal, operator poll checks for this */ + keymap = WM_keymap_ensure(wm->defaultconf, "Pose", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Object Mode", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Curve", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Image Paint", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Sculpt", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Mesh", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Armature", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Metaball", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Lattice", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Particle", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Sculpt Curves", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + /* editfont keymap swallows all... */ + keymap = WM_keymap_ensure(wm->defaultconf, "Font", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Object Non-modal", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "Frames", 0, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + /* own keymap, last so modes can override it */ + keymap = WM_keymap_ensure(wm->defaultconf, "3D View Generic", SPACE_VIEW3D, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + keymap = WM_keymap_ensure(wm->defaultconf, "3D View", SPACE_VIEW3D, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); + + /* add drop boxes */ + lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW); + + WM_event_add_dropbox_handler(®ion->handlers, lb); +} + +static void view3d_main_region_exit(wmWindowManager *wm, ARegion *region) +{ + ED_view3d_stop_render_preview(wm, region); +} + +static bool view3d_drop_in_main_region_poll(bContext *C, const wmEvent *event) +{ + ScrArea *area = CTX_wm_area(C); + return ED_region_overlap_isect_any_xy(area, event->xy) == false; +} + +static ID_Type view3d_drop_id_in_main_region_poll_get_id_type(bContext *C, + wmDrag *drag, + const wmEvent *event) +{ + const ScrArea *area = CTX_wm_area(C); + + if (ED_region_overlap_isect_any_xy(area, event->xy)) { + return ID_Type(0); + } + if (!view3d_drop_in_main_region_poll(C, event)) { + return ID_Type(0); + } + + ID *local_id = WM_drag_get_local_ID(drag, 0); + if (local_id) { + return GS(local_id->name); + } + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + if (asset_drag) { + return ID_Type(asset_drag->id_type); + } + + return ID_Type(0); +} + +static bool view3d_drop_id_in_main_region_poll(bContext *C, + wmDrag *drag, + const wmEvent *event, + ID_Type id_type) +{ + if (!view3d_drop_in_main_region_poll(C, event)) { + return false; + } + + return WM_drag_is_ID_type(drag, id_type); +} + +static void view3d_ob_drop_draw_activate(struct wmDropBox *drop, wmDrag *drag) +{ + V3DSnapCursorState *state = static_cast<V3DSnapCursorState *>(drop->draw_data); + if (state) { + return; + } + + /* Don't use the snap cursor when linking the object. Object transform isn't editable then and + * would be reset on reload. */ + if (WM_drag_asset_will_import_linked(drag)) { + return; + } + + state = static_cast<V3DSnapCursorState *>(ED_view3d_cursor_snap_active()); + drop->draw_data = state; + state->draw_plane = true; + + float dimensions[3] = {0.0f}; + if (drag->type == WM_DRAG_ID) { + Object *ob = (Object *)WM_drag_get_local_ID(drag, ID_OB); + BKE_object_dimensions_get(ob, dimensions); + } + else { + struct AssetMetaData *meta_data = WM_drag_get_asset_meta_data(drag, ID_OB); + IDProperty *dimensions_prop = BKE_asset_metadata_idprop_find(meta_data, "dimensions"); + if (dimensions_prop) { + copy_v3_v3(dimensions, static_cast<float *>(IDP_Array(dimensions_prop))); + } + } + + if (!is_zero_v3(dimensions)) { + mul_v3_v3fl(state->box_dimensions, dimensions, 0.5f); + UI_GetThemeColor4ubv(TH_GIZMO_PRIMARY, state->color_box); + state->draw_box = true; + } +} + +static void view3d_ob_drop_draw_deactivate(struct wmDropBox *drop, wmDrag * /*drag*/) +{ + V3DSnapCursorState *state = static_cast<V3DSnapCursorState *>(drop->draw_data); + if (state) { + ED_view3d_cursor_snap_deactive(state); + drop->draw_data = nullptr; + } +} + +static bool view3d_ob_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + return view3d_drop_id_in_main_region_poll(C, drag, event, ID_OB); +} +static bool view3d_ob_drop_poll_external_asset(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + +/** + * \note the term local here refers to not being an external asset, + * poll will succeed for linked library objects. + */ +static bool view3d_ob_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ob_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} + +static bool view3d_collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + return view3d_drop_id_in_main_region_poll(C, drag, event, ID_GR); +} + +static bool view3d_collection_drop_poll_local_id(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_collection_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ID)) { + return false; + } + return true; +} + +static bool view3d_collection_drop_poll_external_asset(bContext *C, + wmDrag *drag, + const wmEvent *event) +{ + if (!view3d_collection_drop_poll(C, drag, event) || (drag->type != WM_DRAG_ASSET)) { + return false; + } + return true; +} + +static bool view3d_mat_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + return view3d_drop_id_in_main_region_poll(C, drag, event, ID_MA); +} + +static char *view3d_mat_drop_tooltip(bContext *C, + wmDrag *drag, + const int xy[2], + wmDropBox * /*drop*/) +{ + const char *name = WM_drag_get_item_name(drag); + ARegion *region = CTX_wm_region(C); + const int mval[2] = { + xy[0] - region->winrct.xmin, + xy[1] - region->winrct.ymin, + }; + return ED_object_ot_drop_named_material_tooltip(C, name, mval); +} + +static bool view3d_world_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + return view3d_drop_id_in_main_region_poll(C, drag, event, ID_WO); +} + +static bool view3d_object_data_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + ID_Type id_type = view3d_drop_id_in_main_region_poll_get_id_type(C, drag, event); + if (id_type && OB_DATA_SUPPORT_ID(id_type)) { + return true; + } + return false; +} + +static char *view3d_object_data_drop_tooltip(bContext * /*C*/, + wmDrag * /*drag*/, + const int /*xy*/[2], + wmDropBox * /*drop*/) +{ + return BLI_strdup(TIP_("Create object instance from object-data")); +} + +static bool view3d_ima_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (ED_region_overlap_isect_any_xy(CTX_wm_area(C), event->xy)) { + return false; + } + if (drag->type == WM_DRAG_PATH) { + /* rule might not work? */ + return ELEM(drag->icon, 0, ICON_FILE_IMAGE, ICON_FILE_MOVIE); + } + + return WM_drag_is_ID_type(drag, ID_IM); +} + +static bool view3d_ima_bg_is_camera_view(bContext *C) +{ + RegionView3D *rv3d = CTX_wm_region_view3d(C); + if (rv3d && (rv3d->persp == RV3D_CAMOB)) { + View3D *v3d = CTX_wm_view3d(C); + if (v3d && v3d->camera && v3d->camera->type == OB_CAMERA) { + return true; + } + } + return false; +} + +static bool view3d_ima_bg_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ima_drop_poll(C, drag, event)) { + return false; + } + + if (ED_view3d_is_object_under_cursor(C, event->mval)) { + return false; + } + + return view3d_ima_bg_is_camera_view(C); +} + +static bool view3d_ima_empty_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event) +{ + if (!view3d_ima_drop_poll(C, drag, event)) { + return false; + } + + Object *ob = ED_view3d_give_object_under_cursor(C, event->mval); + + if (ob == nullptr) { + return true; + } + + if (ob->type == OB_EMPTY && ob->empty_drawtype == OB_EMPTY_IMAGE) { + return true; + } + + return false; +} + +static bool view3d_volume_drop_poll(bContext * /*C*/, wmDrag *drag, const wmEvent * /*event*/) +{ + return (drag->type == WM_DRAG_PATH) && (drag->icon == ICON_FILE_VOLUME); +} + +static void view3d_ob_drop_matrix_from_snap(V3DSnapCursorState *snap_state, + Object *ob, + float obmat_final[4][4]) +{ + V3DSnapCursorData *snap_data = ED_view3d_cursor_snap_data_get(); + BLI_assert(snap_state->draw_box || snap_state->draw_plane); + UNUSED_VARS_NDEBUG(snap_state); + copy_m4_m3(obmat_final, snap_data->plane_omat); + copy_v3_v3(obmat_final[3], snap_data->loc); + + float scale[3]; + mat4_to_size(scale, ob->obmat); + rescale_m4(obmat_final, scale); + + const BoundBox *bb = BKE_object_boundbox_get(ob); + if (bb) { + float offset[3]; + BKE_boundbox_calc_center_aabb(bb, offset); + offset[2] = bb->vec[0][2]; + mul_mat3_m4_v3(obmat_final, offset); + sub_v3_v3(obmat_final[3], offset); + } +} + +static void view3d_ob_drop_copy_local_id(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID(drag, ID_OB); + + RNA_int_set(drop->ptr, "session_uuid", id->session_uuid); + /* Don't duplicate ID's which were just imported. Only do that for existing, local IDs. */ + BLI_assert(drag->type != WM_DRAG_ASSET); + + V3DSnapCursorState *snap_state = ED_view3d_cursor_snap_state_get(); + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); +} + +/* Mostly the same logic as #view3d_collection_drop_copy_external_asset(), just different enough to + * make sharing code a bit difficult. */ +static void view3d_ob_drop_copy_external_asset(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + /* NOTE(@campbellbarton): Selection is handled here, de-selecting objects before append, + * using auto-select to ensure the new objects are selected. + * This is done so #OBJECT_OT_transform_to_mouse (which runs after this drop handler) + * can use the context setup here to place the objects. */ + BLI_assert(drag->type == WM_DRAG_ASSET); + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + bContext *C = asset_drag->evil_C; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + BKE_view_layer_base_deselect_all(scene, view_layer); + + ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + + /* TODO(sergey): Only update relations for the current scene. */ + DEG_relations_tag_update(CTX_data_main(C)); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + + RNA_int_set(drop->ptr, "session_uuid", id->session_uuid); + + BKE_view_layer_synced_ensure(scene, view_layer); + Base *base = BKE_view_layer_base_find(view_layer, (Object *)id); + if (base != nullptr) { + BKE_view_layer_base_select_and_set_active(view_layer, base); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_object_tag(C); + + V3DSnapCursorState *snap_state = static_cast<V3DSnapCursorState *>(drop->draw_data); + if (snap_state) { + float obmat_final[4][4]; + + view3d_ob_drop_matrix_from_snap(snap_state, (Object *)id, obmat_final); + + RNA_float_set_array(drop->ptr, "matrix", &obmat_final[0][0]); + } +} + +static void view3d_collection_drop_copy_local_id(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID(drag, ID_GR); + RNA_int_set(drop->ptr, "session_uuid", int(id->session_uuid)); +} + +/* Mostly the same logic as #view3d_ob_drop_copy_external_asset(), just different enough to make + * sharing code a bit difficult. */ +static void view3d_collection_drop_copy_external_asset(bContext * /*C*/, + wmDrag *drag, + wmDropBox *drop) +{ + BLI_assert(drag->type == WM_DRAG_ASSET); + + wmDragAsset *asset_drag = WM_drag_get_asset_data(drag, 0); + bContext *C = asset_drag->evil_C; + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + BKE_view_layer_base_deselect_all(scene, view_layer); + + ID *id = WM_drag_asset_id_import(asset_drag, FILE_AUTOSELECT); + Collection *collection = (Collection *)id; + + /* TODO(sergey): Only update relations for the current scene. */ + DEG_relations_tag_update(CTX_data_main(C)); + WM_event_add_notifier(C, NC_SCENE | ND_LAYER_CONTENT, scene); + + RNA_int_set(drop->ptr, "session_uuid", int(id->session_uuid)); + + /* Make an object active, just use the first one in the collection. */ + CollectionObject *cobject = static_cast<CollectionObject *>(collection->gobject.first); + BKE_view_layer_synced_ensure(scene, view_layer); + Base *base = cobject ? BKE_view_layer_base_find(view_layer, cobject->ob) : nullptr; + if (base) { + BLI_assert((base->flag & BASE_SELECTABLE) && (base->flag & BASE_ENABLED_VIEWPORT)); + BKE_view_layer_base_select_and_set_active(view_layer, base); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, scene); + } + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + ED_outliner_select_sync_from_object_tag(C); + + /* XXX Without an undo push here, there will be a crash when the user modifies operator + * properties. The stuff we do in these drop callbacks just isn't safe over undo/redo. */ + ED_undo_push(C, "Collection_Drop"); +} + +static void view3d_id_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); + + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); +} + +static void view3d_id_drop_copy_with_type(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); + + RNA_enum_set(drop->ptr, "type", GS(id->name)); + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); +} + +static void view3d_id_path_drop_copy(bContext * /*C*/, wmDrag *drag, wmDropBox *drop) +{ + ID *id = WM_drag_get_local_ID_or_import_from_asset(drag, 0); + + if (id) { + WM_operator_properties_id_lookup_set_from_id(drop->ptr, id); + RNA_struct_property_unset(drop->ptr, "filepath"); + } + else if (drag->path[0]) { + RNA_string_set(drop->ptr, "filepath", drag->path); + RNA_struct_property_unset(drop->ptr, "image"); + } +} + +static void view3d_lightcache_update(bContext *C) +{ + PointerRNA op_ptr; + + Scene *scene = CTX_data_scene(C); + + if (!BKE_scene_uses_blender_eevee(scene)) { + /* Only do auto bake if eevee is the active engine */ + return; + } + + wmOperatorType *ot = WM_operatortype_find("SCENE_OT_light_cache_bake", true); + WM_operator_properties_create_ptr(&op_ptr, ot); + RNA_int_set(&op_ptr, "delay", 200); + RNA_enum_set_identifier(C, &op_ptr, "subset", "DIRTY"); + + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &op_ptr, nullptr); + + WM_operator_properties_free(&op_ptr); +} + +/* region dropbox definition */ +static void view3d_dropboxes() +{ + ListBase *lb = WM_dropboxmap_find("View3D", SPACE_VIEW3D, RGN_TYPE_WINDOW); + + struct wmDropBox *drop; + drop = WM_dropbox_add(lb, + "OBJECT_OT_add_named", + view3d_ob_drop_poll_local_id, + view3d_ob_drop_copy_local_id, + WM_drag_free_imported_drag_ID, + nullptr); + + drop->draw_droptip = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + + drop = WM_dropbox_add(lb, + "OBJECT_OT_transform_to_mouse", + view3d_ob_drop_poll_external_asset, + view3d_ob_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + nullptr); + + drop->draw_droptip = WM_drag_draw_item_name_fn; + drop->draw_activate = view3d_ob_drop_draw_activate; + drop->draw_deactivate = view3d_ob_drop_draw_deactivate; + + WM_dropbox_add(lb, + "OBJECT_OT_collection_external_asset_drop", + view3d_collection_drop_poll_external_asset, + view3d_collection_drop_copy_external_asset, + WM_drag_free_imported_drag_ID, + nullptr); + WM_dropbox_add(lb, + "OBJECT_OT_collection_instance_add", + view3d_collection_drop_poll_local_id, + view3d_collection_drop_copy_local_id, + WM_drag_free_imported_drag_ID, + nullptr); + + WM_dropbox_add(lb, + "OBJECT_OT_drop_named_material", + view3d_mat_drop_poll, + view3d_id_drop_copy, + WM_drag_free_imported_drag_ID, + view3d_mat_drop_tooltip); + WM_dropbox_add(lb, + "VIEW3D_OT_background_image_add", + view3d_ima_bg_drop_poll, + view3d_id_path_drop_copy, + WM_drag_free_imported_drag_ID, + nullptr); + WM_dropbox_add(lb, + "OBJECT_OT_drop_named_image", + view3d_ima_empty_drop_poll, + view3d_id_path_drop_copy, + WM_drag_free_imported_drag_ID, + nullptr); + WM_dropbox_add(lb, + "OBJECT_OT_volume_import", + view3d_volume_drop_poll, + view3d_id_path_drop_copy, + WM_drag_free_imported_drag_ID, + nullptr); + WM_dropbox_add(lb, + "OBJECT_OT_data_instance_add", + view3d_object_data_drop_poll, + view3d_id_drop_copy_with_type, + WM_drag_free_imported_drag_ID, + view3d_object_data_drop_tooltip); + WM_dropbox_add(lb, + "VIEW3D_OT_drop_world", + view3d_world_drop_poll, + view3d_id_drop_copy, + WM_drag_free_imported_drag_ID, + nullptr); +} + +static void view3d_widgets() +{ + wmGizmoMapType_Params params{SPACE_VIEW3D, RGN_TYPE_WINDOW}; + wmGizmoMapType *gzmap_type = WM_gizmomaptype_ensure(¶ms); + + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_xform_gizmo_context); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_light_spot); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_light_area); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_light_target); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_force_field); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_camera_view); + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_empty_image); + /* TODO(@campbellbarton): Not working well enough, disable for now. */ +#if 0 + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_armature_spline); +#endif + + WM_gizmogrouptype_append(VIEW3D_GGT_xform_gizmo); + WM_gizmogrouptype_append(VIEW3D_GGT_xform_cage); + WM_gizmogrouptype_append(VIEW3D_GGT_xform_shear); + WM_gizmogrouptype_append(VIEW3D_GGT_xform_extrude); + WM_gizmogrouptype_append(VIEW3D_GGT_mesh_preselect_elem); + WM_gizmogrouptype_append(VIEW3D_GGT_mesh_preselect_edgering); + WM_gizmogrouptype_append(VIEW3D_GGT_tool_generic_handle_normal); + WM_gizmogrouptype_append(VIEW3D_GGT_tool_generic_handle_free); + + WM_gizmogrouptype_append(VIEW3D_GGT_ruler); + WM_gizmotype_append(VIEW3D_GT_ruler_item); + + WM_gizmogrouptype_append(VIEW3D_GGT_placement); + + WM_gizmogrouptype_append_and_link(gzmap_type, VIEW3D_GGT_navigate); + WM_gizmotype_append(VIEW3D_GT_navigate_rotate); +} + +/* type callback, not region itself */ +static void view3d_main_region_free(ARegion *region) +{ + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); + + if (rv3d) { + if (rv3d->localvd) { + MEM_freeN(rv3d->localvd); + } + if (rv3d->clipbb) { + MEM_freeN(rv3d->clipbb); + } + + if (rv3d->render_engine) { + RE_engine_free(rv3d->render_engine); + } + + if (rv3d->sms) { + MEM_freeN(rv3d->sms); + } + + MEM_freeN(rv3d); + region->regiondata = nullptr; + } +} + +/* copy regiondata */ +static void *view3d_main_region_duplicate(void *poin) +{ + if (poin) { + RegionView3D *rv3d = static_cast<RegionView3D *>(poin); + RegionView3D *new_rv3d; + + new_rv3d = static_cast<RegionView3D *>(MEM_dupallocN(rv3d)); + if (rv3d->localvd) { + new_rv3d->localvd = static_cast<RegionView3D *>(MEM_dupallocN(rv3d->localvd)); + } + if (rv3d->clipbb) { + new_rv3d->clipbb = static_cast<BoundBox *>(MEM_dupallocN(rv3d->clipbb)); + } + + new_rv3d->render_engine = nullptr; + new_rv3d->sms = nullptr; + new_rv3d->smooth_timer = nullptr; + + return new_rv3d; + } + return nullptr; +} + +static void view3d_main_region_listener(const wmRegionListenerParams *params) +{ + wmWindow *window = params->window; + ScrArea *area = params->area; + ARegion *region = params->region; + const wmNotifier *wmn = params->notifier; + const Scene *scene = params->scene; + View3D *v3d = static_cast<View3D *>(area->spacedata.first); + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); + wmGizmoMap *gzmap = region->gizmo_map; + + /* context changes */ + switch (wmn->category) { + case NC_WM: + if (ELEM(wmn->data, ND_UNDO)) { + WM_gizmomap_tag_refresh(gzmap); + } + else if (ELEM(wmn->data, ND_XR_DATA_CHANGED)) { + /* Only cause a redraw if this a VR session mirror. Should more features be added that + * require redraws, we could pass something to wmn->reference, e.g. the flag value. */ + if (v3d->flag & V3D_XR_SESSION_MIRROR) { + ED_region_tag_redraw(region); + } + } + break; + case NC_ANIMATION: + switch (wmn->data) { + case ND_KEYFRAME_PROP: + case ND_NLA_ACTCHANGE: + ED_region_tag_redraw(region); + break; + case ND_NLA: + case ND_KEYFRAME: + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) { + ED_region_tag_redraw(region); + } + break; + case ND_ANIMCHAN: + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED, NA_SELECTED)) { + ED_region_tag_redraw(region); + } + break; + } + break; + case NC_SCENE: + switch (wmn->data) { + case ND_SCENEBROWSE: + case ND_LAYER_CONTENT: + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + break; + case ND_LAYER: + if (wmn->reference) { + BKE_screen_view3d_sync(v3d, static_cast<Scene *>(wmn->reference)); + } + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + break; + case ND_OB_ACTIVE: + case ND_OB_SELECT: + ATTR_FALLTHROUGH; + case ND_FRAME: + case ND_TRANSFORM: + case ND_OB_VISIBLE: + case ND_RENDER_OPTIONS: + case ND_MARKERS: + case ND_MODE: + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + break; + case ND_WORLD: + /* handled by space_view3d_listener() for v3d access */ + break; + case ND_DRAW_RENDER_VIEWPORT: { + if (v3d->camera && (scene == wmn->reference)) { + if (rv3d->persp == RV3D_CAMOB) { + ED_region_tag_redraw(region); + } + } + break; + } + } + if (wmn->action == NA_EDITED) { + ED_region_tag_redraw(region); + } + break; + case NC_OBJECT: + switch (wmn->data) { + case ND_BONE_ACTIVE: + case ND_BONE_SELECT: + case ND_TRANSFORM: + case ND_POSE: + case ND_DRAW: + case ND_MODIFIER: + case ND_SHADERFX: + case ND_CONSTRAINT: + case ND_KEYS: + case ND_PARTICLE: + case ND_POINTCACHE: + case ND_LOD: + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + break; + case ND_DRAW_ANIMVIZ: + ED_region_tag_redraw(region); + break; + } + switch (wmn->action) { + case NA_ADDED: + ED_region_tag_redraw(region); + break; + } + break; + case NC_GEOM: + switch (wmn->data) { + case ND_SELECT: { + WM_gizmomap_tag_refresh(gzmap); + ATTR_FALLTHROUGH; + } + case ND_DATA: + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + break; + case ND_VERTEX_GROUP: + ED_region_tag_redraw(region); + break; + } + switch (wmn->action) { + case NA_EDITED: + ED_region_tag_redraw(region); + break; + } + break; + case NC_CAMERA: + switch (wmn->data) { + case ND_DRAW_RENDER_VIEWPORT: { + if (v3d->camera && (v3d->camera->data == wmn->reference)) { + if (rv3d->persp == RV3D_CAMOB) { + ED_region_tag_redraw(region); + } + } + break; + } + } + break; + case NC_GROUP: + /* all group ops for now */ + ED_region_tag_redraw(region); + break; + case NC_BRUSH: + switch (wmn->action) { + case NA_EDITED: + ED_region_tag_redraw_cursor(region); + break; + case NA_SELECTED: + /* used on brush changes - needed because 3d cursor + * has to be drawn if clone brush is selected */ + ED_region_tag_redraw(region); + break; + } + break; + case NC_MATERIAL: + switch (wmn->data) { + case ND_SHADING: + case ND_NODES: + /* TODO(sergey): This is a bit too much updates, but needed to + * have proper material drivers update in the viewport. + * + * How to solve? + */ + ED_region_tag_redraw(region); + break; + case ND_SHADING_DRAW: + case ND_SHADING_LINKS: + ED_region_tag_redraw(region); + break; + } + break; + case NC_NODE: + ED_region_tag_redraw(region); + break; + case NC_WORLD: + switch (wmn->data) { + case ND_WORLD_DRAW: + /* handled by space_view3d_listener() for v3d access */ + break; + case ND_WORLD: + /* Needed for updating world materials */ + ED_region_tag_redraw(region); + break; + } + break; + case NC_LAMP: + switch (wmn->data) { + case ND_LIGHTING: + /* TODO(sergey): This is a bit too much, but needed to + * handle updates from new depsgraph. + */ + ED_region_tag_redraw(region); + break; + case ND_LIGHTING_DRAW: + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + break; + } + break; + case NC_LIGHTPROBE: + ED_area_tag_refresh(area); + break; + case NC_IMAGE: + /* this could be more fine grained checks if we had + * more context than just the region */ + ED_region_tag_redraw(region); + break; + case NC_TEXTURE: + /* same as above */ + ED_region_tag_redraw(region); + break; + case NC_MOVIECLIP: + if (wmn->data == ND_DISPLAY || wmn->action == NA_EDITED) { + ED_region_tag_redraw(region); + } + break; + case NC_SPACE: + if (wmn->data == ND_SPACE_VIEW3D) { + if (wmn->subtype == NS_VIEW3D_GPU) { + rv3d->rflag |= RV3D_GPULIGHT_UPDATE; + } + else if (wmn->subtype == NS_VIEW3D_SHADING) { +#ifdef WITH_XR_OPENXR + ED_view3d_xr_shading_update( + static_cast<wmWindowManager *>(G_MAIN->wm.first), v3d, scene); +#endif + + ViewLayer *view_layer = WM_window_get_active_view_layer(window); + Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer); + if (depsgraph) { + ED_render_view3d_update(depsgraph, window, area, true); + } + } + ED_region_tag_redraw(region); + WM_gizmomap_tag_refresh(gzmap); + } + break; + case NC_ID: + if (ELEM(wmn->action, NA_RENAME, NA_EDITED, NA_ADDED, NA_REMOVED)) { + ED_region_tag_redraw(region); + } + break; + case NC_SCREEN: + switch (wmn->data) { + case ND_ANIMPLAY: + case ND_SKETCH: + ED_region_tag_redraw(region); + break; + case ND_LAYOUTBROWSE: + case ND_LAYOUTDELETE: + case ND_LAYOUTSET: + WM_gizmomap_tag_refresh(gzmap); + ED_region_tag_redraw(region); + break; + case ND_LAYER: + ED_region_tag_redraw(region); + break; + } + + break; + case NC_GPENCIL: + if (wmn->data == ND_DATA || ELEM(wmn->action, NA_EDITED, NA_SELECTED)) { + ED_region_tag_redraw(region); + } + break; + case NC_WORKSPACE: + /* In case the region displays workspace settings. */ + ED_region_tag_redraw(region); + break; + case NC_VIEWER_PATH: { + if (v3d->flag2 & V3D_SHOW_VIEWER) { + ViewLayer *view_layer = WM_window_get_active_view_layer(window); + if (Depsgraph *depsgraph = BKE_scene_get_depsgraph(scene, view_layer)) { + ED_render_view3d_update(depsgraph, window, area, true); + } + ED_region_tag_redraw(region); + } + break; + } + } +} + +static void view3d_do_msg_notify_workbench_view_update(struct bContext *C, + struct wmMsgSubscribeKey * /*msg_key*/, + struct wmMsgSubscribeValue *msg_val) +{ + Scene *scene = CTX_data_scene(C); + ScrArea *area = (ScrArea *)msg_val->user_data; + View3D *v3d = (View3D *)area->spacedata.first; + if (v3d->shading.type == OB_SOLID) { + RenderEngineType *engine_type = ED_view3d_engine_type(scene, v3d->shading.type); + DRWUpdateContext drw_context = {nullptr}; + drw_context.bmain = CTX_data_main(C); + drw_context.depsgraph = CTX_data_depsgraph_pointer(C); + drw_context.scene = scene; + drw_context.view_layer = CTX_data_view_layer(C); + drw_context.region = (ARegion *)(msg_val->owner); + drw_context.v3d = v3d; + drw_context.engine_type = engine_type; + DRW_notify_view_update(&drw_context); + } +} + +static void view3d_main_region_message_subscribe(const wmRegionMessageSubscribeParams *params) +{ + struct wmMsgBus *mbus = params->message_bus; + const bContext *C = params->context; + ScrArea *area = params->area; + ARegion *region = params->region; + + /* Developer NOTE: there are many properties that impact 3D view drawing, + * so instead of subscribing to individual properties, just subscribe to types + * accepting some redundant redraws. + * + * For other space types we might try avoid this, keep the 3D view as an exceptional case! */ + wmMsgParams_RNA msg_key_params{}; + + /* Only subscribe to types. */ + StructRNA *type_array[] = { + &RNA_Window, + + /* These object have properties that impact drawing. */ + &RNA_AreaLight, + &RNA_Camera, + &RNA_Light, + &RNA_Speaker, + &RNA_SunLight, + + /* General types the 3D view depends on. */ + &RNA_Object, + &RNA_UnitSettings, /* grid-floor */ + + &RNA_View3DCursor, + &RNA_View3DOverlay, + &RNA_View3DShading, + &RNA_World, + }; + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; + + wmMsgSubscribeValue msg_sub_value_workbench_view_update{}; + msg_sub_value_workbench_view_update.owner = region; + msg_sub_value_workbench_view_update.user_data = area; + msg_sub_value_workbench_view_update.notify = view3d_do_msg_notify_workbench_view_update; + + for (int i = 0; i < ARRAY_SIZE(type_array); i++) { + msg_key_params.ptr.type = type_array[i]; + WM_msg_subscribe_rna_params(mbus, &msg_key_params, &msg_sub_value_region_tag_redraw, __func__); + } + + /* Subscribe to a handful of other properties. */ + RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata); + + WM_msg_subscribe_rna_anon_prop(mbus, RenderSettings, engine, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop( + mbus, RenderSettings, resolution_x, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop( + mbus, RenderSettings, resolution_y, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop( + mbus, RenderSettings, pixel_aspect_x, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_prop( + mbus, RenderSettings, pixel_aspect_y, &msg_sub_value_region_tag_redraw); + if (rv3d->persp == RV3D_CAMOB) { + WM_msg_subscribe_rna_anon_prop( + mbus, RenderSettings, use_border, &msg_sub_value_region_tag_redraw); + } + + WM_msg_subscribe_rna_anon_type(mbus, SceneEEVEE, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_type(mbus, SceneDisplay, &msg_sub_value_region_tag_redraw); + WM_msg_subscribe_rna_anon_type(mbus, ObjectDisplay, &msg_sub_value_region_tag_redraw); + + const Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); + Object *obact = BKE_view_layer_active_object_get(view_layer); + if (obact != nullptr) { + switch (obact->mode) { + case OB_MODE_PARTICLE_EDIT: + WM_msg_subscribe_rna_anon_type(mbus, ParticleEdit, &msg_sub_value_region_tag_redraw); + break; + + case OB_MODE_SCULPT: + WM_msg_subscribe_rna_anon_prop( + mbus, WorkSpace, tools, &msg_sub_value_workbench_view_update); + break; + default: + break; + } + } + + { + wmMsgSubscribeValue msg_sub_value_region_tag_refresh{}; + msg_sub_value_region_tag_refresh.owner = region; + msg_sub_value_region_tag_refresh.user_data = area; + msg_sub_value_region_tag_refresh.notify = WM_toolsystem_do_msg_notify_tag_refresh; + WM_msg_subscribe_rna_anon_prop(mbus, Object, mode, &msg_sub_value_region_tag_refresh); + WM_msg_subscribe_rna_anon_prop(mbus, LayerObjects, active, &msg_sub_value_region_tag_refresh); + } +} + +/* concept is to retrieve cursor type context-less */ +static void view3d_main_region_cursor(wmWindow *win, ScrArea *area, ARegion *region) +{ + if (WM_cursor_set_from_tool(win, area, region)) { + return; + } + + Scene *scene = WM_window_get_active_scene(win); + ViewLayer *view_layer = WM_window_get_active_view_layer(win); + BKE_view_layer_synced_ensure(scene, view_layer); + Object *obedit = BKE_view_layer_edit_object_get(view_layer); + if (obedit) { + WM_cursor_set(win, WM_CURSOR_EDIT); + } + else { + WM_cursor_set(win, WM_CURSOR_DEFAULT); + } +} + +/* add handlers, stuff you only do once or on area/region changes */ +static void view3d_header_region_init(wmWindowManager *wm, ARegion *region) +{ + wmKeyMap *keymap = WM_keymap_ensure(wm->defaultconf, "3D View Generic", SPACE_VIEW3D, 0); + + WM_event_add_keymap_handler(®ion->handlers, keymap); + + ED_region_header_init(region); +} + +static void view3d_header_region_draw(const bContext *C, ARegion *region) +{ + ED_region_header(C, region); +} + +static void view3d_header_region_listener(const wmRegionListenerParams *params) +{ + ARegion *region = params->region; + const wmNotifier *wmn = params->notifier; + + /* context changes */ + switch (wmn->category) { + case NC_SCENE: + switch (wmn->data) { + case ND_FRAME: + case ND_OB_ACTIVE: + case ND_OB_SELECT: + case ND_OB_VISIBLE: + case ND_MODE: + case ND_LAYER: + case ND_TOOLSETTINGS: + case ND_LAYER_CONTENT: + case ND_RENDER_OPTIONS: + ED_region_tag_redraw(region); + break; + } + break; + case NC_SPACE: + if (wmn->data == ND_SPACE_VIEW3D) { + ED_region_tag_redraw(region); + } + break; + case NC_GPENCIL: + if (wmn->data & ND_GPENCIL_EDITMODE) { + ED_region_tag_redraw(region); + } + else if (wmn->action == NA_EDITED) { + ED_region_tag_redraw(region); + } + break; + case NC_BRUSH: + ED_region_tag_redraw(region); + break; + } + + /* From top-bar, which ones are needed? split per header? */ + /* Disable for now, re-enable if needed, or remove - campbell. */ +#if 0 + /* context changes */ + switch (wmn->category) { + case NC_WM: + if (wmn->data == ND_HISTORY) { + ED_region_tag_redraw(region); + } + break; + case NC_SCENE: + if (wmn->data == ND_MODE) { + ED_region_tag_redraw(region); + } + break; + case NC_SPACE: + if (wmn->data == ND_SPACE_VIEW3D) { + ED_region_tag_redraw(region); + } + break; + case NC_GPENCIL: + if (wmn->data == ND_DATA) { + ED_region_tag_redraw(region); + } + break; + } +#endif +} + +static void view3d_header_region_message_subscribe(const wmRegionMessageSubscribeParams *params) +{ + struct wmMsgBus *mbus = params->message_bus; + ARegion *region = params->region; + + wmMsgParams_RNA msg_key_params{}; + + /* Only subscribe to types. */ + StructRNA *type_array[] = { + &RNA_View3DShading, + }; + + wmMsgSubscribeValue msg_sub_value_region_tag_redraw{}; + msg_sub_value_region_tag_redraw.owner = region; + msg_sub_value_region_tag_redraw.user_data = region; + msg_sub_value_region_tag_redraw.notify = ED_region_do_msg_notify_tag_redraw; + + for (int i = 0; i < ARRAY_SIZE(type_array); i++) { + msg_key_params.ptr.type = type_array[i]; + WM_msg_subscribe_rna_params(mbus, &msg_key_params, &msg_sub_value_region_tag_redraw, __func__); + } +} + +/* add handlers, stuff you only do once or on area/region changes */ +static void view3d_buttons_region_init(wmWindowManager *wm, ARegion *region) +{ + wmKeyMap *keymap; + + ED_region_panels_init(wm, region); + + keymap = WM_keymap_ensure(wm->defaultconf, "3D View Generic", SPACE_VIEW3D, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); +} + +void ED_view3d_buttons_region_layout_ex(const bContext *C, + ARegion *region, + const char *category_override) +{ + const enum eContextObjectMode mode = CTX_data_mode_enum(C); + + const char *contexts_base[4] = {nullptr}; + contexts_base[0] = CTX_data_mode_string(C); + + const char **contexts = &contexts_base[1]; + + switch (mode) { + case CTX_MODE_EDIT_MESH: + ARRAY_SET_ITEMS(contexts, ".mesh_edit"); + break; + case CTX_MODE_EDIT_CURVE: + ARRAY_SET_ITEMS(contexts, ".curve_edit"); + break; + case CTX_MODE_EDIT_CURVES: + ARRAY_SET_ITEMS(contexts, ".curves_edit"); + break; + case CTX_MODE_EDIT_SURFACE: + ARRAY_SET_ITEMS(contexts, ".curve_edit"); + break; + case CTX_MODE_EDIT_TEXT: + ARRAY_SET_ITEMS(contexts, ".text_edit"); + break; + case CTX_MODE_EDIT_ARMATURE: + ARRAY_SET_ITEMS(contexts, ".armature_edit"); + break; + case CTX_MODE_EDIT_METABALL: + ARRAY_SET_ITEMS(contexts, ".mball_edit"); + break; + case CTX_MODE_EDIT_LATTICE: + ARRAY_SET_ITEMS(contexts, ".lattice_edit"); + break; + case CTX_MODE_POSE: + ARRAY_SET_ITEMS(contexts, ".posemode"); + break; + case CTX_MODE_SCULPT: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".sculpt_mode"); + break; + case CTX_MODE_PAINT_WEIGHT: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".weightpaint"); + break; + case CTX_MODE_PAINT_VERTEX: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".vertexpaint"); + break; + case CTX_MODE_PAINT_TEXTURE: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".imagepaint"); + break; + case CTX_MODE_PARTICLE: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".particlemode"); + break; + case CTX_MODE_OBJECT: + ARRAY_SET_ITEMS(contexts, ".objectmode"); + break; + case CTX_MODE_PAINT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_paint"); + break; + case CTX_MODE_SCULPT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_sculpt"); + break; + case CTX_MODE_WEIGHT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); + break; + case CTX_MODE_VERTEX_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); + break; + case CTX_MODE_SCULPT_CURVES: + ARRAY_SET_ITEMS(contexts, ".paint_common", ".curves_sculpt"); + break; + default: + break; + } + + switch (mode) { + case CTX_MODE_PAINT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_paint"); + break; + case CTX_MODE_SCULPT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_sculpt"); + break; + case CTX_MODE_WEIGHT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_weight"); + break; + case CTX_MODE_EDIT_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_edit"); + break; + case CTX_MODE_VERTEX_GPENCIL: + ARRAY_SET_ITEMS(contexts, ".greasepencil_vertex"); + break; + default: + break; + } + + ListBase *paneltypes = ®ion->type->paneltypes; + + /* Allow drawing 3D view toolbar from non 3D view space type. */ + if (category_override != nullptr) { + SpaceType *st = BKE_spacetype_from_id(SPACE_VIEW3D); + ARegionType *art = BKE_regiontype_from_id(st, RGN_TYPE_UI); + paneltypes = &art->paneltypes; + } + + ED_region_panels_layout_ex(C, region, paneltypes, contexts_base, category_override); +} + +static void view3d_buttons_region_layout(const bContext *C, ARegion *region) +{ + ED_view3d_buttons_region_layout_ex(C, region, nullptr); +} + +static void view3d_buttons_region_listener(const wmRegionListenerParams *params) +{ + ARegion *region = params->region; + const wmNotifier *wmn = params->notifier; + + /* context changes */ + switch (wmn->category) { + case NC_ANIMATION: + switch (wmn->data) { + case ND_KEYFRAME_PROP: + case ND_NLA_ACTCHANGE: + ED_region_tag_redraw(region); + break; + case ND_NLA: + case ND_KEYFRAME: + if (ELEM(wmn->action, NA_EDITED, NA_ADDED, NA_REMOVED)) { + ED_region_tag_redraw(region); + } + break; + } + break; + case NC_SCENE: + switch (wmn->data) { + case ND_FRAME: + case ND_OB_ACTIVE: + case ND_OB_SELECT: + case ND_OB_VISIBLE: + case ND_MODE: + case ND_LAYER: + case ND_LAYER_CONTENT: + case ND_TOOLSETTINGS: + ED_region_tag_redraw(region); + break; + } + switch (wmn->action) { + case NA_EDITED: + ED_region_tag_redraw(region); + break; + } + break; + case NC_OBJECT: + switch (wmn->data) { + case ND_BONE_ACTIVE: + case ND_BONE_SELECT: + case ND_TRANSFORM: + case ND_POSE: + case ND_DRAW: + case ND_KEYS: + case ND_MODIFIER: + case ND_SHADERFX: + ED_region_tag_redraw(region); + break; + } + break; + case NC_GEOM: + switch (wmn->data) { + case ND_DATA: + case ND_VERTEX_GROUP: + case ND_SELECT: + ED_region_tag_redraw(region); + break; + } + if (wmn->action == NA_EDITED) { + ED_region_tag_redraw(region); + } + break; + case NC_TEXTURE: + case NC_MATERIAL: + /* for brush textures */ + ED_region_tag_redraw(region); + break; + case NC_BRUSH: + /* NA_SELECTED is used on brush changes */ + if (ELEM(wmn->action, NA_EDITED, NA_SELECTED)) { + ED_region_tag_redraw(region); + } + break; + case NC_SPACE: + if (wmn->data == ND_SPACE_VIEW3D) { + ED_region_tag_redraw(region); + } + break; + case NC_ID: + if (wmn->action == NA_RENAME) { + ED_region_tag_redraw(region); + } + break; + case NC_GPENCIL: + if ((wmn->data & (ND_DATA | ND_GPENCIL_EDITMODE)) || (wmn->action == NA_EDITED)) { + ED_region_tag_redraw(region); + } + break; + case NC_IMAGE: + /* Update for the image layers in texture paint. */ + if (wmn->action == NA_EDITED) { + ED_region_tag_redraw(region); + } + break; + case NC_WM: + if (wmn->data == ND_XR_DATA_CHANGED) { + ED_region_tag_redraw(region); + } + break; + } +} + +/* add handlers, stuff you only do once or on area/region changes */ +static void view3d_tools_region_init(wmWindowManager *wm, ARegion *region) +{ + wmKeyMap *keymap; + + ED_region_panels_init(wm, region); + + keymap = WM_keymap_ensure(wm->defaultconf, "3D View Generic", SPACE_VIEW3D, 0); + WM_event_add_keymap_handler(®ion->handlers, keymap); +} + +static void view3d_tools_region_draw(const bContext *C, ARegion *region) +{ + const char *contexts[] = {CTX_data_mode_string(C), nullptr}; + ED_region_panels_ex(C, region, contexts); +} + +/* area (not region) level listener */ +static void space_view3d_listener(const wmSpaceTypeListenerParams *params) +{ + ScrArea *area = params->area; + const wmNotifier *wmn = params->notifier; + View3D *v3d = static_cast<View3D *>(area->spacedata.first); + + /* context changes */ + switch (wmn->category) { + case NC_SCENE: + switch (wmn->data) { + case ND_WORLD: { + const bool use_scene_world = V3D_USES_SCENE_WORLD(v3d); + if (v3d->flag2 & V3D_HIDE_OVERLAYS || use_scene_world) { + ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); + } + break; + } + } + break; + case NC_WORLD: + switch (wmn->data) { + case ND_WORLD_DRAW: + case ND_WORLD: + if (v3d->shading.background_type == V3D_SHADING_BACKGROUND_WORLD) { + ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); + } + break; + } + break; + case NC_MATERIAL: + switch (wmn->data) { + case ND_NODES: + if (v3d->shading.type == OB_TEXTURE) { + ED_area_tag_redraw_regiontype(area, RGN_TYPE_WINDOW); + } + break; + } + break; + } +} + +static void space_view3d_refresh(const bContext *C, ScrArea *area) +{ + Scene *scene = CTX_data_scene(C); + LightCache *lcache = scene->eevee.light_cache_data; + + if (lcache && (lcache->flag & LIGHTCACHE_UPDATE_AUTO) != 0) { + lcache->flag &= ~LIGHTCACHE_UPDATE_AUTO; + view3d_lightcache_update((bContext *)C); + } + + View3D *v3d = (View3D *)area->spacedata.first; + MEM_SAFE_FREE(v3d->runtime.local_stats); +} + +const char *view3d_context_dir[] = { + "active_object", + "selected_ids", + nullptr, +}; + +static int view3d_context(const bContext *C, const char *member, bContextDataResult *result) +{ + /* fallback to the scene layer, + * allows duplicate and other object operators to run outside the 3d view */ + + if (CTX_data_dir(member)) { + CTX_data_dir_set(result, view3d_context_dir); + return CTX_RESULT_OK; + } + if (CTX_data_equals(member, "active_object")) { + /* In most cases the active object is the `view_layer->basact->object`. + * For the 3D view however it can be nullptr when hidden. + * + * This is ignored in the case the object is in any mode (besides object-mode), + * since the object's mode impacts the current tool, cursor, gizmos etc. + * If we didn't have this exception, changing visibility would need to perform + * many of the same updates as changing the objects mode. + * + * Further, there are multiple ways to hide objects - by collection, by object type, etc. + * it's simplest if all these methods behave consistently - respecting the object-mode + * without showing the object. + * + * See T85532 for alternatives that were considered. */ + const Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + BKE_view_layer_synced_ensure(scene, view_layer); + Base *base = BKE_view_layer_active_base_get(view_layer); + if (base) { + Object *ob = base->object; + /* if hidden but in edit mode, we still display, can happen with animation */ + if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0 || + (ob->mode != OB_MODE_OBJECT)) { + CTX_data_id_pointer_set(result, &ob->id); + } + } + + return CTX_RESULT_OK; + } + if (CTX_data_equals(member, "selected_ids")) { + ListBase selected_objects; + CTX_data_selected_objects(C, &selected_objects); + LISTBASE_FOREACH (CollectionPointerLink *, object_ptr_link, &selected_objects) { + ID *selected_id = object_ptr_link->ptr.owner_id; + CTX_data_id_list_add(result, selected_id); + } + BLI_freelistN(&selected_objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); + return CTX_RESULT_OK; + } + + return CTX_RESULT_MEMBER_NOT_FOUND; +} + +static void view3d_id_remap_v3d_ob_centers(View3D *v3d, const struct IDRemapper *mappings) +{ + if (BKE_id_remapper_apply(mappings, (ID **)&v3d->ob_center, ID_REMAP_APPLY_DEFAULT) == + ID_REMAP_RESULT_SOURCE_UNASSIGNED) { + /* Otherwise, bone-name may remain valid... + * We could be smart and check this, too? */ + v3d->ob_center_bone[0] = '\0'; + } +} + +static void view3d_id_remap_v3d(ScrArea *area, + SpaceLink *slink, + View3D *v3d, + const struct IDRemapper *mappings, + const bool is_local) +{ + ARegion *region; + if (BKE_id_remapper_apply(mappings, (ID **)&v3d->camera, ID_REMAP_APPLY_DEFAULT) == + ID_REMAP_RESULT_SOURCE_UNASSIGNED) { + /* 3D view might be inactive, in that case needs to use slink->regionbase */ + ListBase *regionbase = (slink == area->spacedata.first) ? &area->regionbase : + &slink->regionbase; + for (region = static_cast<ARegion *>(regionbase->first); region; region = region->next) { + if (region->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = is_local ? ((RegionView3D *)region->regiondata)->localvd : + static_cast<RegionView3D *>(region->regiondata); + if (rv3d && (rv3d->persp == RV3D_CAMOB)) { + rv3d->persp = RV3D_PERSP; + } + } + } + } +} + +static void view3d_id_remap(ScrArea *area, SpaceLink *slink, const struct IDRemapper *mappings) +{ + + if (!BKE_id_remapper_has_mapping_for( + mappings, FILTER_ID_OB | FILTER_ID_MA | FILTER_ID_IM | FILTER_ID_MC)) { + return; + } + + View3D *view3d = (View3D *)slink; + view3d_id_remap_v3d(area, slink, view3d, mappings, false); + view3d_id_remap_v3d_ob_centers(view3d, mappings); + if (view3d->localvd != nullptr) { + /* Object centers in local-view aren't used, see: T52663 */ + view3d_id_remap_v3d(area, slink, view3d->localvd, mappings, true); + } + BKE_viewer_path_id_remap(&view3d->viewer_path, mappings); +} + +static void view3d_blend_read_data(BlendDataReader *reader, SpaceLink *sl) +{ + View3D *v3d = (View3D *)sl; + + memset(&v3d->runtime, 0x0, sizeof(v3d->runtime)); + + if (v3d->gpd) { + BLO_read_data_address(reader, &v3d->gpd); + BKE_gpencil_blend_read_data(reader, v3d->gpd); + } + BLO_read_data_address(reader, &v3d->localvd); + + /* render can be quite heavy, set to solid on load */ + if (v3d->shading.type == OB_RENDER) { + v3d->shading.type = OB_SOLID; + } + v3d->shading.prev_type = OB_SOLID; + + BKE_screen_view3d_shading_blend_read_data(reader, &v3d->shading); + + BKE_screen_view3d_do_versions_250(v3d, &sl->regionbase); + + BKE_viewer_path_blend_read_data(reader, &v3d->viewer_path); +} + +static void view3d_blend_read_lib(BlendLibReader *reader, ID *parent_id, SpaceLink *sl) +{ + View3D *v3d = (View3D *)sl; + + BLO_read_id_address(reader, parent_id->lib, &v3d->camera); + BLO_read_id_address(reader, parent_id->lib, &v3d->ob_center); + + if (v3d->localvd) { + BLO_read_id_address(reader, parent_id->lib, &v3d->localvd->camera); + } + + BKE_viewer_path_blend_read_lib(reader, parent_id->lib, &v3d->viewer_path); +} + +static void view3d_blend_write(BlendWriter *writer, SpaceLink *sl) +{ + View3D *v3d = (View3D *)sl; + BLO_write_struct(writer, View3D, v3d); + + if (v3d->localvd) { + BLO_write_struct(writer, View3D, v3d->localvd); + } + + BKE_screen_view3d_shading_blend_write(writer, &v3d->shading); + + BKE_viewer_path_blend_write(writer, &v3d->viewer_path); +} + +void ED_spacetype_view3d() +{ + SpaceType *st = MEM_cnew<SpaceType>("spacetype view3d"); + ARegionType *art; + + st->spaceid = SPACE_VIEW3D; + STRNCPY(st->name, "View3D"); + + st->create = view3d_create; + st->free = view3d_free; + st->init = view3d_init; + st->exit = view3d_exit; + st->listener = space_view3d_listener; + st->refresh = space_view3d_refresh; + st->duplicate = view3d_duplicate; + st->operatortypes = view3d_operatortypes; + st->keymap = view3d_keymap; + st->dropboxes = view3d_dropboxes; + st->gizmos = view3d_widgets; + st->context = view3d_context; + st->id_remap = view3d_id_remap; + st->blend_read_data = view3d_blend_read_data; + st->blend_read_lib = view3d_blend_read_lib; + st->blend_write = view3d_blend_write; + + /* regions: main window */ + art = MEM_cnew<ARegionType>("spacetype view3d main region"); + art->regionid = RGN_TYPE_WINDOW; + art->keymapflag = ED_KEYMAP_GIZMO | ED_KEYMAP_TOOL | ED_KEYMAP_GPENCIL; + art->draw = view3d_main_region_draw; + art->init = view3d_main_region_init; + art->exit = view3d_main_region_exit; + art->free = view3d_main_region_free; + art->duplicate = view3d_main_region_duplicate; + art->listener = view3d_main_region_listener; + art->message_subscribe = view3d_main_region_message_subscribe; + art->cursor = view3d_main_region_cursor; + art->lock = 1; /* can become flag, see BKE_spacedata_draw_locks */ + BLI_addhead(&st->regiontypes, art); + + /* regions: listview/buttons */ + art = MEM_cnew<ARegionType>("spacetype view3d buttons region"); + art->regionid = RGN_TYPE_UI; + art->prefsizex = UI_SIDEBAR_PANEL_WIDTH; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + art->listener = view3d_buttons_region_listener; + art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_ui; + art->init = view3d_buttons_region_init; + art->layout = view3d_buttons_region_layout; + art->draw = ED_region_panels_draw; + BLI_addhead(&st->regiontypes, art); + + view3d_buttons_register(art); + + /* regions: tool(bar) */ + art = MEM_cnew<ARegionType>("spacetype view3d tools region"); + art->regionid = RGN_TYPE_TOOLS; + art->prefsizex = 58; /* XXX */ + art->prefsizey = 50; /* XXX */ + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_FRAMES; + art->listener = view3d_buttons_region_listener; + art->message_subscribe = ED_region_generic_tools_region_message_subscribe; + art->snap_size = ED_region_generic_tools_region_snap_size; + art->init = view3d_tools_region_init; + art->draw = view3d_tools_region_draw; + BLI_addhead(&st->regiontypes, art); + + /* regions: tool header */ + art = MEM_cnew<ARegionType>("spacetype view3d tool header region"); + art->regionid = RGN_TYPE_TOOL_HEADER; + art->prefsizey = HEADERY; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->listener = view3d_header_region_listener; + art->message_subscribe = ED_area_do_mgs_subscribe_for_tool_header; + art->init = view3d_header_region_init; + art->draw = view3d_header_region_draw; + BLI_addhead(&st->regiontypes, art); + + /* regions: header */ + art = MEM_cnew<ARegionType>("spacetype view3d header region"); + art->regionid = RGN_TYPE_HEADER; + art->prefsizey = HEADERY; + art->keymapflag = ED_KEYMAP_UI | ED_KEYMAP_VIEW2D | ED_KEYMAP_FRAMES | ED_KEYMAP_HEADER; + art->listener = view3d_header_region_listener; + art->message_subscribe = view3d_header_region_message_subscribe; + art->init = view3d_header_region_init; + art->draw = view3d_header_region_draw; + BLI_addhead(&st->regiontypes, art); + + /* regions: hud */ + art = ED_area_type_hud(st->spaceid); + BLI_addhead(&st->regiontypes, art); + + /* regions: xr */ + art = MEM_cnew<ARegionType>("spacetype view3d xr region"); + art->regionid = RGN_TYPE_XR; + BLI_addhead(&st->regiontypes, art); + + BKE_spacetype_register(st); +} |