View roll checked if the resulting roll was close to a view axis but didn't write the aligned quaternion back to the final result. Add ED_view3d_quat_to_axis_view_and_reset_quat since most callers to ED_view3d_quat_to_axis_view will reset the quaternion when a view aligned axis is found.
294 lines
8.1 KiB
C
294 lines
8.1 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup spview3d
|
|
*/
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_dial_2d.h"
|
|
#include "BLI_math.h"
|
|
|
|
#include "BKE_context.h"
|
|
|
|
#include "WM_api.h"
|
|
|
|
#include "RNA_access.h"
|
|
#include "RNA_define.h"
|
|
|
|
#include "ED_screen.h"
|
|
|
|
#include "view3d_intern.h"
|
|
#include "view3d_navigate.h" /* own include */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name View Roll Operator
|
|
* \{ */
|
|
|
|
/**
|
|
* \param use_axis_view: When true, keep axis-aligned orthographic views
|
|
* (when rotating in 90 degree increments). While this may seem obscure some NDOF
|
|
* devices have key shortcuts to do this (see #NDOF_BUTTON_ROLL_CW & #NDOF_BUTTON_ROLL_CCW).
|
|
*/
|
|
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)
|
|
{
|
|
RegionView3D *rv3d = region->regiondata;
|
|
float quat_mul[4];
|
|
|
|
/* camera axis */
|
|
axis_angle_normalized_to_quat(quat_mul, dvec, angle);
|
|
|
|
mul_qt_qtqt(quat, orig_quat, quat_mul);
|
|
|
|
/* avoid precision loss over time */
|
|
normalize_qt(quat);
|
|
|
|
if (use_axis_view && RV3D_VIEW_IS_AXIS(rv3d->view) && (fabsf(angle) == (float)M_PI_2)) {
|
|
ED_view3d_quat_to_axis_view_and_reset_quat(quat, 0.01f, &rv3d->view, &rv3d->view_axis_roll);
|
|
}
|
|
else {
|
|
rv3d->view = RV3D_VIEW_USER;
|
|
}
|
|
}
|
|
|
|
static void viewroll_apply(ViewOpsData *vod, int x, int y)
|
|
{
|
|
float angle = BLI_dial_angle(vod->init.dial, (const float[2]){x, y});
|
|
|
|
if (angle != 0.0f) {
|
|
view_roll_angle(
|
|
vod->region, vod->rv3d->viewquat, vod->init.quat, vod->init.mousevec, angle, false);
|
|
}
|
|
|
|
if (vod->use_dyn_ofs) {
|
|
view3d_orbit_apply_dyn_ofs(
|
|
vod->rv3d->ofs, vod->init.ofs, vod->init.quat, vod->rv3d->viewquat, vod->dyn_ofs);
|
|
}
|
|
|
|
if (RV3D_LOCK_FLAGS(vod->rv3d) & RV3D_BOXVIEW) {
|
|
view3d_boxview_sync(vod->area, vod->region);
|
|
}
|
|
|
|
ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
|
|
|
|
ED_region_tag_redraw(vod->region);
|
|
}
|
|
|
|
static int viewroll_modal(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
ViewOpsData *vod = op->customdata;
|
|
short event_code = VIEW_PASS;
|
|
bool use_autokey = false;
|
|
int ret = OPERATOR_RUNNING_MODAL;
|
|
|
|
/* execute the events */
|
|
if (event->type == MOUSEMOVE) {
|
|
event_code = VIEW_APPLY;
|
|
}
|
|
else if (event->type == EVT_MODAL_MAP) {
|
|
switch (event->val) {
|
|
case VIEW_MODAL_CONFIRM:
|
|
event_code = VIEW_CONFIRM;
|
|
break;
|
|
case VIEWROT_MODAL_SWITCH_MOVE:
|
|
WM_operator_name_call(C, "VIEW3D_OT_move", WM_OP_INVOKE_DEFAULT, NULL, event);
|
|
event_code = VIEW_CONFIRM;
|
|
break;
|
|
case VIEWROT_MODAL_SWITCH_ROTATE:
|
|
WM_operator_name_call(C, "VIEW3D_OT_rotate", WM_OP_INVOKE_DEFAULT, NULL, event);
|
|
event_code = VIEW_CONFIRM;
|
|
break;
|
|
}
|
|
}
|
|
else if (ELEM(event->type, EVT_ESCKEY, RIGHTMOUSE)) {
|
|
/* Note this does not remove auto-keys on locked cameras. */
|
|
copy_qt_qt(vod->rv3d->viewquat, vod->init.quat);
|
|
ED_view3d_camera_lock_sync(vod->depsgraph, vod->v3d, vod->rv3d);
|
|
viewops_data_free(C, op->customdata);
|
|
op->customdata = NULL;
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
else if (event->type == vod->init.event_type && event->val == KM_RELEASE) {
|
|
event_code = VIEW_CONFIRM;
|
|
}
|
|
|
|
if (event_code == VIEW_APPLY) {
|
|
viewroll_apply(vod, event->xy[0], event->xy[1]);
|
|
if (ED_screen_animation_playing(CTX_wm_manager(C))) {
|
|
use_autokey = true;
|
|
}
|
|
}
|
|
else if (event_code == VIEW_CONFIRM) {
|
|
use_autokey = true;
|
|
ret = OPERATOR_FINISHED;
|
|
}
|
|
|
|
if (use_autokey) {
|
|
ED_view3d_camera_lock_autokey(vod->v3d, vod->rv3d, C, true, false);
|
|
}
|
|
|
|
if (ret & OPERATOR_FINISHED) {
|
|
viewops_data_free(C, op->customdata);
|
|
op->customdata = NULL;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
enum {
|
|
V3D_VIEW_STEPLEFT = 1,
|
|
V3D_VIEW_STEPRIGHT,
|
|
};
|
|
|
|
static const EnumPropertyItem prop_view_roll_items[] = {
|
|
{0, "ANGLE", 0, "Roll Angle", "Roll the view using an angle value"},
|
|
{V3D_VIEW_STEPLEFT, "LEFT", 0, "Roll Left", "Roll the view around to the left"},
|
|
{V3D_VIEW_STEPRIGHT, "RIGHT", 0, "Roll Right", "Roll the view around to the right"},
|
|
{0, NULL, 0, NULL, NULL},
|
|
};
|
|
|
|
static int viewroll_exec(bContext *C, wmOperator *op)
|
|
{
|
|
View3D *v3d;
|
|
RegionView3D *rv3d;
|
|
ARegion *region;
|
|
|
|
if (op->customdata) {
|
|
ViewOpsData *vod = op->customdata;
|
|
region = vod->region;
|
|
v3d = vod->v3d;
|
|
}
|
|
else {
|
|
ED_view3d_context_user_region(C, &v3d, ®ion);
|
|
}
|
|
|
|
rv3d = region->regiondata;
|
|
if ((rv3d->persp != RV3D_CAMOB) || ED_view3d_camera_lock_check(v3d, rv3d)) {
|
|
|
|
ED_view3d_smooth_view_force_finish(C, v3d, region);
|
|
|
|
int type = RNA_enum_get(op->ptr, "type");
|
|
float angle = (type == 0) ? RNA_float_get(op->ptr, "angle") : DEG2RADF(U.pad_rot_angle);
|
|
float mousevec[3];
|
|
float quat_new[4];
|
|
|
|
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
|
|
|
|
if (type == V3D_VIEW_STEPLEFT) {
|
|
angle = -angle;
|
|
}
|
|
|
|
normalize_v3_v3(mousevec, rv3d->viewinv[2]);
|
|
negate_v3(mousevec);
|
|
view_roll_angle(region, quat_new, rv3d->viewquat, mousevec, angle, true);
|
|
|
|
const float *dyn_ofs_pt = NULL;
|
|
float dyn_ofs[3];
|
|
if (U.uiflag & USER_ORBIT_SELECTION) {
|
|
if (view3d_orbit_calc_center(C, dyn_ofs)) {
|
|
negate_v3(dyn_ofs);
|
|
dyn_ofs_pt = dyn_ofs;
|
|
}
|
|
}
|
|
|
|
ED_view3d_smooth_view(C,
|
|
v3d,
|
|
region,
|
|
smooth_viewtx,
|
|
&(const V3D_SmoothParams){
|
|
.quat = quat_new,
|
|
.dyn_ofs = dyn_ofs_pt,
|
|
});
|
|
|
|
viewops_data_free(C, op->customdata);
|
|
op->customdata = NULL;
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
viewops_data_free(C, op->customdata);
|
|
op->customdata = NULL;
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
static int viewroll_invoke(bContext *C, wmOperator *op, const wmEvent *event)
|
|
{
|
|
ViewOpsData *vod;
|
|
|
|
bool use_angle = RNA_enum_get(op->ptr, "type") != 0;
|
|
|
|
if (use_angle || RNA_struct_property_is_set(op->ptr, "angle")) {
|
|
viewroll_exec(C, op);
|
|
}
|
|
else {
|
|
/* makes op->customdata */
|
|
vod = op->customdata = viewops_data_create(C, event, viewops_flag_from_prefs());
|
|
vod->init.dial = BLI_dial_init((const float[2]){BLI_rcti_cent_x(&vod->region->winrct),
|
|
BLI_rcti_cent_y(&vod->region->winrct)},
|
|
FLT_EPSILON);
|
|
|
|
ED_view3d_smooth_view_force_finish(C, vod->v3d, vod->region);
|
|
|
|
/* overwrite the mouse vector with the view direction */
|
|
normalize_v3_v3(vod->init.mousevec, vod->rv3d->viewinv[2]);
|
|
negate_v3(vod->init.mousevec);
|
|
|
|
if (event->type == MOUSEROTATE) {
|
|
vod->init.event_xy[0] = vod->prev.event_xy[0] = event->xy[0];
|
|
viewroll_apply(vod, event->prev_xy[0], event->prev_xy[1]);
|
|
|
|
viewops_data_free(C, op->customdata);
|
|
op->customdata = NULL;
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
/* add temp handler */
|
|
WM_event_add_modal_handler(C, op);
|
|
return OPERATOR_RUNNING_MODAL;
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
static void viewroll_cancel(bContext *C, wmOperator *op)
|
|
{
|
|
viewops_data_free(C, op->customdata);
|
|
op->customdata = NULL;
|
|
}
|
|
|
|
void VIEW3D_OT_view_roll(wmOperatorType *ot)
|
|
{
|
|
PropertyRNA *prop;
|
|
|
|
/* identifiers */
|
|
ot->name = "View Roll";
|
|
ot->description = "Roll the view";
|
|
ot->idname = "VIEW3D_OT_view_roll";
|
|
|
|
/* api callbacks */
|
|
ot->invoke = viewroll_invoke;
|
|
ot->exec = viewroll_exec;
|
|
ot->modal = viewroll_modal;
|
|
ot->poll = ED_operator_rv3d_user_region_poll;
|
|
ot->cancel = viewroll_cancel;
|
|
|
|
/* flags */
|
|
ot->flag = 0;
|
|
|
|
/* properties */
|
|
ot->prop = prop = RNA_def_float(
|
|
ot->srna, "angle", 0, -FLT_MAX, FLT_MAX, "Roll", "", -FLT_MAX, FLT_MAX);
|
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
|
prop = RNA_def_enum(ot->srna,
|
|
"type",
|
|
prop_view_roll_items,
|
|
0,
|
|
"Roll Angle Source",
|
|
"How roll angle is calculated");
|
|
RNA_def_property_flag(prop, PROP_SKIP_SAVE);
|
|
}
|
|
|
|
/** \} */
|