more consistent and modal-friendly ndof events, fly mode v1

This commit is contained in:
Mike Erwin 2011-07-14 21:20:45 +00:00
parent 44d2e6eb10
commit cc1ba4569c
8 changed files with 369 additions and 139 deletions

@ -434,14 +434,24 @@ typedef struct {
GHOST_TUns8 **strings;
} GHOST_TStringArray;
typedef enum {
GHOST_kNotStarted,
GHOST_kStarting,
GHOST_kInProgress,
GHOST_kFinishing,
GHOST_kFinished
} GHOST_TProgress;
typedef struct {
/** N-degree of freedom device data v3 [GSoC 2010]*/
/* Each component normally ranges from -1 to +1, but can exceed that. */
float tx, ty, tz; /* translation: -x left, +y forward, -z up */
float rx, ry, rz; /* rotation:
axis = (rx,ry,rz).normalized
amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg] */
float dt; // time since previous NDOF Motion event (or zero if this is the first)
/** N-degree of freedom device data v3 [GSoC 2010] */
// Each component normally ranges from -1 to +1, but can exceed that.
// These use blender standard view coordinates, with positive rotations being CCW about the axis.
float tx, ty, tz; // translation
float rx, ry, rz; // rotation:
// axis = (rx,ry,rz).normalized
// amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg]
float dt; // time since previous NDOF Motion event
GHOST_TProgress progress; // Starting, InProgress or Finishing (for modal handlers)
} GHOST_TEventNDOFMotionData;
typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction;

@ -29,6 +29,12 @@
#include <stdio.h> // for error/info reporting
#include <math.h>
#ifdef DEBUG_NDOF_MOTION
// printable version of each GHOST_TProgress value
static const char* progress_string[] =
{"not started","starting","in progress","finishing","finished"};
#endif
#ifdef DEBUG_NDOF_BUTTONS
static const char* ndof_button_names[] = {
// used internally, never sent
@ -139,9 +145,10 @@ GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys)
, m_buttonCount(0)
, m_buttonMask(0)
, m_buttons(0)
, m_motionTime(1000) // one full second (operators should filter out such large time deltas)
, m_motionTime(0)
, m_prevMotionTime(0)
, m_atRest(true)
, m_motionState(GHOST_kNotStarted)
, m_motionEventPending(false)
{
// to avoid the rare situation where one triple is updated and
// the other is not, initialize them both here:
@ -179,10 +186,16 @@ void GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ
break;
// -- older devices --
case 0xC623: puts("ndof: SpaceTraveler not supported, please file a bug report"); break;
// no buttons?
case 0xC625: puts("ndof: SpacePilot not supported, please file a bug report"); break;
// 21 buttons
// keep unknown device type so rogue button events will get discarded
// "mystery device" owners can help build another HID_map for their hardware
case 0xC623:
puts("ndof: SpaceTraveler not supported, please file a bug report");
m_buttonCount = 8;
break;
case 0xC625:
puts("ndof: SpacePilot not supported, please file a bug report");
m_buttonCount = 21;
break;
default: printf("ndof: unknown Logitech product %04hx\n", product_id);
}
@ -198,18 +211,41 @@ void GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short produ
#endif
}
void GHOST_NDOFManager::updateMotionState()
{
if (m_motionEventPending)
return;
switch (m_motionState)
{
case GHOST_kFinished:
case GHOST_kNotStarted:
m_motionState = GHOST_kStarting;
break;
case GHOST_kStarting:
m_motionState = GHOST_kInProgress;
break;
default:
// InProgress remains InProgress
// should never be Finishing
break;
}
m_motionEventPending = true;
}
void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
{
memcpy(m_translation, t, sizeof(m_translation));
m_motionTime = time;
m_atRest = false;
updateMotionState();
}
void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
{
memcpy(m_rotation, r, sizeof(m_rotation));
m_motionTime = time;
m_atRest = false;
updateMotionState();
}
void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
@ -290,7 +326,7 @@ void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
int diff = m_buttons ^ button_bits;
for (int button_number = 0; button_number <= 31; ++button_number)
for (int button_number = 0; button_number < m_buttonCount; ++button_number)
{
int mask = 1 << button_number;
@ -302,15 +338,21 @@ void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
}
}
static bool atHomePosition(GHOST_TEventNDOFMotionData* ndof, float threshold)
static bool atHomePosition(GHOST_TEventNDOFMotionData* ndof)
{
#define HOME(foo) (fabsf(ndof->foo) < threshold)
#define HOME(foo) (ndof->foo == 0)
return HOME(tx) && HOME(ty) && HOME(tz) && HOME(rx) && HOME(ry) && HOME(rz);
}
static bool nearHomePosition(GHOST_TEventNDOFMotionData* ndof, float threshold)
{
#define HOME1(foo) (fabsf(ndof->foo) < threshold)
return HOME1(tx) && HOME1(ty) && HOME1(tz) && HOME1(rx) && HOME1(ry) && HOME1(rz);
}
bool GHOST_NDOFManager::sendMotionEvent()
{
if (m_atRest)
if (m_motionState == GHOST_kFinished || m_motionState == GHOST_kNotStarted)
return false;
GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
@ -320,7 +362,7 @@ bool GHOST_NDOFManager::sendMotionEvent()
const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually
// possible future enhancement
// probable future enhancement
// scale *= m_sensitivity;
data->tx = scale * m_translation[0];
@ -331,19 +373,35 @@ bool GHOST_NDOFManager::sendMotionEvent()
data->ry = scale * m_rotation[1];
data->rz = scale * m_rotation[2];
data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
if (m_motionState == GHOST_kStarting)
// prev motion time will be ancient, so just make up something reasonable
data->dt = 0.0125f;
else
data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
m_prevMotionTime = m_motionTime;
// 'at rest' test goes at the end so that the first 'rest' event gets sent
if (atHomePosition(data))
// if (nearHomePosition(data, 0.05f)) // Linux & Windows have trouble w/ calibration
{
data->progress = GHOST_kFinishing;
// for internal state, skip Finishing & jump to Finished
m_motionState = GHOST_kFinished;
}
else
data->progress = m_motionState; // Starting or InProgress
#ifdef DEBUG_NDOF_MOTION
printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
data->tx, data->ty, data->tz, data->rx, data->ry, data->rz, data->dt);
printf("ndof %s: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f\n",
progress_string[data->progress],
data->tx, data->ty, data->tz,
data->rx, data->ry, data->rz,
data->dt);
#endif
m_system.pushEvent(event);
// 'at rest' test goes at the end so that the first 'rest' event gets sent
m_atRest = atHomePosition(data, 0.05f);
m_motionEventPending = false;
return true;
}

@ -27,9 +27,7 @@
#include "GHOST_System.h"
// --- the following type definitions will find a home somewhere else once finished ---
//#define DEBUG_NDOF_MOTION
#define DEBUG_NDOF_MOTION
#define DEBUG_NDOF_BUTTONS
typedef enum { NDOF_UnknownDevice, NDOF_SpaceNavigator, NDOF_SpaceExplorer, NDOF_SpacePilotPro } NDOF_DeviceT;
@ -120,7 +118,7 @@ protected:
private:
void sendButtonEvent(NDOF_ButtonT, bool press, GHOST_TUns64 time, GHOST_IWindow*);
void sendKeyEvent(GHOST_TKey, bool press, GHOST_TUns64 time, GHOST_IWindow*);
void updateMotionState();
NDOF_DeviceT m_deviceType;
int m_buttonCount;
@ -132,7 +130,8 @@ private:
GHOST_TUns64 m_motionTime; // in milliseconds
GHOST_TUns64 m_prevMotionTime; // time of most recent Motion event sent
bool m_atRest;
GHOST_TProgress m_motionState;
bool m_motionEventPending;
};
#endif

@ -929,10 +929,30 @@ void VIEW3D_OT_rotate(wmOperatorType *ot)
ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
}
#if 0 // NDOF utility functions
// NDOF utility functions
// (should these functions live in this file?)
float ndof_to_angle_axis(struct wmNDOFMotionData* ndof, float axis[3])
{
const float x = ndof->rx;
const float y = ndof->ry;
const float z = ndof->rz;
float angular_velocity = sqrtf(x*x + y*y + z*z);
float angle = ndof->dt * angular_velocity;
float scale = 1.f / angular_velocity;
// normalize
axis[0] = scale * x;
axis[1] = scale * y;
axis[2] = scale * z;
return angle;
}
#if 0 // unused utility functions
// returns angular velocity (0..1), fills axis of rotation
// (shouldn't live in this file!)
static float ndof_to_angle_axis(const float ndof[3], float axis[3])
float ndof_to_angle_axis(const float ndof[3], float axis[3])
{
const float x = ndof[0];
const float y = ndof[1];
@ -960,7 +980,7 @@ static float ndof_to_angular_velocity(wmNDOFMotionData* ndof)
}
#endif
static void ndof_to_quat(wmNDOFMotionData* ndof, float q[4])
void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4])
{
const float x = ndof->rx;
const float y = ndof->ry;
@ -988,6 +1008,8 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
RegionView3D* rv3d = CTX_wm_region_view3d(C);
wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
const float dt = ndof->dt;
// tune these until everything feels right
const float rot_sensitivity = 1.f;
const float zoom_sensitivity = 1.f;
@ -996,12 +1018,6 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
// rather have bool, but...
int has_rotation = rv3d->viewlock != RV3D_LOCKED && (ndof->rx || ndof->ry || ndof->rz);
float dt = ndof->dt;
if (dt > 0.25f)
/* this is probably the first event for this motion, so set dt to something reasonable */
/* TODO: replace such guesswork with a flag or field from the NDOF manager */
ndof->dt = dt = 0.0125f;
//#define DEBUG_NDOF_MOTION
#ifdef DEBUG_NDOF_MOTION
printf("ndof: T=(%.2f,%.2f,%.2f) R=(%.2f,%.2f,%.2f) dt=%.3f delivered to 3D view\n",
@ -1086,74 +1102,7 @@ static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
return OPERATOR_FINISHED;
}
#if 0 // not ready
static int ndof_fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
RegionView3D* rv3d = CTX_wm_region_view3d(C);
wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
const int shouldRotate = 0, shouldMove = 1;
float dt = ndof->dt;
if (dt > 0.25f)
/* this is probably the first event for this motion, so set dt to something reasonable */
/* TODO: replace such guesswork with a flag or field from the NDOF manager */
ndof->dt = dt = 0.0125f;
if (shouldRotate)
{
const float turn_sensitivity = 1.f;
float rot[4];
ndof_to_quat(ndof, rot);
rv3d->view = RV3D_VIEW_USER;
}
if (shouldMove)
{
const float forward_sensitivity = 1.f;
const float vertical_sensitivity = 1.f;
const float lateral_sensitivity = 1.f;
float trans[3] = {
lateral_sensitivity * dt * ndof->tx,
vertical_sensitivity * dt * ndof->ty,
forward_sensitivity * rv3d->dist * dt * ndof->tz
};
}
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
// BEGIN old fly code
// derived from blender 2.4
static void getndof(wmNDOFMotionData* indof, float* outdof)
{
// Rotations feel relatively faster than translations only in fly mode, so
// we have no choice but to fix that here (not in the plugins)
const float turn_sensitivity = 0.8f;
const float forward_sensitivity = 2.5f;
const float vertical_sensitivity = 1.6f;
const float lateral_sensitivity = 2.5f;
const float dt = (indof->dt < 0.25f) ? indof->dt : 0.0125f;
// this is probably the first event for this motion, so set dt to something reasonable
// TODO: replace such guesswork with a flag or field from the NDOF manager
outdof[0] = lateral_sensitivity * dt * indof->tx;
outdof[1] = vertical_sensitivity * dt * indof->ty;
outdof[2] = forward_sensitivity * dt * indof->tz;
outdof[3] = turn_sensitivity * dt * indof->rx;
outdof[4] = turn_sensitivity * dt * indof->ry;
outdof[5] = turn_sensitivity * dt * indof->rz;
}
#if 0
// statics for controlling rv3d->dist corrections.
// viewmoveNDOF zeros and adjusts rv3d->ofs.
// viewmove restores based on dz_flag state.
@ -1200,6 +1149,94 @@ static void mouse_rotation_workaround_pop(RegionView3D* rv3d)
}
}
static int ndof_fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
RegionView3D* rv3d = CTX_wm_region_view3d(C);
wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
const int shouldRotate = 1, shouldTranslate = 0;
const float dt = ndof->dt;
float view_inv[4];
invert_qt_qt(view_inv, rv3d->viewquat);
if (shouldRotate)
{
const float turn_sensitivity = 1.f;
float rot[4];
float view_inv_conj[4];
mouse_rotation_workaround_push(rv3d);
ndof_to_quat(ndof, rot);
copy_qt_qt(view_inv_conj, view_inv);
conjugate_qt(view_inv_conj);
// transform rotation from view to world coordinates
mul_qt_qtqt(rot, view_inv, rot);
mul_qt_qtqt(rot, rot, view_inv_conj);
// apply rotation to view offset (focal point)
mul_qt_v3(rot, rv3d->ofs);
// mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
rv3d->view = RV3D_VIEW_USER;
}
if (shouldTranslate)
{
const float forward_sensitivity = 1.f;
const float vertical_sensitivity = 1.f;
const float lateral_sensitivity = 1.f;
float trans[3] = {
lateral_sensitivity * ndof->tx,
vertical_sensitivity * ndof->ty,
forward_sensitivity * ndof->tz
};
// mul_v3_fl(trans, rv3d->dist * dt);
mul_v3_fl(trans, dt);
/* transform motion from view to world coordinates */
mul_qt_v3(view_inv, trans);
/* move center of view opposite of hand motion (this is camera mode, not object mode) */
sub_v3_v3(rv3d->ofs, trans);
}
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
// BEGIN old fly code
// derived from blender 2.4
static void getndof(wmNDOFMotionData* indof, float* outdof)
{
// Rotations feel relatively faster than translations only in fly mode, so
// we have no choice but to fix that here (not in the plugins)
const float turn_sensitivity = 0.8f;
const float forward_sensitivity = 2.5f;
const float vertical_sensitivity = 1.6f;
const float lateral_sensitivity = 2.5f;
const float dt = indof->dt;
outdof[0] = lateral_sensitivity * dt * indof->tx;
outdof[1] = vertical_sensitivity * dt * indof->ty;
outdof[2] = forward_sensitivity * dt * indof->tz;
outdof[3] = turn_sensitivity * dt * indof->rx;
outdof[4] = turn_sensitivity * dt * indof->ry;
outdof[5] = turn_sensitivity * dt * indof->rz;
}
static int ndof_oldfly_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
RegionView3D* rv3d = CTX_wm_region_view3d(C);
@ -1254,7 +1291,7 @@ static int ndof_oldfly_invoke(bContext *C, wmOperator *op, wmEvent *event)
// translate the view
sub_v3_v3(rv3d->ofs, tvec);
mouse_rotation_workaround_pop(rv3d);
// mouse_rotation_workaround_pop(rv3d);
// back to 2.5 land!
ED_region_tag_redraw(CTX_wm_region(C));
@ -1272,8 +1309,9 @@ void VIEW3D_OT_ndof(struct wmOperatorType *ot)
ot->idname = "VIEW3D_OT_ndof";
/* api callbacks */
// ot->invoke = ndof_oldfly_invoke;
ot->invoke = ndof_orbit_invoke;
// ot->invoke = ndof_fly_invoke;
// ot->invoke = ndof_oldfly_invoke;
ot->poll = ED_operator_view3d_active;
/* flags */

@ -158,7 +158,9 @@ typedef struct FlyInfo {
short state;
short use_precision;
short redraw;
int mval[2];
int mval[2]; /* latest 2D mouse values */
wmNDOFMotionData* ndof; /* latest 3D mouse values */
/* fly state state */
float speed; /* the speed the view is moving per redraw */
@ -257,6 +259,8 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
fly->ar = CTX_wm_region(C);
fly->scene= CTX_data_scene(C);
puts("\n-- fly begin --");
if(fly->rv3d->persp==RV3D_CAMOB && fly->v3d->camera->id.lib) {
BKE_report(op->reports, RPT_ERROR, "Cannot fly a camera from an external library");
return FALSE;
@ -282,12 +286,14 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
fly->zlock_momentum=0.0f;
fly->grid= 1.0f;
fly->use_precision= 0;
fly->redraw= 1;
fly->dvec_prev[0]= fly->dvec_prev[1]= fly->dvec_prev[2]= 0.0f;
fly->timer= WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
VECCOPY2D(fly->mval, event->mval)
fly->ndof = NULL;
fly->time_lastdraw= fly->time_lastwheel= PIL_check_seconds_timer();
@ -329,8 +335,17 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
/* perspective or ortho */
if (fly->rv3d->persp==RV3D_ORTHO)
fly->rv3d->persp= RV3D_PERSP; /*if ortho projection, make perspective */
copy_qt_qt(fly->rot_backup, fly->rv3d->viewquat);
copy_v3_v3(fly->ofs_backup, fly->rv3d->ofs);
/* the dist defines a vector that is infront of the offset
to rotate the view about.
this is no good for fly mode because we
want to rotate about the viewers center.
but to correct the dist removal we must
alter offset so the view doesn't jump. */
fly->rv3d->dist= 0.0f;
upvec[2]= fly->dist_backup; /*x and y are 0*/
@ -338,7 +353,6 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
sub_v3_v3(fly->rv3d->ofs, upvec);
/*Done with correcting for the dist*/
}
/* center the mouse, probably the UI mafia are against this but without its quite annoying */
WM_cursor_warp(CTX_wm_window(C), fly->ar->winrct.xmin + fly->ar->winx/2, fly->ar->winrct.ymin + fly->ar->winy/2);
@ -356,6 +370,8 @@ static int flyEnd(bContext *C, FlyInfo *fly)
if(fly->state == FLY_RUNNING)
return OPERATOR_RUNNING_MODAL;
puts("\n-- fly end --");
WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), fly->timer);
ED_region_draw_cb_exit(fly->ar->type, fly->draw_handle_pixel);
@ -401,6 +417,9 @@ static int flyEnd(bContext *C, FlyInfo *fly)
if(fly->obtfm)
MEM_freeN(fly->obtfm);
if(fly->ndof)
MEM_freeN(fly->ndof);
if(fly->state == FLY_CONFIRM) {
MEM_freeN(fly);
return OPERATOR_FINISHED;
@ -412,12 +431,46 @@ static int flyEnd(bContext *C, FlyInfo *fly)
static void flyEvent(FlyInfo *fly, wmEvent *event)
{
if (event->type == TIMER && event->customdata == fly->timer) {
fly->redraw = 1;
}
else if (event->type == MOUSEMOVE) {
if (event->type == MOUSEMOVE) {
VECCOPY2D(fly->mval, event->mval);
} /* handle modal keymap first */
}
else if (event->type == NDOF_MOTION) {
// do these automagically get delivered? yes.
// puts("ndof motion detected in fly mode!");
// static const char* tag_name = "3D mouse position";
wmNDOFMotionData* incoming_ndof = (wmNDOFMotionData*) event->customdata;
switch (incoming_ndof->progress)
{
case P_STARTING:
// start keeping track of 3D mouse position
puts("start keeping track of 3D mouse position");
// fall through...
case P_IN_PROGRESS:
// update 3D mouse position
putchar('.'); fflush(stdout);
if (fly->ndof == NULL)
// fly->ndof = MEM_mallocN(sizeof(wmNDOFMotionData), tag_name);
fly->ndof = MEM_dupallocN(incoming_ndof);
// fly->ndof = malloc(sizeof(wmNDOFMotionData));
else
memcpy(fly->ndof, incoming_ndof, sizeof(wmNDOFMotionData));
break;
case P_FINISHING:
// stop keeping track of 3D mouse position
puts("stop keeping track of 3D mouse position");
if (fly->ndof)
{
MEM_freeN(fly->ndof);
// free(fly->ndof);
fly->ndof = NULL;
}
break;
default:
; // should always be one of the above 3
}
}
/* handle modal keymap first */
else if (event->type == EVT_MODAL_MAP) {
switch (event->val) {
case FLY_MODAL_CANCEL:
@ -528,7 +581,6 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
case FLY_MODAL_PRECISION_DISABLE:
fly->use_precision= FALSE;
break;
}
}
}
@ -567,16 +619,12 @@ static int flyApply(bContext *C, FlyInfo *fly)
unsigned char
apply_rotation= 1; /* if the user presses shift they can look about without movinf the direction there looking*/
static unsigned int iteration = 1;
printf("fly timer %d\n", iteration++);
if(fly->root_parent)
ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
/* the dist defines a vector that is infront of the offset
to rotate the view about.
this is no good for fly mode because we
want to rotate about the viewers center.
but to correct the dist removal we must
alter offset so the view doesn't jump. */
xmargin= ar->winx/20.0f;
ymargin= ar->winy/20.0f;
@ -622,6 +670,8 @@ static int flyApply(bContext *C, FlyInfo *fly)
float time_redraw;
float time_redraw_clamped;
fly->redraw= 1;
time_current= PIL_check_seconds_timer();
time_redraw= (float)(time_current - fly->time_lastdraw);
time_redraw_clamped= MIN2(0.05f, time_redraw); /* clamt the redraw time to avoid jitter in roll correction */
@ -854,11 +904,69 @@ static int flyApply(bContext *C, FlyInfo *fly)
copy_v3_v3(fly->dvec_prev, dvec);
}
/* moved to flyEnd() */
return OPERATOR_FINISHED;
}
static int flyApply_ndof(bContext *C, FlyInfo *fly)
{
// shorthand for oft-used variables
wmNDOFMotionData* ndof = fly->ndof;
const float dt = ndof->dt;
RegionView3D* rv3d = fly->rv3d;
const int shouldRotate = 1, shouldTranslate = 1;
float view_inv[4];
invert_qt_qt(view_inv, rv3d->viewquat);
if (shouldRotate)
{
const float turn_sensitivity = 1.f;
float rotation[4];
float axis[3];
float angle = turn_sensitivity * ndof_to_angle_axis(ndof, axis);
// transform rotation axis from view to world coordinates
mul_qt_v3(view_inv, axis);
// apply rotation to view
axis_angle_to_quat(rotation, axis, angle);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
rv3d->view = RV3D_VIEW_USER;
fly->redraw = 1;
}
if (shouldTranslate)
{
const float forward_sensitivity = 1.f;
const float vertical_sensitivity = 0.4f;
const float lateral_sensitivity = 0.6f;
float speed = 10.f; // blender units per second
// ^^ this is ok for default cube scene, but should scale with.. something
float trans[3] = {
lateral_sensitivity * ndof->tx,
vertical_sensitivity * ndof->ty,
forward_sensitivity * ndof->tz
};
mul_v3_fl(trans, speed * dt);
// transform motion from view to world coordinates
mul_qt_v3(view_inv, trans);
// move center of view opposite of hand motion (this is camera mode, not object mode)
sub_v3_v3(rv3d->ofs, trans);
fly->redraw = 1;
}
return OPERATOR_FINISHED;
}
static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
@ -908,7 +1016,12 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
flyEvent(fly, event);
if(event->type==TIMER && event->customdata == fly->timer)
if (fly->ndof) // 3D mouse overrules [2D mouse + timer]
{
if (event->type==NDOF_MOTION)
flyApply_ndof(C, fly);
}
else if (event->type==TIMER && event->customdata == fly->timer)
flyApply(C, fly);
do_draw |= fly->redraw;

@ -51,6 +51,7 @@ struct ARegionType;
struct bPoseChannel;
struct bAnimVizSettings;
struct bMotionPath;
struct wmNDOFMotionData;
#define BL_NEAR_CLIP 0.001
@ -92,6 +93,8 @@ void VIEW3D_OT_zoom_border(struct wmOperatorType *ot);
void VIEW3D_OT_drawtype(struct wmOperatorType *ot);
void view3d_boxview_copy(ScrArea *sa, ARegion *ar);
void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4]);
float ndof_to_angle_axis(struct wmNDOFMotionData* ndof, float axis[3]);
/* view3d_fly.c */
void view3d_keymap(struct wmKeyConfig *keyconf);

@ -377,17 +377,24 @@ typedef struct wmTabletData {
float Ytilt; /* as above */
} wmTabletData;
typedef struct {
typedef enum { // motion progress, for modal handlers
P_NOT_STARTED,
P_STARTING, // <--
P_IN_PROGRESS, // <-- only these are sent for NDOF motion
P_FINISHING, // <--
P_FINISHED
} wmProgress;
typedef struct wmNDOFMotionData {
/* awfully similar to GHOST_TEventNDOFMotionData... */
/* Each component normally ranges from -1 to +1, but can exceed that. */
float tx, ty, tz; /* translation: -x left, +y forward, -z up */
float rx, ry, rz; /* rotation:
axis = (rx,ry,rz).normalized
amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg] */
float dt; // time since previous NDOF Motion event (or zero if this is the first)
// Each component normally ranges from -1 to +1, but can exceed that.
// These use blender standard view coordinates, with positive rotations being CCW about the axis.
float tx, ty, tz; // translation
float rx, ry, rz; // rotation:
// axis = (rx,ry,rz).normalized
// amount = (rx,ry,rz).magnitude [in revolutions, 1.0 = 360 deg]
float dt; // time since previous NDOF Motion event
wmProgress progress; // is this the first event, the last, or one of many in between?
} wmNDOFMotionData;
typedef struct wmTimer {

@ -2324,6 +2324,8 @@ static void attach_ndof_data(wmEvent* event, const GHOST_TEventNDOFMotionData* g
data->dt = ghost->dt;
data->progress = (wmProgress) ghost->progress;
event->custom = EVT_DATA_NDOF_MOTION;
event->customdata = data;
event->customdatafree = 1;