Blender V4.5
view3d_navigate_view_roll.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_dial_2d.h"
10#include "BLI_math_rotation.h"
11#include "BLI_math_vector.h"
12#include "BLI_rect.h"
13
14#include "BKE_context.hh"
15
16#include "WM_api.hh"
17
18#include "RNA_access.hh"
19#include "RNA_define.hh"
20
21#include "ED_screen.hh"
22
23#include "view3d_intern.hh"
24#include "view3d_navigate.hh" /* own include */
25
26/* -------------------------------------------------------------------- */
29
35static void view_roll_angle(ARegion *region,
36 float quat[4],
37 const float orig_quat[4],
38 const float dvec[3],
39 float angle,
40 bool use_axis_view)
41{
42 RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
43 float quat_mul[4];
44
45 /* camera axis */
46 axis_angle_normalized_to_quat(quat_mul, dvec, angle);
47
48 mul_qt_qtqt(quat, orig_quat, quat_mul);
49
50 /* avoid precision loss over time */
51 normalize_qt(quat);
52
53 if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == float(M_PI_2))) {
55 }
56 else {
57 rv3d->view = RV3D_VIEW_USER;
58 }
59}
60
61static void viewroll_apply(ViewOpsData *vod, int x, int y)
62{
63 const float current_position[2] = {float(x), float(y)};
64 float angle = BLI_dial_angle(vod->init.dial, current_position);
65
66 if (angle != 0.0f) {
68 vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle, false);
69 }
70
71 if (vod->use_dyn_ofs) {
73 vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs);
74 }
75
76 if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
78 }
79
81
83}
84
86{
87 ViewOpsData *vod = static_cast<ViewOpsData *>(op->customdata);
88 short event_code = VIEW_PASS;
89 bool use_autokey = false;
91
92 /* Execute the events. */
93 if (event->type == EVT_MODAL_MAP) {
94 switch (event->val) {
96 event_code = VIEW_CONFIRM;
97 break;
99 event_code = VIEW_CANCEL;
100 break;
102 WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, nullptr, event);
103 event_code = VIEW_CONFIRM;
104 break;
106 WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, nullptr, event);
107 event_code = VIEW_CONFIRM;
108 break;
109 }
110 }
111 else {
112 if (event->type == MOUSEMOVE) {
113 event_code = VIEW_APPLY;
114 }
115 else if (event->type == vod->init.event_type) {
116 /* Check `vod->init.event_type` first in case RMB was used to invoke.
117 * in this case confirming takes precedence over canceling, see: #102937. */
118 if (event->val == KM_RELEASE) {
119 event_code = VIEW_CONFIRM;
120 }
121 }
122 else if (event->type == EVT_ESCKEY) {
123 if (event->val == KM_PRESS) {
124 event_code = VIEW_CANCEL;
125 }
126 }
127 }
128
129 switch (event_code) {
130 case VIEW_APPLY: {
131 viewroll_apply(vod, event->xy[0], event->xy[1]);
133 use_autokey = true;
134 }
135 break;
136 }
137 case VIEW_CONFIRM: {
138 use_autokey = true;
140 break;
141 }
142 case VIEW_CANCEL: {
143 vod->state_restore();
145 break;
146 }
147 }
148
149 if (use_autokey) {
150 ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false);
151 }
152
153 if ((ret & OPERATOR_RUNNING_MODAL) == 0) {
154 viewops_data_free(C, static_cast<ViewOpsData *>(op->customdata));
155 op->customdata = nullptr;
156 }
157
158 return ret;
159}
160
161enum {
164};
165
167 {0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"},
168 {V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the left"},
169 {V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the right"},
170 {0, nullptr, 0, nullptr, nullptr},
171};
172
174{
175 ViewOpsData *vod;
176 if (op->customdata) {
177 vod = static_cast<ViewOpsData *>(op->customdata);
178 }
179 else {
180 vod = new ViewOpsData();
182 vod->rv3d = static_cast<RegionView3D *>(vod->region->regiondata);
183 }
184
186
187 const bool is_camera_lock = ED_view3d_camera_lock_check(vod->v3d, vod->rv3d);
188 if (vod->rv3d->persp == RV3D_CAMOB && !is_camera_lock) {
189 viewops_data_free(C, vod);
190 op->customdata = nullptr;
191 return OPERATOR_CANCELLED;
192 }
193
194 if (vod->depsgraph == nullptr) {
196 vod->init_navigation(C, nullptr, &ViewOpsType_roll);
197 }
198
199 int type = RNA_enum_get(op->ptr, "type");
200 float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle);
201 float mousevec[3];
202 float quat_new[4];
203
204 const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
205
206 if (type == V3D_VIEW_STEPLEFT) {
207 angle = -angle;
208 }
209
210 normalize_v3_v3(mousevec, vod->rv3d->viewinv[2]);
211 negate_v3(mousevec);
212 view_roll_angle(vod->region, quat_new, vod->rv3d->viewquat, mousevec, angle, true);
213
214 V3D_SmoothParams sview_params = {};
215 sview_params.quat = quat_new;
216 /* Group as successive roll may run by holding a key. */
217 sview_params.undo_str = op->type->name;
218 sview_params.undo_grouped = true;
219
220 if (vod->use_dyn_ofs) {
221 sview_params.dyn_ofs = vod->dyn_ofs;
222 }
223
224 ED_view3d_smooth_view(C, vod->v3d, vod->region, smooth_viewtx, &sview_params);
225
226 viewops_data_free(C, vod);
227 op->customdata = nullptr;
228 return OPERATOR_FINISHED;
229}
230
232{
233 ViewOpsData *vod;
234
235 bool use_angle = RNA_enum_get(op->ptr, "type") != 0;
236
237 if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) {
238 viewroll_exec(C, op);
239 }
240 else {
241 /* The equivalent functionality for orbiting the view: #VIEW3D_OT_orbit & #VIEW3D_OT_rotate are
242 * separate operators with different poll functions [which are only permissive for non-locked
243 * views]. This operator however mixes modal-interaction & instant-stepping into the same
244 * operator and its current poll function permissively finds the non-locked region in quad
245 * view. For instant-stepping into the operator via keyboard shortcuts, forwarding to the
246 * non-locked view (when in a locked view) makes sense, but modal-interaction with the locked
247 * view forwarding to a different view doesn't (hence the check). */
248 {
249 ARegion *region = CTX_wm_region(C);
250 if (region->regiontype == RGN_TYPE_WINDOW) {
251 const RegionView3D *rv3d = static_cast<const RegionView3D *>(region->regiondata);
252 if (rv3d->viewlock & RV3D_LOCK_ROTATION) {
254 }
255 }
256 }
257
258 /* makes op->customdata */
259 vod = viewops_data_create(C, event, &ViewOpsType_roll, false);
260
261 const float start_position[2] = {float(BLI_rcti_cent_x(&vod->region->winrct)),
262 float(BLI_rcti_cent_y(&vod->region->winrct))};
263 vod->init.dial = BLI_dial_init(start_position, FLT_EPSILON);
264 op->customdata = vod;
265
267
268 /* overwrite the mouse vector with the view direction */
269 normalize_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]);
270 negate_v3(vod->init.mousevec);
271
272 if (event->type == MOUSEROTATE) {
273 vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
274 viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]);
275
276 viewops_data_free(C, static_cast<ViewOpsData *>(op->customdata));
277 op->customdata = nullptr;
278 return OPERATOR_FINISHED;
279 }
280
281 /* add temp handler */
284 }
285 return OPERATOR_FINISHED;
286}
287
289{
290 PropertyRNA *prop;
291
292 /* identifiers */
293 ot->name = "View Roll";
294 ot->description = "Roll the view";
295 ot->idname = ViewOpsType_roll.idname;
296
297 /* API callbacks. */
298 ot->invoke = viewroll_invoke;
299 ot->exec = viewroll_exec;
300 ot->modal = viewroll_modal;
303
304 /* flags */
305 ot->flag = 0;
306
307 /* properties */
308 ot->prop = prop = RNA_def_float(
309 ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
311 prop = RNA_def_enum(ot->srna,
312 "type",
314 0,
315 "Roll Angle Source",
316 "How roll angle is calculated");
318}
319
321
323 /*flag*/ (VIEWOPS_FLAG_ORBIT_SELECT),
324 /*idname*/ "VIEW3D_OT_view_roll",
325 /*poll_fn*/ nullptr,
326 /*init_fn*/ nullptr,
327 /*apply_fn*/ nullptr,
328};
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
ARegion * CTX_wm_region(const bContext *C)
wmWindowManager * CTX_wm_manager(const bContext *C)
Dial * BLI_dial_init(const float start_position[2], float threshold)
float BLI_dial_angle(Dial *dial, const float current_position[2])
#define DEG2RADF(_deg)
#define M_PI_2
float normalize_qt(float q[4])
void axis_angle_normalized_to_quat(float r[4], const float axis[3], float angle)
void mul_qt_qtqt(float q[4], const float a[4], const float b[4])
MINLINE void negate_v3(float r[3])
MINLINE float normalize_v3_v3(float r[3], const float a[3])
BLI_INLINE int BLI_rcti_cent_y(const struct rcti *rct)
Definition BLI_rect.h:181
BLI_INLINE int BLI_rcti_cent_x(const struct rcti *rct)
Definition BLI_rect.h:177
@ RGN_TYPE_WINDOW
#define RV3D_VIEW_IS_AXIS(view)
@ RV3D_LOCK_ROTATION
@ RV3D_BOXVIEW
#define RV3D_LOCK_FLAGS(rv3d)
@ RV3D_CAMOB
@ RV3D_VIEW_USER
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_RUNNING_MODAL
@ OPERATOR_PASS_THROUGH
bScreen * ED_screen_animation_playing(const wmWindowManager *wm)
void ED_region_tag_redraw(ARegion *region)
Definition area.cc:639
bool ED_view3d_camera_lock_sync(const Depsgraph *depsgraph, View3D *v3d, RegionView3D *rv3d)
bool ED_view3d_context_user_region(bContext *C, View3D **r_v3d, ARegion **r_region)
bool ED_view3d_quat_to_axis_view_and_reset_quat(float quat[4], float epsilon, char *r_view, char *r_view_axis_roll)
bool ED_view3d_camera_lock_check(const View3D *v3d, const RegionView3D *rv3d)
bool ED_view3d_camera_lock_autokey(View3D *v3d, RegionView3D *rv3d, bContext *C, bool do_rotate, bool do_translate)
bool ED_operator_rv3d_user_region_poll(bContext *C)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
Definition IK_Math.h:117
@ PROP_SKIP_SAVE
Definition RNA_types.hh:330
#define C
Definition RandGen.cpp:29
@ KM_PRESS
Definition WM_types.hh:308
@ KM_RELEASE
Definition WM_types.hh:309
@ WM_OP_INVOKE_DEFAULT
Definition WM_types.hh:238
#define U
#define fabsf(x)
@ VIEW_CONFIRM
Definition image_ops.cc:600
@ VIEW_PASS
Definition image_ops.cc:598
@ VIEW_APPLY
Definition image_ops.cc:599
return ret
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
#define FLT_MAX
Definition stdcycles.h:14
void * regiondata
float viewinv[4][4]
const char * undo_str
const float * dyn_ofs
struct ViewOpsData::@223301325120147357216345116354254074005135062017 prev
Depsgraph * depsgraph
blender::int2 event_xy
struct ViewOpsData::@361211012146244153242124114136342371103125160111 init
RegionView3D * rv3d
void init_navigation(bContext *C, const wmEvent *event, const ViewOpsType *nav_type, const float dyn_ofs_override[3]=nullptr, const bool use_cursor_init=false)
VecBase< T, 2 > xy() const
wmEventType type
Definition WM_types.hh:754
short val
Definition WM_types.hh:756
int xy[2]
Definition WM_types.hh:758
int prev_xy[2]
Definition WM_types.hh:817
const char * name
Definition WM_types.hh:1030
struct wmOperatorType * type
struct PointerRNA * ptr
void view3d_boxview_sync(ScrArea *area, ARegion *region)
void view3d_navigate_cancel_fn(bContext *C, wmOperator *op)
void viewops_data_free(bContext *C, ViewOpsData *vod)
ViewOpsData * viewops_data_create(bContext *C, const wmEvent *event, const ViewOpsType *nav_type, const bool use_cursor_init)
void view3d_orbit_apply_dyn_ofs(float r_ofs[3], const float ofs_old[3], const float viewquat_old[4], const float viewquat_new[4], const float dyn_ofs[3])
void ED_view3d_smooth_view(bContext *C, View3D *v3d, ARegion *region, int smooth_viewtx, const V3D_SmoothParams *sview)
@ VIEWOPS_FLAG_ORBIT_SELECT
@ VIEW_CANCEL
const ViewOpsType ViewOpsType_roll
@ VIEWROT_MODAL_SWITCH_ROTATE
@ VIEW_MODAL_CANCEL
@ VIEWROT_MODAL_SWITCH_MOVE
@ VIEW_MODAL_CONFIRM
void ED_view3d_smooth_view_force_finish(bContext *C, View3D *v3d, ARegion *region)
void VIEW3D_OT_view_roll(wmOperatorType *ot)
static wmOperatorStatus viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus viewroll_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem prop_view_roll_items[]
static wmOperatorStatus viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void view_roll_angle(ARegion *region, float quat[4], const float orig_quat[4], const float dvec[3], float angle, bool use_axis_view)
static void viewroll_apply(ViewOpsData *vod, int x, int y)
wmEventHandler_Op * WM_event_add_modal_handler(bContext *C, wmOperator *op)
wmOperatorStatus WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
@ EVT_MODAL_MAP
@ MOUSEROTATE
@ MOUSEMOVE
@ EVT_ESCKEY
wmOperatorType * ot
Definition wm_files.cc:4226
int WM_operator_smooth_viewtx_get(const wmOperator *op)