3D mouse support from merwin-spacenav branch

This commit is contained in:
Mike Erwin 2011-08-02 04:28:05 +00:00
commit 56918978b7
62 changed files with 2614 additions and 1292 deletions

@ -179,6 +179,7 @@ option(WITH_LZO "Enable fast LZO compression (used for pointcache)" ON
option(WITH_LZMA "Enable best LZMA compression, (used for pointcache)" ON)
# Misc
option(WITH_NDOF "Enable NDOF input devices (SpaceNavigator and friends)" ON)
option(WITH_RAYOPTIMIZATION "Enable use of SIMD (SSE) optimizations for the raytracer" ON)
if(UNIX AND NOT APPLE)
option(WITH_INSTALL_PORTABLE "Install redistributeable runtime, otherwise install into CMAKE_INSTALL_PREFIX" ON)
@ -452,6 +453,15 @@ if(UNIX AND NOT APPLE)
endif()
endif()
if (WITH_NDOF)
if(CMAKE_SYSTEM_NAME MATCHES "Linux")
set(NDOF /usr)
set(NDOF_INC ${NDOF}/include)
set(NDOF_LIBRARY spnav)
set(NDOF_LIBPATH ${NDOF}/lib)
endif()
endif()
# OpenSuse needs lutil, ArchLinux not, for now keep, can avoid by using --as-needed
set(PLATFORM_LINKLIBS "-lutil -lc -lm -lpthread -lstdc++")
@ -1029,6 +1039,10 @@ elseif(APPLE)
set(TIFF_LIBPATH ${TIFF}/lib)
endif()
if (WITH_NDOF)
# linker needs "-weak_framework 3DconnexionClient"
endif()
set(EXETYPE MACOSX_BUNDLE)
set(CMAKE_C_FLAGS_DEBUG "-fno-strict-aliasing -g")

@ -193,6 +193,9 @@ macro(SETUP_LIBDIRS)
if(WITH_MEM_JEMALLOC)
link_directories(${JEMALLOC_LIBPATH})
endif()
if(WITH_NDOF)
link_directories(${NDOF_LIBPATH})
endif()
if(WIN32 AND NOT UNIX)
link_directories(${PTHREADS_LIBPATH})
@ -314,6 +317,10 @@ macro(setup_liblinks
if(WITH_MEM_JEMALLOC)
target_link_libraries(${target} ${JEMALLOC_LIBRARIES})
endif()
if(WITH_NDOF)
target_link_libraries(${target} ${NDOF_LIBRARY})
endif()
if(WIN32 AND NOT UNIX)
target_link_libraries(${target} ${PTHREADS_LIBRARIES})
endif()

@ -264,7 +264,9 @@ if MACOSX_ARCHITECTURE == 'i386':
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse']
elif MACOSX_ARCHITECTURE == 'x86_64':
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-msse2']
# SpaceNavigator and related 3D mice
WITH_BF_3DMOUSE = True
#############################################################################
################### various compile settings and flags ##################
@ -294,6 +296,9 @@ if WITH_BF_QUICKTIME == True:
else:
PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS+['-framework','QuickTime']
if WITH_BF_3DMOUSE:
PLATFORM_LINKFLAGS = PLATFORM_LINKFLAGS + ['-weak_framework','3DconnexionClient']
#note to build succesfully on 10.3.9 SDK you need to patch 10.3.9 by adding the SystemStubs.a lib from 10.4
LLIBS = ['stdc++', 'SystemStubs']

@ -192,6 +192,10 @@ WITH_BF_OPENMP = True
WITH_BF_RAYOPTIMIZATION = True
BF_RAYOPTIMIZATION_SSE_FLAGS = ['-msse','-pthread']
#SpaceNavigator and friends
WITH_BF_3DMOUSE = True
BF_3DMOUSE_LIB = 'spnav'
##
CC = 'gcc'
CXX = 'g++'
@ -223,6 +227,8 @@ CXX_WARN = ['-Wno-invalid-offsetof', '-Wno-sign-compare']
##FIX_STUBS_WARNINGS = -Wno-unused
LLIBS = ['util', 'c', 'm', 'dl', 'pthread', 'stdc++']
if WITH_BF_3DMOUSE:
LLIBS = LLIBS + [BF_3DMOUSE_LIB];
##LOPTS = --dynamic
##DYNLDFLAGS = -shared $(LDFLAGS)

@ -158,11 +158,13 @@ elseif(APPLE)
intern/GHOST_SystemCocoa.mm
intern/GHOST_SystemPathsCocoa.mm
intern/GHOST_WindowCocoa.mm
intern/GHOST_NDOFManagerCocoa.mm
intern/GHOST_DisplayManagerCocoa.h
intern/GHOST_SystemCocoa.h
intern/GHOST_SystemPathsCocoa.h
intern/GHOST_WindowCocoa.h
intern/GHOST_NDOFManagerCocoa.h
)
else()
list(APPEND SRC
@ -197,11 +199,13 @@ elseif(UNIX)
intern/GHOST_SystemX11.cpp
intern/GHOST_SystemPathsX11.cpp
intern/GHOST_WindowX11.cpp
intern/GHOST_NDOFManagerX11.cpp
intern/GHOST_DisplayManagerX11.h
intern/GHOST_SystemX11.h
intern/GHOST_SystemPathsX11.h
intern/GHOST_WindowX11.h
intern/GHOST_NDOFManagerX11.h
)
if(NOT WITH_INSTALL_PORTABLE)
@ -230,6 +234,7 @@ elseif(WIN32)
intern/GHOST_SystemPathsWin32.cpp
intern/GHOST_WindowWin32.cpp
intern/GHOST_DropTargetWin32.cpp
intern/GHOST_NDOFManagerWin32.cpp
intern/GHOST_DisplayManagerWin32.h
intern/GHOST_DropTargetWin32.h
@ -237,6 +242,7 @@ elseif(WIN32)
intern/GHOST_SystemPathsWin32.h
intern/GHOST_WindowWin32.h
intern/GHOST_TaskbarWin32.h
intern/GHOST_NDOFManagerWin32.h
)
endif()

@ -288,21 +288,6 @@ extern GHOST_TSuccess GHOST_SetProgressBar(GHOST_WindowHandle windowhandle, floa
* @param windowhandle The handle to the window
*/
extern GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle);
/***************************************************************************************
** N-degree of freedom device management functionality
***************************************************************************************/
/**
* Open N-degree of freedom devices
*/
extern int GHOST_OpenNDOF(GHOST_SystemHandle systemhandle,
GHOST_WindowHandle windowhandle,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen
);
/***************************************************************************************
** Cursor management functionality

@ -298,22 +298,6 @@ public:
*/
virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer) = 0;
/***************************************************************************************
** N-degree of freedom device management functionality
***************************************************************************************/
/**
* Starts the N-degree of freedom device manager
*/
virtual int openNDOF(GHOST_IWindow*,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen
// original patch only
// GHOST_NDOFEventHandler_fp setNdofEventHandler
) = 0;
/***************************************************************************************
** Cursor management functionality
***************************************************************************************/

@ -47,11 +47,6 @@ typedef unsigned short GHOST_TUns16;
typedef int GHOST_TInt32;
typedef unsigned int GHOST_TUns32;
#ifdef WIN32
#define WM_BLND_NDOF_AXIS WM_USER + 1
#define WM_BLND_NDOF_BTN WM_USER + 2
#endif
#if defined(WIN32) && !defined(FREE_WINDOWS)
typedef __int64 GHOST_TInt64;
typedef unsigned __int64 GHOST_TUns64;
@ -440,37 +435,33 @@ typedef struct {
GHOST_TUns8 **strings;
} GHOST_TStringArray;
/* original patch used floats, but the driver return ints and uns. We will calibrate in view, no sense on doing conversions twice */
/* as all USB device controls are likely to use ints, this is also more future proof */
//typedef struct {
// /** N-degree of freedom device data */
// float tx, ty, tz; /** -x left, +y up, +z forward */
// float rx, ry, rz;
// float dt;
//} GHOST_TEventNDOFData;
typedef enum {
GHOST_kNotStarted,
GHOST_kStarting,
GHOST_kInProgress,
GHOST_kFinishing,
GHOST_kFinished
} GHOST_TProgress;
typedef struct {
/** N-degree of freedom device data v2*/
int changed;
GHOST_TUns64 client;
GHOST_TUns64 address;
GHOST_TInt16 tx, ty, tz; /** -x left, +y up, +z forward */
GHOST_TInt16 rx, ry, rz;
GHOST_TInt16 buttons;
GHOST_TUns64 time;
GHOST_TUns64 delta;
} GHOST_TEventNDOFData;
/** 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 int (*GHOST_NDOFLibraryInit_fp)(void);
typedef void (*GHOST_NDOFLibraryShutdown_fp)(void* deviceHandle);
typedef void* (*GHOST_NDOFDeviceOpen_fp)(void* platformData);
typedef enum { GHOST_kPress, GHOST_kRelease } GHOST_TButtonAction;
// good for mouse or other buttons too, hmmm?
// original patch windows callback. In mac os X version the callback is internal to the plug-in and post an event to main thead.
// not necessary faster, but better integration with other events.
//typedef int (*GHOST_NDOFEventHandler_fp)(float* result7, void* deviceHandle, unsigned int message, unsigned int* wParam, unsigned long* lParam);
//typedef void (*GHOST_NDOFCallBack_fp)(GHOST_TEventNDOFDataV2 *VolDatas);
typedef struct {
GHOST_TButtonAction action;
short button;
} GHOST_TEventNDOFButtonData;
typedef struct {
/** The key code. */

@ -11,7 +11,7 @@ if window_system == 'darwin':
sources += env.Glob('intern/*.mm')
pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget']
pf = ['GHOST_DisplayManager', 'GHOST_System', 'GHOST_SystemPaths', 'GHOST_Window', 'GHOST_DropTarget', 'GHOST_NDOFManager']
defs=['_USE_MATH_DEFINES']
incs = '. ../string #extern/glew/include #source/blender/imbuf #source/blender/makesdna ' + env['BF_OPENGL_INC']

@ -275,23 +275,6 @@ GHOST_TSuccess GHOST_EndProgressBar(GHOST_WindowHandle windowhandle)
}
int GHOST_OpenNDOF(GHOST_SystemHandle systemhandle, GHOST_WindowHandle windowhandle,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
//original patch only
/* GHOST_NDOFEventHandler_fp setNdofEventHandler)*/
{
GHOST_ISystem* system = (GHOST_ISystem*) systemhandle;
return system->openNDOF((GHOST_IWindow*) windowhandle,
setNdofLibraryInit, setNdofLibraryShutdown, setNdofDeviceOpen);
// original patch
// setNdofLibraryInit, setNdofLibraryShutdown, setNdofDeviceOpen, setNdofEventHandler);
}
GHOST_TStandardCursor GHOST_GetCursorShape(GHOST_WindowHandle windowhandle)
{
GHOST_IWindow* window = (GHOST_IWindow*) windowhandle;

@ -35,8 +35,11 @@
#include "GHOST_DisplayManagerWin32.h"
#include "GHOST_Debug.h"
// We do not support multiple monitors at the moment
#define _WIN32_WINNT 0x501 // require Windows XP or newer
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
// We do not support multiple monitors at the moment
#define COMPILE_MULTIMON_STUBS
#ifndef FREE_WINDOWS
#include <multimon.h>

@ -33,6 +33,7 @@
#include "GHOST_Debug.h"
#include "GHOST_DropTargetWin32.h"
#include <ShellApi.h>
#ifdef GHOST_DEBUG
// utility

@ -33,7 +33,6 @@
#ifndef _GHOST_DROP_TARGET_WIN32_H_
#define _GHOST_DROP_TARGET_WIN32_H_
#include <windows.h>
#include <string.h>
#include <GHOST_Types.h>
#include "GHOST_WindowWin32.h"

@ -42,7 +42,7 @@
#include "GHOST_EventManager.h"
#include <algorithm>
#include "GHOST_Debug.h"
#include <stdio.h> // [mce] temp debug
GHOST_EventManager::GHOST_EventManager()
{

@ -19,11 +19,6 @@
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_EventNDOF.h
* \ingroup GHOST
*/
#ifndef _GHOST_EVENT_NDOF_H_
@ -31,32 +26,33 @@
#include "GHOST_Event.h"
/**
* N-degree of freedom device event.
*/
class GHOST_EventNDOF : public GHOST_Event
{
public:
/**
* Constructor.
* @param msec The time this event was generated.
* @param type The type of this event.
* @param x The x-coordinate of the location the cursor was at at the time of the event.
* @param y The y-coordinate of the location the cursor was at at the time of the event.
*/
GHOST_EventNDOF(GHOST_TUns64 msec, GHOST_TEventType type, GHOST_IWindow* window,
GHOST_TEventNDOFData data)
: GHOST_Event(msec, type, window)
{
m_ndofEventData = data;
m_data = &m_ndofEventData;
}
protected:
/** translation & rotation from the device. */
GHOST_TEventNDOFData m_ndofEventData;
};
class GHOST_EventNDOFMotion : public GHOST_Event
{
protected:
GHOST_TEventNDOFMotionData m_axisData;
public:
GHOST_EventNDOFMotion(GHOST_TUns64 time, GHOST_IWindow* window)
: GHOST_Event(time, GHOST_kEventNDOFMotion, window)
{
m_data = &m_axisData;
}
};
class GHOST_EventNDOFButton : public GHOST_Event
{
protected:
GHOST_TEventNDOFButtonData m_buttonData;
public:
GHOST_EventNDOFButton(GHOST_TUns64 time, GHOST_IWindow* window)
: GHOST_Event(time, GHOST_kEventNDOFButton, window)
{
m_data = &m_buttonData;
}
};
#endif // _GHOST_EVENT_NDOF_H_

@ -15,122 +15,471 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): none yet.
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_NDOFManager.cpp
* \ingroup GHOST
*/
#include <stdio.h> /* just for printf */
#include "GHOST_NDOFManager.h"
#include "GHOST_EventNDOF.h"
#include "GHOST_EventKey.h"
#include "GHOST_WindowManager.h"
#include <string.h> // for memory functions
#include <stdio.h> // for error/info reporting
#include <math.h>
// the variable is outside the class because it must be accessed from plugin
static volatile GHOST_TEventNDOFData currentNdofValues = {0,0,0,0,0,0,0,0,0,0,0};
#if !defined(_WIN32) && !defined(__APPLE__)
#include "GHOST_SystemX11.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
namespace
{
GHOST_NDOFLibraryInit_fp ndofLibraryInit = 0;
GHOST_NDOFLibraryShutdown_fp ndofLibraryShutdown = 0;
GHOST_NDOFDeviceOpen_fp ndofDeviceOpen = 0;
}
GHOST_NDOFManager::GHOST_NDOFManager()
{
m_DeviceHandle = 0;
// discover the API from the plugin
ndofLibraryInit = 0;
ndofLibraryShutdown = 0;
ndofDeviceOpen = 0;
}
GHOST_NDOFManager::~GHOST_NDOFManager()
{
if (ndofLibraryShutdown)
ndofLibraryShutdown(m_DeviceHandle);
m_DeviceHandle = 0;
}
int
GHOST_NDOFManager::deviceOpen(GHOST_IWindow* window,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
{
int Pid;
ndofLibraryInit = setNdofLibraryInit;
ndofLibraryShutdown = setNdofLibraryShutdown;
ndofDeviceOpen = setNdofDeviceOpen;
if (ndofLibraryInit && ndofDeviceOpen)
{
Pid= ndofLibraryInit();
#if 0
printf("%i client \n", Pid);
#ifdef DEBUG_NDOF_BUTTONS
static const char* ndof_button_names[] = {
// used internally, never sent
"NDOF_BUTTON_NONE",
// these two are available from any 3Dconnexion device
"NDOF_BUTTON_MENU",
"NDOF_BUTTON_FIT",
// standard views
"NDOF_BUTTON_TOP",
"NDOF_BUTTON_BOTTOM",
"NDOF_BUTTON_LEFT",
"NDOF_BUTTON_RIGHT",
"NDOF_BUTTON_FRONT",
"NDOF_BUTTON_BACK",
// more views
"NDOF_BUTTON_ISO1",
"NDOF_BUTTON_ISO2",
// 90 degree rotations
"NDOF_BUTTON_ROLL_CW",
"NDOF_BUTTON_ROLL_CCW",
"NDOF_BUTTON_SPIN_CW",
"NDOF_BUTTON_SPIN_CCW",
"NDOF_BUTTON_TILT_CW",
"NDOF_BUTTON_TILT_CCW",
// device control
"NDOF_BUTTON_ROTATE",
"NDOF_BUTTON_PANZOOM",
"NDOF_BUTTON_DOMINANT",
"NDOF_BUTTON_PLUS",
"NDOF_BUTTON_MINUS",
// general-purpose buttons
"NDOF_BUTTON_1",
"NDOF_BUTTON_2",
"NDOF_BUTTON_3",
"NDOF_BUTTON_4",
"NDOF_BUTTON_5",
"NDOF_BUTTON_6",
"NDOF_BUTTON_7",
"NDOF_BUTTON_8",
"NDOF_BUTTON_9",
"NDOF_BUTTON_10",
};
#endif
#if defined(WITH_HEADLESS)
/* do nothing */
#elif defined(_WIN32) || defined(__APPLE__)
m_DeviceHandle = ndofDeviceOpen((void *)&currentNdofValues);
#elif defined(WITH_GHOST_SDL)
/* do nothing */
#else
GHOST_SystemX11 *sys;
sys = static_cast<GHOST_SystemX11*>(GHOST_ISystem::getSystem());
void *ndofInfo = sys->prepareNdofInfo(&currentNdofValues);
m_DeviceHandle = ndofDeviceOpen(ndofInfo);
static const NDOF_ButtonT SpaceNavigator_HID_map[] =
{
NDOF_BUTTON_MENU,
NDOF_BUTTON_FIT
};
static const NDOF_ButtonT SpaceExplorer_HID_map[] =
{
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_TOP,
NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT,
NDOF_BUTTON_FRONT,
NDOF_BUTTON_NONE, // esc key
NDOF_BUTTON_NONE, // alt key
NDOF_BUTTON_NONE, // shift key
NDOF_BUTTON_NONE, // ctrl key
NDOF_BUTTON_FIT,
NDOF_BUTTON_MENU,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS,
NDOF_BUTTON_ROTATE
};
static const NDOF_ButtonT SpacePilotPro_HID_map[] =
{
NDOF_BUTTON_MENU,
NDOF_BUTTON_FIT,
NDOF_BUTTON_TOP,
NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT,
NDOF_BUTTON_FRONT,
NDOF_BUTTON_BOTTOM,
NDOF_BUTTON_BACK,
NDOF_BUTTON_ROLL_CW,
NDOF_BUTTON_ROLL_CCW,
NDOF_BUTTON_ISO1,
NDOF_BUTTON_ISO2,
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_3,
NDOF_BUTTON_4,
NDOF_BUTTON_5,
NDOF_BUTTON_6,
NDOF_BUTTON_7,
NDOF_BUTTON_8,
NDOF_BUTTON_9,
NDOF_BUTTON_10,
NDOF_BUTTON_NONE, // esc key
NDOF_BUTTON_NONE, // alt key
NDOF_BUTTON_NONE, // shift key
NDOF_BUTTON_NONE, // ctrl key
NDOF_BUTTON_ROTATE,
NDOF_BUTTON_PANZOOM,
NDOF_BUTTON_DOMINANT,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS
};
static const NDOF_ButtonT SpacePilot_HID_map[] =
// this is the older SpacePilot (sans Pro)
// thanks to polosson for the info in this table
{
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_3,
NDOF_BUTTON_4,
NDOF_BUTTON_5,
NDOF_BUTTON_6,
NDOF_BUTTON_TOP,
NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT,
NDOF_BUTTON_FRONT,
NDOF_BUTTON_NONE, // esc key
NDOF_BUTTON_NONE, // alt key
NDOF_BUTTON_NONE, // shift key
NDOF_BUTTON_NONE, // ctrl key
NDOF_BUTTON_FIT,
NDOF_BUTTON_MENU,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS,
NDOF_BUTTON_DOMINANT,
NDOF_BUTTON_ROTATE,
NDOF_BUTTON_NONE // the CONFIG button -- what does it do?
};
GHOST_NDOFManager::GHOST_NDOFManager(GHOST_System& sys)
: m_system(sys)
, m_deviceType(NDOF_UnknownDevice) // each platform has its own device detection code
, m_buttonCount(0)
, m_buttonMask(0)
, m_buttons(0)
, m_motionTime(0)
, m_prevMotionTime(0)
, m_motionState(GHOST_kNotStarted)
, m_motionEventPending(false)
, m_deadZone(0.f)
{
// to avoid the rare situation where one triple is updated and
// the other is not, initialize them both here:
memset(m_translation, 0, sizeof(m_translation));
memset(m_rotation, 0, sizeof(m_rotation));
#ifdef WITH_BF_3DMOUSE
puts("WITH_BF_3DMOUSE is defined!");
#else
puts("WITH_BF_3DMOUSE is not defined.");
#endif
}
bool GHOST_NDOFManager::setDevice(unsigned short vendor_id, unsigned short product_id)
{
// default to NDOF_UnknownDevice so rogue button events will get discarded
// "mystery device" owners can help build a HID_map for their hardware
switch (vendor_id)
{
case 0x046D: // Logitech (3Dconnexion)
switch (product_id)
{
// -- current devices --
case 0xC626:
puts("ndof: using SpaceNavigator");
m_deviceType = NDOF_SpaceNavigator;
m_buttonCount = 2;
break;
case 0xC628:
puts("ndof: using SpaceNavigator for Notebooks");
m_deviceType = NDOF_SpaceNavigator; // for Notebooks
m_buttonCount = 2;
break;
case 0xC627:
puts("ndof: using SpaceExplorer");
m_deviceType = NDOF_SpaceExplorer;
m_buttonCount = 15;
break;
case 0xC629:
puts("ndof: using SpacePilotPro");
m_deviceType = NDOF_SpacePilotPro;
m_buttonCount = 31;
break;
// -- older devices --
case 0xC625:
puts("ndof: using SpacePilot");
m_deviceType = NDOF_SpacePilot;
m_buttonCount = 21;
break;
case 0xC623:
puts("ndof: SpaceTraveler not supported, please file a bug report");
m_buttonCount = 8;
break;
default:
printf("ndof: unknown Logitech product %04hx\n", product_id);
}
break;
default:
printf("ndof: unknown device %04hx:%04hx\n", vendor_id, product_id);
}
if (m_deviceType == NDOF_UnknownDevice)
return false;
else
{
m_buttonMask = ~(-1 << m_buttonCount);
#ifdef DEBUG_NDOF_BUTTONS
printf("ndof: %d buttons -> hex:%X\n", m_buttonCount, m_buttonMask);
#endif
return (Pid > 0) ? 0 : 1;
} else
return 1;
}
return true;
}
}
bool
GHOST_NDOFManager::available() const
{
return m_DeviceHandle != 0;
}
void GHOST_NDOFManager::updateTranslation(short t[3], GHOST_TUns64 time)
{
memcpy(m_translation, t, sizeof(m_translation));
m_motionTime = time;
m_motionEventPending = true;
}
bool
GHOST_NDOFManager::event_present() const
{
if( currentNdofValues.changed >0) {
printf("time %llu but%u x%i y%i z%i rx%i ry%i rz%i \n" ,
currentNdofValues.time, currentNdofValues.buttons,
currentNdofValues.tx,currentNdofValues.ty,currentNdofValues.tz,
currentNdofValues.rx,currentNdofValues.ry,currentNdofValues.rz);
return true;
}else
return false;
void GHOST_NDOFManager::updateRotation(short r[3], GHOST_TUns64 time)
{
memcpy(m_rotation, r, sizeof(m_rotation));
m_motionTime = time;
m_motionEventPending = true;
}
}
void GHOST_NDOFManager::sendButtonEvent(NDOF_ButtonT button, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
{
GHOST_EventNDOFButton* event = new GHOST_EventNDOFButton(time, window);
GHOST_TEventNDOFButtonData* data = (GHOST_TEventNDOFButtonData*) event->getData();
void GHOST_NDOFManager::GHOST_NDOFGetDatas(GHOST_TEventNDOFData &datas) const
{
datas.tx = currentNdofValues.tx;
datas.ty = currentNdofValues.ty;
datas.tz = currentNdofValues.tz;
datas.rx = currentNdofValues.rx;
datas.ry = currentNdofValues.ry;
datas.rz = currentNdofValues.rz;
datas.buttons = currentNdofValues.buttons;
datas.client = currentNdofValues.client;
datas.address = currentNdofValues.address;
datas.time = currentNdofValues.time;
datas.delta = currentNdofValues.delta;
}
data->action = press ? GHOST_kPress : GHOST_kRelease;
data->button = button;
#ifdef DEBUG_NDOF_BUTTONS
printf("%s %s\n", ndof_button_names[button], press ? "pressed" : "released");
#endif
m_system.pushEvent(event);
}
void GHOST_NDOFManager::sendKeyEvent(GHOST_TKey key, bool press, GHOST_TUns64 time, GHOST_IWindow* window)
{
GHOST_TEventType type = press ? GHOST_kEventKeyDown : GHOST_kEventKeyUp;
GHOST_EventKey* event = new GHOST_EventKey(time, type, window, key);
#ifdef DEBUG_NDOF_BUTTONS
printf("keyboard %s\n", press ? "down" : "up");
#endif
m_system.pushEvent(event);
}
void GHOST_NDOFManager::updateButton(int button_number, bool press, GHOST_TUns64 time)
{
GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
#ifdef DEBUG_NDOF_BUTTONS
if (m_deviceType != NDOF_UnknownDevice)
printf("ndof: button %d -> ", button_number);
#endif
switch (m_deviceType)
{
case NDOF_SpaceNavigator:
sendButtonEvent(SpaceNavigator_HID_map[button_number], press, time, window);
break;
case NDOF_SpaceExplorer:
switch (button_number)
{
case 6: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
case 7: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
case 8: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
case 9: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
default: sendButtonEvent(SpaceExplorer_HID_map[button_number], press, time, window);
}
break;
case NDOF_SpacePilotPro:
switch (button_number)
{
case 22: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
case 23: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
case 24: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
case 25: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
default: sendButtonEvent(SpacePilotPro_HID_map[button_number], press, time, window);
}
break;
case NDOF_SpacePilot:
switch (button_number)
{
case 10: sendKeyEvent(GHOST_kKeyEsc, press, time, window); break;
case 11: sendKeyEvent(GHOST_kKeyLeftAlt, press, time, window); break;
case 12: sendKeyEvent(GHOST_kKeyLeftShift, press, time, window); break;
case 13: sendKeyEvent(GHOST_kKeyLeftControl, press, time, window); break;
case 20: puts("ndof: ignoring CONFIG button"); break;
default: sendButtonEvent(SpacePilot_HID_map[button_number], press, time, window);
}
break;
case NDOF_UnknownDevice:
printf("ndof: button %d on unknown device (ignoring)\n", button_number);
}
int mask = 1 << button_number;
if (press)
m_buttons |= mask; // set this button's bit
else
m_buttons &= ~mask; // clear this button's bit
}
void GHOST_NDOFManager::updateButtons(int button_bits, GHOST_TUns64 time)
{
button_bits &= m_buttonMask; // discard any "garbage" bits
int diff = m_buttons ^ button_bits;
for (int button_number = 0; button_number < m_buttonCount; ++button_number)
{
int mask = 1 << button_number;
if (diff & mask)
{
bool press = button_bits & mask;
updateButton(button_number, press, time);
}
}
}
void GHOST_NDOFManager::setDeadZone(float dz)
{
if (dz < 0.f)
// negative values don't make sense, so clamp at zero
dz = 0.f;
else if (dz > 0.5f)
// warn the rogue user/programmer, but allow it
printf("ndof: dead zone of %.2f is rather high...\n", dz);
m_deadZone = dz;
printf("ndof: dead zone set to %.2f\n", dz);
}
static bool atHomePosition(GHOST_TEventNDOFMotionData* ndof)
{
#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)
{
if (threshold == 0.f)
return atHomePosition(ndof);
else
{
#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_motionEventPending)
return false;
m_motionEventPending = false; // any pending motion is handled right now
GHOST_IWindow* window = m_system.getWindowManager()->getActiveWindow();
if (window == NULL)
return false; // delivery will fail, so don't bother sending
GHOST_EventNDOFMotion* event = new GHOST_EventNDOFMotion(m_motionTime, window);
GHOST_TEventNDOFMotionData* data = (GHOST_TEventNDOFMotionData*) event->getData();
// scale axis values here to normalize them to around +/- 1
// they are scaled again for overall sensitivity in the WM based on user prefs
const float scale = 1.f / 350.f; // 3Dconnexion devices send +/- 350 usually
data->tx = scale * m_translation[0];
data->ty = scale * m_translation[1];
data->tz = scale * m_translation[2];
data->rx = scale * m_rotation[0];
data->ry = scale * m_rotation[1];
data->rz = scale * m_rotation[2];
data->dt = 0.001f * (m_motionTime - m_prevMotionTime); // in seconds
bool handMotion = !nearHomePosition(data, m_deadZone);
// determine what kind of motion event to send (Starting, InProgress, Finishing)
// and where that leaves this NDOF manager (NotStarted, InProgress, Finished)
switch (m_motionState)
{
case GHOST_kNotStarted:
case GHOST_kFinished:
if (handMotion)
{
data->progress = GHOST_kStarting;
m_motionState = GHOST_kInProgress;
// prev motion time will be ancient, so just make up something reasonable
data->dt = 0.0125f;
}
else
{
// send no event and keep current state
delete event;
return false;
}
break;
case GHOST_kInProgress:
if (handMotion)
{
data->progress = GHOST_kInProgress;
// keep InProgress state
}
else
{
data->progress = GHOST_kFinishing;
m_motionState = GHOST_kFinished;
}
break;
}
#ifdef DEBUG_NDOF_MOTION
printf("ndof motion sent -- %s\n", progress_string[data->progress]);
// show details about this motion event
printf(" 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);
#endif
m_system.pushEvent(event);
m_prevMotionTime = m_motionTime;
return true;
}

@ -15,43 +15,144 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s): none yet.
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file ghost/intern/GHOST_NDOFManager.h
* \ingroup GHOST
*/
#ifndef _GHOST_NDOFMANAGER_H_
#define _GHOST_NDOFMANAGER_H_
#include "GHOST_System.h"
#include "GHOST_IWindow.h"
// #define DEBUG_NDOF_MOTION
#define DEBUG_NDOF_BUTTONS
typedef enum {
NDOF_UnknownDevice, // <-- motion will work fine, buttons are ignored
// current devices
NDOF_SpaceNavigator,
NDOF_SpaceExplorer,
NDOF_SpacePilotPro,
// older devices
NDOF_SpacePilot
} NDOF_DeviceT;
// NDOF device button event types
typedef enum {
// used internally, never sent
NDOF_BUTTON_NONE,
// these two are available from any 3Dconnexion device
NDOF_BUTTON_MENU,
NDOF_BUTTON_FIT,
// standard views
NDOF_BUTTON_TOP,
NDOF_BUTTON_BOTTOM,
NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT,
NDOF_BUTTON_FRONT,
NDOF_BUTTON_BACK,
// more views
NDOF_BUTTON_ISO1,
NDOF_BUTTON_ISO2,
// 90 degree rotations
// these don't all correspond to physical buttons
NDOF_BUTTON_ROLL_CW,
NDOF_BUTTON_ROLL_CCW,
NDOF_BUTTON_SPIN_CW,
NDOF_BUTTON_SPIN_CCW,
NDOF_BUTTON_TILT_CW,
NDOF_BUTTON_TILT_CCW,
// device control
NDOF_BUTTON_ROTATE,
NDOF_BUTTON_PANZOOM,
NDOF_BUTTON_DOMINANT,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS,
// general-purpose buttons
// users can assign functions via keymap editor
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_3,
NDOF_BUTTON_4,
NDOF_BUTTON_5,
NDOF_BUTTON_6,
NDOF_BUTTON_7,
NDOF_BUTTON_8,
NDOF_BUTTON_9,
NDOF_BUTTON_10,
} NDOF_ButtonT;
class GHOST_NDOFManager
{
public:
GHOST_NDOFManager();
virtual ~GHOST_NDOFManager();
GHOST_NDOFManager(GHOST_System&);
int deviceOpen(GHOST_IWindow* window,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen);
void GHOST_NDOFGetDatas(GHOST_TEventNDOFData &datas) const;
bool available() const;
bool event_present() const;
virtual ~GHOST_NDOFManager() {};
// whether multi-axis functionality is available (via the OS or driver)
// does not imply that a device is plugged in or being used
virtual bool available() = 0;
// each platform's device detection should call this
// use standard USB/HID identifiers
bool setDevice(unsigned short vendor_id, unsigned short product_id);
// filter out small/accidental/uncalibrated motions by
// setting up a "dead zone" around home position
// set to 0 to disable
// 0.1 is a safe and reasonable value
void setDeadZone(float);
// the latest raw axis data from the device
// NOTE: axis data should be in blender view coordinates
// +X is to the right
// +Y is up
// +Z is out of the screen
// for rotations, look from origin to each +axis
// rotations are + when CCW, - when CW
// each platform is responsible for getting axis data into this form
// these values should not be scaled (just shuffled or flipped)
void updateTranslation(short t[3], GHOST_TUns64 time);
void updateRotation(short r[3], GHOST_TUns64 time);
// the latest raw button data from the device
// use HID button encoding (not NDOF_ButtonT)
void updateButton(int button_number, bool press, GHOST_TUns64 time);
void updateButtons(int button_bits, GHOST_TUns64 time);
// NDOFButton events are sent immediately
// processes and sends most recent raw data as an NDOFMotion event
// returns whether an event was sent
bool sendMotionEvent();
protected:
void* m_DeviceHandle;
GHOST_System& m_system;
private:
void sendButtonEvent(NDOF_ButtonT, bool press, GHOST_TUns64 time, GHOST_IWindow*);
void sendKeyEvent(GHOST_TKey, bool press, GHOST_TUns64 time, GHOST_IWindow*);
NDOF_DeviceT m_deviceType;
int m_buttonCount;
int m_buttonMask;
short m_translation[3];
short m_rotation[3];
int m_buttons; // bit field
GHOST_TUns64 m_motionTime; // in milliseconds
GHOST_TUns64 m_prevMotionTime; // time of most recent Motion event sent
GHOST_TProgress m_motionState;
bool m_motionEventPending;
float m_deadZone; // discard motion with each component < this
};
#endif

@ -0,0 +1,48 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef _GHOST_NDOFMANAGERCOCOA_H_
#define _GHOST_NDOFMANAGERCOCOA_H_
#include "GHOST_NDOFManager.h"
// Event capture is handled within the NDOF manager on Macintosh,
// so there's no need for SystemCocoa to look for them.
class GHOST_NDOFManagerCocoa : public GHOST_NDOFManager
{
public:
GHOST_NDOFManagerCocoa(GHOST_System&);
~GHOST_NDOFManagerCocoa();
// whether multi-axis functionality is available (via the OS or driver)
// does not imply that a device is plugged in or being used
bool available();
private:
unsigned short m_clientID;
};
#endif

@ -0,0 +1,172 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "GHOST_NDOFManagerCocoa.h"
#include "GHOST_SystemCocoa.h"
extern "C" {
#include <3DconnexionClient/ConnexionClientAPI.h>
#include <stdio.h>
}
// static functions need to talk to these objects:
static GHOST_SystemCocoa* ghost_system = NULL;
static GHOST_NDOFManager* ndof_manager = NULL;
// 3Dconnexion drivers before 10.x are "old"
// not all buttons will work
static bool has_old_driver = true;
static void NDOF_DeviceAdded(io_connect_t connection)
{
printf("ndof: device added\n"); // change these: printf --> informational reports
#if 0 // device preferences will be useful some day
ConnexionDevicePrefs p;
ConnexionGetCurrentDevicePrefs(kDevID_AnyDevice, &p);
#endif
// determine exactly which device is plugged in
SInt32 result = 0;
ConnexionControl(kConnexionCtlGetDeviceID, 0, &result);
unsigned short vendorID = result >> 16;
unsigned short productID = result & 0xffff;
ndof_manager->setDevice(vendorID, productID);
}
static void NDOF_DeviceRemoved(io_connect_t connection)
{
printf("ndof: device removed\n");
}
static void NDOF_DeviceEvent(io_connect_t connection, natural_t messageType, void* messageArgument)
{
switch (messageType)
{
case kConnexionMsgDeviceState:
{
ConnexionDeviceState* s = (ConnexionDeviceState*)messageArgument;
GHOST_TUns64 now = ghost_system->getMilliSeconds();
switch (s->command)
{
case kConnexionCmdHandleAxis:
{
// convert to blender view coordinates
short t[3] = {s->axis[0], -(s->axis[2]), s->axis[1]};
short r[3] = {-(s->axis[3]), s->axis[5], -(s->axis[4])};
ndof_manager->updateTranslation(t, now);
ndof_manager->updateRotation(r, now);
ghost_system->notifyExternalEventProcessed();
break;
}
case kConnexionCmdHandleButtons:
{
int button_bits = has_old_driver ? s->buttons8 : s->buttons;
ndof_manager->updateButtons(button_bits, now);
ghost_system->notifyExternalEventProcessed();
break;
}
case kConnexionCmdAppSpecific:
printf("ndof: app-specific command, param = %hd, value = %d\n", s->param, s->value);
break;
default:
printf("ndof: mystery device command %d\n", s->command);
}
break;
}
case kConnexionMsgPrefsChanged:
// printf("ndof: prefs changed\n"); // this includes app switches
// TODO: look through updated prefs for things blender cares about
break;
case kConnexionMsgCalibrateDevice:
printf("ndof: calibrate\n"); // but what should blender do?
break;
case kConnexionMsgDoMapping:
// printf("ndof: driver did something\n");
// sent when the driver itself consumes an NDOF event
// and performs whatever action is set in user prefs
// 3Dx header file says to ignore these
break;
default:
printf("ndof: mystery event %d\n", messageType);
}
}
GHOST_NDOFManagerCocoa::GHOST_NDOFManagerCocoa(GHOST_System& sys)
: GHOST_NDOFManager(sys)
{
if (available())
{
// give static functions something to talk to:
ghost_system = dynamic_cast<GHOST_SystemCocoa*>(&sys);
ndof_manager = this;
OSErr error = InstallConnexionHandlers(NDOF_DeviceEvent, NDOF_DeviceAdded, NDOF_DeviceRemoved);
if (error)
{
printf("ndof: error %d while installing handlers\n", error);
return;
}
// Pascal string *and* a four-letter constant. How old-skool.
m_clientID = RegisterConnexionClient('blnd', (UInt8*) "\007blender",
kConnexionClientModeTakeOver, kConnexionMaskAll);
// printf("ndof: client id = %d\n", m_clientID);
if (SetConnexionClientButtonMask != NULL)
{
has_old_driver = false;
SetConnexionClientButtonMask(m_clientID, kConnexionMaskAllButtons);
}
else
printf("ndof: old 3Dx driver installed, some buttons may not work\n");
}
else
{
printf("ndof: 3Dx driver not found\n");
// This isn't a hard error, just means the user doesn't have a 3D mouse.
}
}
GHOST_NDOFManagerCocoa::~GHOST_NDOFManagerCocoa()
{
UnregisterConnexionClient(m_clientID);
CleanupConnexionHandlers();
ghost_system = NULL;
ndof_manager = NULL;
}
bool GHOST_NDOFManagerCocoa::available()
{
// extern OSErr InstallConnexionHandlers() __attribute__((weak_import));
// ^^ not needed since the entire framework is weak-linked
return InstallConnexionHandlers != NULL;
// this means that the driver is installed and dynamically linked to blender
}

@ -0,0 +1,37 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "GHOST_NDOFManagerWin32.h"
GHOST_NDOFManagerWin32::GHOST_NDOFManagerWin32(GHOST_System& sys)
: GHOST_NDOFManager(sys)
{}
// whether multi-axis functionality is available (via the OS or driver)
// does not imply that a device is plugged in or being used
bool GHOST_NDOFManagerWin32::available()
{
// always available since RawInput is built into Windows
return true;
}

@ -0,0 +1,38 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef _GHOST_NDOFMANAGERWIN32_H_
#define _GHOST_NDOFMANAGERWIN32_H_
#include "GHOST_NDOFManager.h"
class GHOST_NDOFManagerWin32 : public GHOST_NDOFManager
{
public:
GHOST_NDOFManagerWin32(GHOST_System&);
bool available();
};
#endif

@ -0,0 +1,110 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
#include "GHOST_NDOFManagerX11.h"
#include "GHOST_SystemX11.h"
#include <spnav.h>
#include <stdio.h>
GHOST_NDOFManagerX11::GHOST_NDOFManagerX11(GHOST_System& sys)
: GHOST_NDOFManager(sys)
, m_available(false)
{
setDeadZone(0.1f); // how to calibrate on Linux? throw away slight motion!
if (spnav_open() != -1)
{
// determine exactly which device (if any) is plugged in
#define MAX_LINE_LENGTH 100
// look for USB devices with Logitech's vendor ID
FILE* command_output = popen("lsusb -d 046d:","r");
if (command_output)
{
char line[MAX_LINE_LENGTH] = {0};
while (fgets(line, MAX_LINE_LENGTH, command_output))
{
unsigned short vendor_id = 0, product_id = 0;
if (sscanf(line, "Bus %*d Device %*d: ID %hx:%hx", &vendor_id, &product_id) == 2)
if (setDevice(vendor_id, product_id))
{
m_available = true;
break; // stop looking once the first 3D mouse is found
}
}
pclose(command_output);
}
}
else
{
printf("ndof: spacenavd not found\n");
// This isn't a hard error, just means the user doesn't have a 3D mouse.
}
}
GHOST_NDOFManagerX11::~GHOST_NDOFManagerX11()
{
if (m_available)
spnav_close();
}
bool GHOST_NDOFManagerX11::available()
{
return m_available;
}
//bool GHOST_NDOFManagerX11::identifyDevice()
// {
//
// }
bool GHOST_NDOFManagerX11::processEvents()
{
GHOST_TUns64 now = m_system.getMilliSeconds();
bool anyProcessed = false;
spnav_event e;
while (spnav_poll_event(&e))
{
switch (e.type)
{
case SPNAV_EVENT_MOTION:
{
// convert to blender view coords
short t[3] = {e.motion.x, e.motion.y, -e.motion.z};
short r[3] = {-e.motion.rx, -e.motion.ry, e.motion.rz};
updateTranslation(t, now);
updateRotation(r, now);
break;
}
case SPNAV_EVENT_BUTTON:
updateButton(e.button.bnum, e.button.press, now);
break;
}
anyProcessed = true;
}
return anyProcessed;
}

@ -0,0 +1,47 @@
/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Contributor(s):
* Mike Erwin
*
* ***** END GPL LICENSE BLOCK *****
*/
#ifndef _GHOST_NDOFMANAGERX11_H_
#define _GHOST_NDOFMANAGERX11_H_
#include "GHOST_NDOFManager.h"
// Event capture is handled within the NDOF manager on Linux,
// so there's no need for SystemX11 to look for them.
class GHOST_NDOFManagerX11 : public GHOST_NDOFManager
{
public:
GHOST_NDOFManagerX11(GHOST_System&);
~GHOST_NDOFManagerX11();
bool available();
bool processEvents();
private:
// bool identifyDevice();
bool m_available;
};
#endif

@ -194,12 +194,15 @@ bool GHOST_System::getFullScreen(void)
bool GHOST_System::dispatchEvents()
{
bool handled;
if (m_eventManager) {
handled = m_eventManager->dispatchEvents();
bool handled = false;
// NDOF Motion event is sent only once per dispatch, so do it now:
if (m_ndofManager) {
handled |= m_ndofManager->sendMotionEvent();
}
else {
handled = false;
if (m_eventManager) {
handled |= m_eventManager->dispatchEvents();
}
m_timerManager->fireTimers(getMilliSeconds());
@ -243,18 +246,6 @@ GHOST_TSuccess GHOST_System::pushEvent(GHOST_IEvent* event)
return success;
}
int GHOST_System::openNDOF(GHOST_IWindow* w,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen)
{
return m_ndofManager->deviceOpen(w,
setNdofLibraryInit,
setNdofLibraryShutdown,
setNdofDeviceOpen);
}
GHOST_TSuccess GHOST_System::getModifierKeyState(GHOST_TModifierKeyMask mask, bool& isDown) const
{
GHOST_ModifierKeys keys;
@ -285,12 +276,6 @@ GHOST_TSuccess GHOST_System::init()
m_timerManager = new GHOST_TimerManager ();
m_windowManager = new GHOST_WindowManager ();
m_eventManager = new GHOST_EventManager ();
m_ndofManager = new GHOST_NDOFManager();
#if 0
if(m_ndofManager)
printf("ndof manager \n");
#endif
#ifdef GHOST_DEBUG
if (m_eventManager) {

@ -190,25 +190,6 @@ public:
*/
virtual GHOST_TSuccess removeEventConsumer(GHOST_IEventConsumer* consumer);
/***************************************************************************************
** N-degree of freedom devcice management functionality
***************************************************************************************/
/** Inherited from GHOST_ISystem
* Opens the N-degree of freedom device manager
* return 0 if device found, 1 otherwise
*/
virtual int openNDOF(GHOST_IWindow* w,
GHOST_NDOFLibraryInit_fp setNdofLibraryInit,
GHOST_NDOFLibraryShutdown_fp setNdofLibraryShutdown,
GHOST_NDOFDeviceOpen_fp setNdofDeviceOpen);
// original patch only
// GHOST_NDOFEventHandler_fp setNdofEventHandler);
/***************************************************************************************
** Cursor management functionality
***************************************************************************************/

@ -220,6 +220,11 @@ public:
*/
GHOST_TSuccess handleApplicationBecomeActiveEvent();
/**
* External objects should call this when they send an event outside processEvents.
*/
void notifyExternalEventProcessed();
/**
* @see GHOST_ISystem
*/
@ -267,7 +272,7 @@ protected:
/** Start time at initialization. */
GHOST_TUns64 m_start_time;
/** Event has been processed directly by Cocoa and has sent a ghost event to be dispatched */
/** Event has been processed directly by Cocoa (or NDOF manager) and has sent a ghost event to be dispatched */
bool m_outsideLoopEventProcessed;
/** Raised window is not yet known by the window manager, so delay application become active event handling */

@ -52,7 +52,7 @@
#include "GHOST_TimerTask.h"
#include "GHOST_WindowManager.h"
#include "GHOST_WindowCocoa.h"
#include "GHOST_NDOFManager.h"
#include "GHOST_NDOFManagerCocoa.h"
#include "AssertMacros.h"
#pragma mark KeyMap, mouse converters
@ -596,6 +596,9 @@ GHOST_TSuccess GHOST_SystemCocoa::init()
GHOST_TSuccess success = GHOST_System::init();
if (success) {
m_ndofManager = new GHOST_NDOFManagerCocoa(*this);
//ProcessSerialNumber psn;
//Carbon stuff to move window & menu to foreground
@ -1007,6 +1010,11 @@ GHOST_TSuccess GHOST_SystemCocoa::handleApplicationBecomeActiveEvent()
return GHOST_kSuccess;
}
void GHOST_SystemCocoa::notifyExternalEventProcessed()
{
m_outsideLoopEventProcessed = true;
}
//Note: called from NSWindow delegate
GHOST_TSuccess GHOST_SystemCocoa::handleWindowEvent(GHOST_TEventType eventType, GHOST_WindowCocoa* window)
{

@ -32,12 +32,9 @@
#include "GHOST_SystemPathsWin32.h"
#define WIN32_LEAN_AND_MEAN
#ifdef _WIN32_IE
#undef _WIN32_IE
#endif
#ifndef _WIN32_IE
#define _WIN32_IE 0x0501
#include <windows.h>
#endif
#include <shlobj.h>
#if defined(__MINGW32__) || defined(__CYGWIN__)

@ -38,6 +38,8 @@
#error WIN32 only!
#endif // WIN32
#define _WIN32_WINNT 0x501 // require Windows XP or newer
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "GHOST_SystemPaths.h"

@ -39,21 +39,18 @@
* @date May 7, 2001
*/
#ifdef BF_GHOST_DEBUG
#include <iostream>
#ifdef FREE_WINDOWS
# define WINVER 0x0501 /* GetConsoleWindow() for MinGW */
#endif
#include <stdio.h> // [mce] temporary debug, remove soon!
#include "GHOST_SystemWin32.h"
#include "GHOST_EventDragnDrop.h"
#define WIN32_LEAN_AND_MEAN
#ifdef _WIN32_IE
#undef _WIN32_IE
#ifndef _WIN32_IE
#define _WIN32_IE 0x0501 /* shipped before XP, so doesn't impose additional requirements */
#endif
#define _WIN32_IE 0x0501
#include <windows.h>
#include <shlobj.h>
#include <tlhelp32.h>
@ -65,48 +62,17 @@
#endif
#endif
/*
* According to the docs the mouse wheel message is supported from windows 98
* upwards. Leaving WINVER at default value, the WM_MOUSEWHEEL message and the
* wheel detent value are undefined.
*/
#ifndef WM_MOUSEWHEEL
#define WM_MOUSEWHEEL 0x020A
#endif // WM_MOUSEWHEEL
#ifndef WHEEL_DELTA
#define WHEEL_DELTA 120 /* Value for rolling one detent, (old convention! MS changed it) */
#endif // WHEEL_DELTA
/*
* Defines for mouse buttons 4 and 5 aka xbutton1 and xbutton2.
* MSDN: Declared in Winuser.h, include Windows.h
* This does not seem to work with MinGW so we define our own here.
*/
#ifndef XBUTTON1
#define XBUTTON1 0x0001
#endif // XBUTTON1
#ifndef XBUTTON2
#define XBUTTON2 0x0002
#endif // XBUTTON2
#ifndef WM_XBUTTONUP
#define WM_XBUTTONUP 524
#endif // WM_XBUTTONUP
#ifndef WM_XBUTTONDOWN
#define WM_XBUTTONDOWN 523
#endif // WM_XBUTTONDOWN
#include "GHOST_Debug.h"
#include "GHOST_DisplayManagerWin32.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventCursor.h"
#include "GHOST_EventKey.h"
#include "GHOST_EventWheel.h"
#include "GHOST_EventNDOF.h"
#include "GHOST_TimerTask.h"
#include "GHOST_TimerManager.h"
#include "GHOST_WindowManager.h"
#include "GHOST_WindowWin32.h"
#include "GHOST_NDOFManager.h"
#include "GHOST_NDOFManagerWin32.h"
// Key code values not found in winuser.h
#ifndef VK_MINUS
@ -159,18 +125,25 @@
#define VK_MEDIA_PLAY_PAUSE 0xB3
#endif // VK_MEDIA_PLAY_PAUSE
/*
Initiates WM_INPUT messages from keyboard
That way GHOST can retrieve true keys
*/
GHOST_TInt32 GHOST_SystemWin32::initKeyboardRawInput(void)
static void initRawInput()
{
RAWINPUTDEVICE device = {0};
device.usUsagePage = 0x01; /* usUsagePage & usUsage for keyboard*/
device.usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
RAWINPUTDEVICE devices[2];
memset(devices, 0, 2 * sizeof(RAWINPUTDEVICE));
return RegisterRawInputDevices(&device, 1, sizeof(device));
};
// multi-axis mouse (SpaceNavigator, etc.)
devices[0].usUsagePage = 0x01;
devices[0].usUsage = 0x08;
// Initiates WM_INPUT messages from keyboard
// That way GHOST can retrieve true keys
devices[1].usUsagePage = 0x01;
devices[1].usUsage = 0x06; /* http://msdn.microsoft.com/en-us/windows/hardware/gg487473.aspx */
if (RegisterRawInputDevices(devices, 2, sizeof(RAWINPUTDEVICE)))
puts("registered for RawInput (spacenav & keyboard)");
else
printf("could not register for RawInput: %d\n", (int)GetLastError());
}
GHOST_SystemWin32::GHOST_SystemWin32()
: m_hasPerformanceCounter(false), m_freq(0), m_start(0)
@ -187,6 +160,8 @@ GHOST_SystemWin32::GHOST_SystemWin32()
this->handleKeyboardChange();
// Require COM for GHOST_DropTargetWin32 created in GHOST_WindowWin32.
OleInitialize(0);
m_ndofManager = new GHOST_NDOFManagerWin32(*this);
}
GHOST_SystemWin32::~GHOST_SystemWin32()
@ -245,6 +220,7 @@ GHOST_IWindow* GHOST_SystemWin32::createWindow(
// Store the pointer to the window
// if (state != GHOST_kWindowStateFullScreen) {
m_windowManager->addWindow(window);
m_windowManager->setActiveWindow(window);
// }
}
else {
@ -385,22 +361,15 @@ GHOST_TSuccess GHOST_SystemWin32::init()
GHOST_TSuccess success = GHOST_System::init();
/* Disable scaling on high DPI displays on Vista */
HMODULE
user32 = ::LoadLibraryA("user32.dll");
typedef BOOL (WINAPI * LPFNSETPROCESSDPIAWARE)();
LPFNSETPROCESSDPIAWARE SetProcessDPIAware =
(LPFNSETPROCESSDPIAWARE)GetProcAddress(user32, "SetProcessDPIAware");
if (SetProcessDPIAware)
SetProcessDPIAware();
#ifdef NEED_RAW_PROC
pRegisterRawInputDevices = (LPFNDLLRRID)GetProcAddress(user32, "RegisterRawInputDevices");
pGetRawInputData = (LPFNDLLGRID)GetProcAddress(user32, "GetRawInputData");
#else
FreeLibrary(user32);
#endif
/* Initiates WM_INPUT messages from keyboard */
initKeyboardRawInput();
FreeLibrary(user32);
initRawInput();
// Determine whether this system has a high frequency performance counter. */
m_hasPerformanceCounter = ::QueryPerformanceFrequency((LARGE_INTEGER*)&m_freq) == TRUE;
@ -441,104 +410,84 @@ GHOST_TSuccess GHOST_SystemWin32::init()
GHOST_TSuccess GHOST_SystemWin32::exit()
{
#ifdef NEED_RAW_PROC
FreeLibrary(user32);
#endif
return GHOST_System::exit();
}
GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, int * keyDown, char * vk)
GHOST_TKey GHOST_SystemWin32::hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int * keyDown, char * vk)
{
unsigned int size = 0;
char * data;
GHOST_TKey key = GHOST_kKeyUnknown;
if(!keyDown)
return GHOST_kKeyUnknown;
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, 0, &size, sizeof(RAWINPUTHEADER));
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
if((data = (char*)malloc(size)) &&
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, data, &size, sizeof(RAWINPUTHEADER)))
{
RAWINPUT ri;
memcpy(&ri,data,(size < sizeof(ri)) ? size : sizeof(ri));
if (ri.header.dwType == RIM_TYPEKEYBOARD)
{
GHOST_SystemWin32 *system = (GHOST_SystemWin32 *)getSystem();
GHOST_ModifierKeys modifiers;
system->retrieveModifierKeys(modifiers);
*keyDown = !(ri.data.keyboard.Flags & RI_KEY_BREAK);
key = this->convertKey(window, ri.data.keyboard.VKey, ri.data.keyboard.MakeCode, (ri.data.keyboard.Flags&(RI_KEY_E1|RI_KEY_E0)));
// extra handling of modifier keys: don't send repeats out from GHOST
if(key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt)
{
bool changed = false;
GHOST_TModifierKeyMask modifier;
switch(key) {
case GHOST_kKeyLeftShift:
{
changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
modifier = GHOST_kModifierKeyLeftShift;
}
break;
case GHOST_kKeyRightShift:
{
changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
modifier = GHOST_kModifierKeyRightShift;
}
break;
case GHOST_kKeyLeftControl:
{
changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
modifier = GHOST_kModifierKeyLeftControl;
}
break;
case GHOST_kKeyRightControl:
{
changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
modifier = GHOST_kModifierKeyRightControl;
}
break;
case GHOST_kKeyLeftAlt:
{
changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
modifier = GHOST_kModifierKeyLeftAlt;
}
break;
case GHOST_kKeyRightAlt:
{
changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
modifier = GHOST_kModifierKeyRightAlt;
}
break;
default: break;
}
if(changed)
{
modifiers.set(modifier, (bool)*keyDown);
system->storeModifierKeys(modifiers);
}
else
{
key = GHOST_kKeyUnknown;
}
}
GHOST_ModifierKeys modifiers;
system->retrieveModifierKeys(modifiers);
*keyDown = !(raw.data.keyboard.Flags & RI_KEY_BREAK);
key = this->convertKey(window, raw.data.keyboard.VKey, raw.data.keyboard.MakeCode, (raw.data.keyboard.Flags&(RI_KEY_E1|RI_KEY_E0)));
// extra handling of modifier keys: don't send repeats out from GHOST
if(key >= GHOST_kKeyLeftShift && key <= GHOST_kKeyRightAlt)
{
bool changed = false;
GHOST_TModifierKeyMask modifier;
switch(key) {
case GHOST_kKeyLeftShift:
{
changed = (modifiers.get(GHOST_kModifierKeyLeftShift) != (bool)*keyDown);
modifier = GHOST_kModifierKeyLeftShift;
}
break;
case GHOST_kKeyRightShift:
{
changed = (modifiers.get(GHOST_kModifierKeyRightShift) != (bool)*keyDown);
modifier = GHOST_kModifierKeyRightShift;
}
break;
case GHOST_kKeyLeftControl:
{
changed = (modifiers.get(GHOST_kModifierKeyLeftControl) != (bool)*keyDown);
modifier = GHOST_kModifierKeyLeftControl;
}
break;
case GHOST_kKeyRightControl:
{
changed = (modifiers.get(GHOST_kModifierKeyRightControl) != (bool)*keyDown);
modifier = GHOST_kModifierKeyRightControl;
}
break;
case GHOST_kKeyLeftAlt:
{
changed = (modifiers.get(GHOST_kModifierKeyLeftAlt) != (bool)*keyDown);
modifier = GHOST_kModifierKeyLeftAlt;
}
break;
case GHOST_kKeyRightAlt:
{
changed = (modifiers.get(GHOST_kModifierKeyRightAlt) != (bool)*keyDown);
modifier = GHOST_kModifierKeyRightAlt;
}
break;
default: break;
}
if(changed)
{
modifiers.set(modifier, (bool)*keyDown);
system->storeModifierKeys(modifiers);
}
else
{
key = GHOST_kKeyUnknown;
}
}
if(vk) *vk = ri.data.keyboard.VKey;
};
};
free(data);
if(vk) *vk = raw.data.keyboard.VKey;
return key;
}
@ -742,12 +691,12 @@ GHOST_EventWheel* GHOST_SystemWin32::processWheelEvent(GHOST_IWindow *window, WP
}
GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam)
GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw)
{
int keyDown=0;
char vk;
GHOST_SystemWin32 * system = (GHOST_SystemWin32 *)getSystem();
GHOST_TKey key = system->hardKey(window, wParam, lParam, &keyDown, &vk);
GHOST_TKey key = system->hardKey(window, raw, &keyDown, &vk);
GHOST_EventKey* event;
if (key != GHOST_kKeyUnknown) {
char ascii = '\0';
@ -777,7 +726,15 @@ GHOST_EventKey* GHOST_SystemWin32::processKeyEvent(GHOST_IWindow *window, WPARAM
GHOST_Event* GHOST_SystemWin32::processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window)
{
return new GHOST_Event(getSystem()->getMilliSeconds(), type, window);
GHOST_System* system = (GHOST_System*)getSystem();
if (type == GHOST_kEventWindowActivate)
{
puts("activating window");
system->getWindowManager()->setActiveWindow(window);
}
return new GHOST_Event(system->getMilliSeconds(), type, window);
}
GHOST_TSuccess GHOST_SystemWin32::pushDragDropEvent(GHOST_TEventType eventType,
@ -800,9 +757,101 @@ void GHOST_SystemWin32::processMinMaxInfo(MINMAXINFO * minmax)
minmax->ptMinTrackSize.y=240;
}
bool GHOST_SystemWin32::processNDOF(RAWINPUT const& raw)
{
bool eventSent = false;
GHOST_TUns64 now = getMilliSeconds();
static bool firstEvent = true;
if (firstEvent)
{ // determine exactly which device is plugged in
RID_DEVICE_INFO info;
unsigned infoSize = sizeof(RID_DEVICE_INFO);
info.cbSize = infoSize;
GetRawInputDeviceInfo(raw.header.hDevice, RIDI_DEVICEINFO, &info, &infoSize);
if (info.dwType == RIM_TYPEHID)
m_ndofManager->setDevice(info.hid.dwVendorId, info.hid.dwProductId);
else
puts("<!> not a HID device... mouse/kb perhaps?");
firstEvent = false;
}
// The NDOF manager sends button changes immediately, and *pretends* to
// send motion. Mark as 'sent' so motion will always get dispatched.
eventSent = true;
#ifdef _MSC_VER
// using Microsoft compiler & header files
// they invented the RawInput API, so this version is (probably) correct
BYTE const* data = raw.data.hid.bRawData;
// struct RAWHID {
// DWORD dwSizeHid;
// DWORD dwCount;
// BYTE bRawData[1];
// };
#else
// MinGW's definition (below) doesn't agree, so we need a slight
// workaround until it's fixed
BYTE const* data = &raw.data.hid.bRawData;
// struct RAWHID {
// DWORD dwSizeHid;
// DWORD dwCount;
// BYTE bRawData; // <== isn't this s'posed to be a BYTE*?
// };
#endif
BYTE packetType = data[0];
switch (packetType)
{
case 1: // translation
{
short* axis = (short*)(data + 1);
short t[3] = {axis[0], -axis[2], axis[1]};
m_ndofManager->updateTranslation(t, now);
if (raw.data.hid.dwSizeHid == 13)
{ // this report also includes rotation
short r[3] = {-axis[3], axis[5], -axis[4]};
m_ndofManager->updateRotation(r, now);
// I've never gotten one of these, has anyone else?
puts("ndof: combined T + R");
}
break;
}
case 2: // rotation
{
short* axis = (short*)(data + 1);
short r[3] = {-axis[0], axis[2], -axis[1]};
m_ndofManager->updateRotation(r, now);
break;
}
case 3: // buttons
{
#if 0
// I'm getting garbage bits -- examine whole report:
printf("ndof: HID report for buttons [");
for (int i = 0; i < raw.data.hid.dwSizeHid; ++i)
printf(" %02X", data[i]);
printf(" ]\n");
#endif
int button_bits;
memcpy(&button_bits, data + 1, sizeof(button_bits));
m_ndofManager->updateButtons(button_bits, now);
break;
}
}
return eventSent;
}
LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
GHOST_Event* event = 0;
bool eventHandled = false;
LRESULT lResult = 0;
GHOST_SystemWin32* system = ((GHOST_SystemWin32*)getSystem());
GHOST_ASSERT(system, "GHOST_SystemWin32::s_wndProc(): system not initialized")
@ -819,18 +868,36 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
// Keyboard events, processed
////////////////////////////////////////////////////////////////////////
case WM_INPUT:
{
// check WM_INPUT from input sink when ghost window is not in the foreground
if (wParam == RIM_INPUTSINK) {
if (GetFocus() != hwnd) // WM_INPUT message not for this window
return 0;
} //else wPAram == RIM_INPUT
event = processKeyEvent(window, wParam, lParam);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
GHOST_PRINT(msg)
GHOST_PRINT(" key ignored\n")
} //else wParam == RIM_INPUT
RAWINPUT raw;
RAWINPUT* raw_ptr = &raw;
UINT rawSize = sizeof(RAWINPUT);
GetRawInputData((HRAWINPUT)lParam, RID_INPUT, raw_ptr, &rawSize, sizeof(RAWINPUTHEADER));
switch (raw.header.dwType)
{
case RIM_TYPEKEYBOARD:
event = processKeyEvent(window, raw);
if (!event) {
GHOST_PRINT("GHOST_SystemWin32::wndProc: key event ")
GHOST_PRINT(msg)
GHOST_PRINT(" key ignored\n")
}
break;
case RIM_TYPEHID:
if (system->processNDOF(raw))
eventHandled = true;
break;
}
break;
break;
}
////////////////////////////////////////////////////////////////////////
// Keyboard events, ignored
////////////////////////////////////////////////////////////////////////
@ -840,9 +907,9 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
case WM_SYSKEYUP:
/* These functions were replaced by WM_INPUT*/
case WM_CHAR:
/* The WM_CHAR message is posted to the window with the keyboard focus when
* a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
* contains the character code of the key that was pressed.
/* The WM_CHAR message is posted to the window with the keyboard focus when
* a WM_KEYDOWN message is translated by the TranslateMessage function. WM_CHAR
* contains the character code of the key that was pressed.
*/
case WM_DEADCHAR:
/* The WM_DEADCHAR message is posted to the window with the keyboard focus when a
@ -1128,28 +1195,6 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
* In GHOST, we let DefWindowProc call the timer callback.
*/
break;
case WM_BLND_NDOF_AXIS:
{
GHOST_TEventNDOFData ndofdata;
system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
system->m_eventManager->
pushEvent(new GHOST_EventNDOF(
system->getMilliSeconds(),
GHOST_kEventNDOFMotion,
window, ndofdata));
}
break;
case WM_BLND_NDOF_BTN:
{
GHOST_TEventNDOFData ndofdata;
system->m_ndofManager->GHOST_NDOFGetDatas(ndofdata);
system->m_eventManager->
pushEvent(new GHOST_EventNDOF(
system->getMilliSeconds(),
GHOST_kEventNDOFButton,
window, ndofdata));
}
break;
}
}
else {
@ -1171,10 +1216,12 @@ LRESULT WINAPI GHOST_SystemWin32::s_wndProc(HWND hwnd, UINT msg, WPARAM wParam,
if (event) {
system->pushEvent(event);
eventHandled = true;
}
else {
if (!eventHandled)
lResult = ::DefWindowProc(hwnd, msg, wParam, lParam);
}
return lResult;
}

@ -38,7 +38,10 @@
#error WIN32 only!
#endif // WIN32
#define _WIN32_WINNT 0x501 // require Windows XP or newer
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <ole2.h> // for drag-n-drop
#include "GHOST_System.h"
@ -46,95 +49,6 @@
# define __int64 long long
#endif
#ifndef WM_INPUT
#define WM_INPUT 0x00FF
#endif
#ifndef RID_INPUT
#define RID_INPUT 0x10000003
#endif
#ifndef RIM_INPUTSINK
#define RIM_INPUTSINK 0x1
#endif
#ifndef RI_KEY_BREAK
#define RI_KEY_BREAK 0x1
#endif
#ifndef RI_KEY_E0
#define RI_KEY_E0 0x2
#endif
#ifndef RI_KEY_E1
#define RI_KEY_E1 0x4
#endif
#ifndef RIM_TYPEMOUSE
#define RIM_TYPEMOUSE 0x0
#define RIM_TYPEKEYBOARD 0x1
#define RIM_TYPEHID 0x2
typedef struct tagRAWINPUTDEVICE {
USHORT usUsagePage;
USHORT usUsage;
DWORD dwFlags;
HWND hwndTarget;
} RAWINPUTDEVICE;
typedef struct tagRAWINPUTHEADER {
DWORD dwType;
DWORD dwSize;
HANDLE hDevice;
WPARAM wParam;
} RAWINPUTHEADER;
typedef struct tagRAWMOUSE {
USHORT usFlags;
union {
ULONG ulButtons;
struct {
USHORT usButtonFlags;
USHORT usButtonData;
};
};
ULONG ulRawButtons;
LONG lLastX;
LONG lLastY;
ULONG ulExtraInformation;
} RAWMOUSE;
typedef struct tagRAWKEYBOARD {
USHORT MakeCode;
USHORT Flags;
USHORT Reserved;
USHORT VKey;
UINT Message;
ULONG ExtraInformation;
} RAWKEYBOARD;
typedef struct tagRAWHID {
DWORD dwSizeHid;
DWORD dwCount;
BYTE bRawData[1];
} RAWHID;
typedef struct tagRAWINPUT {
RAWINPUTHEADER header;
union {
RAWMOUSE mouse;
RAWKEYBOARD keyboard;
RAWHID hid;
} data;
} RAWINPUT;
DECLARE_HANDLE(HRAWINPUT);
#endif
#ifdef FREE_WINDOWS
#define NEED_RAW_PROC
typedef BOOL (WINAPI * LPFNDLLRRID)(RAWINPUTDEVICE*,UINT, UINT);
typedef UINT (WINAPI * LPFNDLLGRID)(HRAWINPUT, UINT, LPVOID, PUINT, UINT);
#define GetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader) ((pGetRawInputData)?pGetRawInputData(hRawInput, uiCommand, pData, pcbSize, cbSizeHeader):(UINT)-1)
#endif
class GHOST_EventButton;
class GHOST_EventCursor;
class GHOST_EventKey;
@ -314,14 +228,13 @@ protected:
/**
* Catches raw WIN32 key codes from WM_INPUT in the wndproc.
* @param window-> The window for this handling
* @param wParam The wParam from the wndproc
* @param lParam The lParam from the wndproc
* @param window The window for this handling
* @param raw RawInput structure with detailed info about the key event
* @param keyDown Pointer flag that specify if a key is down
* @param vk Pointer to virtual key
* @return The GHOST key (GHOST_kKeyUnknown if no match).
*/
virtual GHOST_TKey hardKey(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam, int * keyDown, char * vk);
virtual GHOST_TKey hardKey(GHOST_IWindow *window, RAWINPUT const& raw, int * keyDown, char * vk);
/**
* Creates modifier key event(s) and updates the key data stored locally (m_modifierKeys).
@ -362,10 +275,9 @@ protected:
* In most cases this is a straightforward conversion of key codes.
* For the modifier keys however, we want to distinguish left and right keys.
* @param window The window receiving the event (the active window).
* @param wParam The wParam from the wndproc
* @param lParam The lParam from the wndproc
* @param raw RawInput structure with detailed info about the key event
*/
static GHOST_EventKey* processKeyEvent(GHOST_IWindow *window, WPARAM wParam, LPARAM lParam);
static GHOST_EventKey* processKeyEvent(GHOST_IWindow *window, RAWINPUT const& raw);
/**
* Process special keys (VK_OEM_*), to see if current key layout
@ -383,12 +295,22 @@ protected:
* @return The event created.
*/
static GHOST_Event* processWindowEvent(GHOST_TEventType type, GHOST_IWindow* window);
/**
/**
* Handles minimum window size.
* @param minmax The MINMAXINFO structure.
*/
static void processMinMaxInfo(MINMAXINFO * minmax);
/**
* Handles Motion and Button events from a SpaceNavigator or related device.
* Instead of returning an event object, this function communicates directly
* with the GHOST_NDOFManager.
* @param raw RawInput structure with detailed info about the NDOF event
* @return Whether an event was generated and sent.
*/
bool processNDOF(RAWINPUT const& raw);
/**
* Returns the local state of the modifier keys (from the message queue).
* @param keys The state of the keys.
@ -412,11 +334,6 @@ protected:
*/
static LRESULT WINAPI s_wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
/**
* Initiates WM_INPUT messages from keyboard
*/
GHOST_TInt32 initKeyboardRawInput(void);
/**
* Toggles console
* @action 0 - Hides
@ -445,15 +362,6 @@ protected:
/** Console status */
int m_consoleStatus;
/** handle for user32.dll*/
HMODULE user32;
#ifdef NEED_RAW_PROC
/* pointer to RegisterRawInputDevices function */
LPFNDLLRRID pRegisterRawInputDevices;
/* pointer to GetRawInputData function */
LPFNDLLGRID pGetRawInputData;
#endif
};
inline void GHOST_SystemWin32::retrieveModifierKeys(GHOST_ModifierKeys& keys) const
@ -487,4 +395,3 @@ inline void GHOST_SystemWin32::handleKeyboardChange(void)
}
}
#endif // _GHOST_SYSTEM_WIN32_H_

@ -42,8 +42,7 @@
#include "GHOST_EventKey.h"
#include "GHOST_EventButton.h"
#include "GHOST_EventWheel.h"
#include "GHOST_EventNDOF.h"
#include "GHOST_NDOFManager.h"
#include "GHOST_NDOFManagerX11.h"
#include "GHOST_DisplayManagerX11.h"
#include "GHOST_Debug.h"
@ -79,19 +78,6 @@
static GHOST_TKey
convertXKey(KeySym key);
typedef struct NDOFPlatformInfo {
Display *display;
Window window;
volatile GHOST_TEventNDOFData *currValues;
Atom cmdAtom;
Atom motionAtom;
Atom btnPressAtom;
Atom btnRelAtom;
} NDOFPlatformInfo;
static NDOFPlatformInfo sNdofInfo = {NULL, 0, NULL, 0, 0, 0, 0};
//these are for copy and select copy
static char *txt_cut_buffer= NULL;
static char *txt_select_buffer= NULL;
@ -181,6 +167,7 @@ init(
GHOST_TSuccess success = GHOST_System::init();
if (success) {
m_ndofManager = new GHOST_NDOFManagerX11(*this);
m_displayManager = new GHOST_DisplayManagerX11(this);
if (m_displayManager) {
@ -275,7 +262,7 @@ createWindow(
if (window->getValid()) {
// Store the pointer to the window
m_windowManager->addWindow(window);
m_windowManager->setActiveWindow(window);
pushEvent( new GHOST_Event(getMilliSeconds(), GHOST_kEventWindowSize, window) );
}
else {
@ -386,8 +373,6 @@ lastEventTime(Time default_time) {
return data.timestamp;
}
bool
GHOST_SystemX11::
processEvents(
@ -428,6 +413,11 @@ processEvents(
if (generateWindowExposeEvents()) {
anyProcessed = true;
}
if (dynamic_cast<GHOST_NDOFManagerX11*>(m_ndofManager)->processEvents()) {
anyProcessed = true;
}
} while (waitForEvent && !anyProcessed);
return anyProcessed;
@ -611,6 +601,9 @@ GHOST_SystemX11::processEvent(XEvent *xe)
case FocusOut:
{
XFocusChangeEvent &xfe = xe->xfocus;
// TODO: make sure this is the correct place for activate/deactivate
// printf("X: focus %s for window %d\n", xfe.type == FocusIn ? "in" : "out", (int) xfe.window);
// May have to look at the type of event and filter some
// out.
@ -641,32 +634,8 @@ GHOST_SystemX11::processEvent(XEvent *xe)
);
} else
#endif
if (sNdofInfo.currValues) {
static GHOST_TEventNDOFData data = {0,0,0,0,0,0,0,0,0,0,0};
if (xcme.message_type == sNdofInfo.motionAtom)
{
data.changed = 1;
data.delta = xcme.data.s[8] - data.time;
data.time = xcme.data.s[8];
data.tx = xcme.data.s[2] >> 2;
data.ty = xcme.data.s[3] >> 2;
data.tz = xcme.data.s[4] >> 2;
data.rx = xcme.data.s[5];
data.ry = xcme.data.s[6];
data.rz =-xcme.data.s[7];
g_event = new GHOST_EventNDOF(getMilliSeconds(),
GHOST_kEventNDOFMotion,
window, data);
} else if (xcme.message_type == sNdofInfo.btnPressAtom) {
data.changed = 2;
data.delta = xcme.data.s[8] - data.time;
data.time = xcme.data.s[8];
data.buttons = xcme.data.s[2];
g_event = new GHOST_EventNDOF(getMilliSeconds(),
GHOST_kEventNDOFButton,
window, data);
}
} else if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
if (((Atom)xcme.data.l[0]) == m_wm_take_focus) {
XWindowAttributes attr;
Window fwin;
int revert_to;
@ -723,6 +692,14 @@ GHOST_SystemX11::processEvent(XEvent *xe)
xce.y_root
);
}
// printf("X: %s window %d\n", xce.type == EnterNotify ? "entering" : "leaving", (int) xce.window);
if (xce.type == EnterNotify)
m_windowManager->setActiveWindow(window);
else
m_windowManager->setWindowInactive(window);
break;
}
case MapNotify:
@ -834,6 +811,8 @@ GHOST_SystemX11::processEvent(XEvent *xe)
}
}
#if 0 // obsolete SpaceNav code
void *
GHOST_SystemX11::
prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
@ -846,6 +825,8 @@ prepareNdofInfo(volatile GHOST_TEventNDOFData *currentNdofValues)
return (void*)&sNdofInfo;
}
#endif
GHOST_TSuccess
GHOST_SystemX11::
getModifierKeys(

@ -203,11 +203,6 @@ public:
return m_display;
}
void *
prepareNdofInfo(
volatile GHOST_TEventNDOFData *current_values
);
/* Helped function for get data from the clipboard. */
void getClipboard_xcout(XEvent evt, Atom sel, Atom target,
unsigned char **txt, unsigned long *len,

@ -3,20 +3,16 @@
*/
#ifndef GHOST_TASKBARWIN32_H_
#define GHOST_TASKBARWIN32_H_
#ifndef WIN32
#error WIN32 only!
#endif // WIN32
#define _WIN32_WINNT 0x501 // require Windows XP or newer
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <shlobj.h>
/* MinGW needs it */
#ifdef FREE_WINDOWS
#ifdef WINVER
#undef WINVER
#endif
#define WINVER 0x0501
#endif /* FREE_WINDOWS */
// ITaskbarList, ITaskbarList2 and ITaskbarList3 might be missing, present here in that case.
// Note, ITaskbarList3 is supported only since Windows 7, though. Check for that is done in

@ -39,20 +39,12 @@
#endif // WIN32
#include "GHOST_Window.h"
/* MinGW needs it */
#ifdef FREE_WINDOWS
#ifdef WINVER
#undef WINVER
#endif
#define WINVER 0x0501
#endif
#include <windows.h>
#include "GHOST_TaskbarWin32.h"
#define _WIN32_WINNT 0x501 // require Windows XP or newer
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <wintab.h>
#define PACKETDATA (PK_BUTTONS | PK_NORMAL_PRESSURE | PK_ORIENTATION | PK_CURSOR)

@ -817,12 +817,9 @@ class USERPREF_PT_input(bpy.types.Panel, InputKeyMapPanel):
#sub.prop(view, "wheel_scroll_lines", text="Scroll Lines")
col.separator()
''' not implemented yet
sub = col.column()
sub.label(text="NDOF Device:")
sub.prop(inputs, "ndof_pan_speed", text="Pan Speed")
sub.prop(inputs, "ndof_rotate_speed", text="Orbit Speed")
'''
sub.prop(inputs, "ndof_sensitivity", text="NDOF Sensitivity")
row.separator()

@ -271,6 +271,8 @@ class InputKeyMapPanel:
row.prop(kmi, "type", text="", full_event=True)
elif map_type == 'MOUSE':
row.prop(kmi, "type", text="", full_event=True)
if map_type == 'NDOF':
row.prop(kmi, "type", text="", full_event=True)
elif map_type == 'TWEAK':
subrow = row.row()
subrow.prop(kmi, "type", text="")
@ -306,7 +308,7 @@ class InputKeyMapPanel:
sub = split.column()
subrow = sub.row(align=True)
if map_type == 'KEYBOARD':
if map_type in ('KEYBOARD', 'NDOF'):
subrow.prop(kmi, "type", text="", event=True)
subrow.prop(kmi, "value", text="")
elif map_type == 'MOUSE':

@ -349,6 +349,30 @@ class VIEW3D_MT_view_navigation(bpy.types.Menu):
layout.operator("view3d.fly")
class VIEW3D_MT_ndof_settings(bpy.types.Menu):
bl_label = "3D Mouse Settings"
def draw(self, context):
layout = self.layout
input_prefs = context.user_preferences.inputs
layout.separator()
layout.prop(input_prefs, "ndof_sensitivity")
if context.space_data.type == 'VIEW_3D':
layout.separator()
layout.prop(input_prefs, "ndof_show_guide")
layout.separator()
layout.label(text="orbit options")
layout.prop(input_prefs, "ndof_orbit_invert_axes")
layout.separator()
layout.label(text="fly options")
layout.prop(input_prefs, "ndof_fly_helicopter", icon='NDOF_FLY')
layout.prop(input_prefs, "ndof_lock_horizon", icon='NDOF_DOM')
class VIEW3D_MT_view_align(bpy.types.Menu):
bl_label = "Align View"

@ -92,9 +92,6 @@ typedef struct Global {
/* save the allowed windowstate of blender when using -W or -w */
int windowstate;
/* ndof device found ? */
int ndofdevice;
} Global;
/* **************** GLOBAL ********************* */
@ -174,5 +171,3 @@ extern Global G;
#endif
#endif

@ -1617,6 +1617,18 @@ static int gpencil_draw_modal (bContext *C, wmOperator *op, wmEvent *event)
tGPsdata *p= op->customdata;
int estate = OPERATOR_PASS_THROUGH; /* default exit state - not handled, so let others have a share of the pie */
// if (event->type == NDOF_MOTION)
// return OPERATOR_PASS_THROUGH;
// -------------------------------
// [mce] Not quite what I was looking
// for, but a good start! GP continues to
// draw on the screen while the 3D mouse
// moves the viewpoint. Problem is that
// the stroke is converted to 3D only after
// it is finished. This approach should work
// better in tools that immediately apply
// in 3D space.
//printf("\tGP - handle modal event...\n");
/* exit painting mode (and/or end current stroke) */

@ -1585,6 +1585,12 @@ void init_userdef_do_versions(void)
if (U.anisotropic_filter <= 0)
U.anisotropic_filter = 1;
if (U.ndof_sensitivity == 0.0f) {
U.ndof_sensitivity = 1.0f;
U.ndof_flag = NDOF_SHOW_GUIDE | NDOF_LOCK_HORIZON |
NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM | NDOF_SHOULD_ROTATE;
}
/* funny name, but it is GE stuff, moves userdef stuff to engine */
// XXX space_set_commmandline_options();
/* this timer uses U */

@ -832,6 +832,13 @@ int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
float mouse[2];
int first= 0;
// let NDOF motion pass through to the 3D view so we can paint and rotate simultaneously!
// this isn't perfect... even when an extra MOUSEMOVE is spoofed, the stroke discards it
// since the 2D deltas are zero -- code in this file needs to be updated to use the
// post-NDOF_MOTION MOUSEMOVE
if (event->type == NDOF_MOTION)
return OPERATOR_PASS_THROUGH;
if(!stroke->stroke_started) {
stroke->last_mouse_position[0] = event->x;
stroke->last_mouse_position[1] = event->y;

@ -73,6 +73,7 @@ void IMAGE_OT_view_zoom(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot);
void IMAGE_OT_view_zoom_ratio(struct wmOperatorType *ot);
void IMAGE_OT_view_ndof(struct wmOperatorType *ot);
void IMAGE_OT_new(struct wmOperatorType *ot);
void IMAGE_OT_open(struct wmOperatorType *ot);

@ -437,6 +437,60 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot)
"Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out.", -FLT_MAX, FLT_MAX);
}
/********************** NDOF operator *********************/
/* Combined pan/zoom from a 3D mouse device.
* Z zooms, XY pans
* "view" (not "paper") control -- user moves the viewpoint, not the image being viewed
* that explains the negative signs in the code below
*/
static int view_ndof_invoke(bContext *C, wmOperator *op, wmEvent *event)
{
SpaceImage *sima= CTX_wm_space_image(C);
ARegion *ar= CTX_wm_region(C);
wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
float dt = ndof->dt > 0.25f ? 0.0125f : ndof->dt;
/* 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
*/
/* tune these until it feels right */
const float zoom_sensitivity = 0.5f;
const float pan_sensitivity = 300.f;
float pan_x = pan_sensitivity * dt * ndof->tx / sima->zoom;
float pan_y = pan_sensitivity * dt * ndof->ty / sima->zoom;
/* "mouse zoom" factor = 1 + (dx + dy) / 300
* what about "ndof zoom" factor? should behave like this:
* at rest -> factor = 1
* move forward -> factor > 1
* move backward -> factor < 1
*/
float zoom_factor = 1.f + zoom_sensitivity * dt * -ndof->tz;
sima_zoom_set_factor(sima, ar, zoom_factor);
sima->xof += pan_x;
sima->yof += pan_y;
ED_region_tag_redraw(ar);
return OPERATOR_FINISHED;
}
void IMAGE_OT_view_ndof(wmOperatorType *ot)
{
/* identifiers */
ot->name= "NDOF Pan/Zoom";
ot->idname= "IMAGE_OT_view_ndof";
/* api callbacks */
ot->invoke= view_ndof_invoke;
}
/********************** view all operator *********************/
/* Updates the fields of the View2D member of the SpaceImage struct.

@ -469,6 +469,7 @@ static void image_operatortypes(void)
WM_operatortype_append(IMAGE_OT_view_zoom_in);
WM_operatortype_append(IMAGE_OT_view_zoom_out);
WM_operatortype_append(IMAGE_OT_view_zoom_ratio);
WM_operatortype_append(IMAGE_OT_view_ndof);
WM_operatortype_append(IMAGE_OT_new);
WM_operatortype_append(IMAGE_OT_open);
@ -518,6 +519,9 @@ static void image_keymap(struct wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MIDDLEMOUSE, KM_PRESS, KM_SHIFT, 0);
WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MOUSEPAN, 0, 0, 0);
WM_keymap_add_item(keymap, "IMAGE_OT_view_all", NDOF_BUTTON_FIT, KM_PRESS, 0, 0); // or view selected?
WM_keymap_add_item(keymap, "IMAGE_OT_view_ndof", NDOF_MOTION, 0, 0, 0);
WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0);

@ -675,6 +675,104 @@ static void draw_view_axis(RegionView3D *rv3d)
glDisable(GL_BLEND);
}
/* draw center and axis of rotation for ongoing 3D mouse navigation */
static void draw_rotation_guide(RegionView3D *rv3d)
{
float o[3]; // center of rotation
float end[3]; // endpoints for drawing
float color[4] = {0.f ,0.4235f, 1.f, 1.f}; // bright blue so it matches device LEDs
negate_v3_v3(o, rv3d->ofs);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_SMOOTH);
glPointSize(5);
glEnable(GL_POINT_SMOOTH);
glDepthMask(0); // don't overwrite zbuf
if (rv3d->rot_angle != 0.f) {
// -- draw rotation axis --
float scaled_axis[3];
const float scale = rv3d->dist;
mul_v3_v3fl(scaled_axis, rv3d->rot_axis, scale);
glBegin(GL_LINE_STRIP);
color[3] = 0.f; // more transparent toward the ends
glColor4fv(color);
add_v3_v3v3(end, o, scaled_axis);
glVertex3fv(end);
// color[3] = 0.2f + fabsf(rv3d->rot_angle); // modulate opacity with angle
// ^^ neat idea, but angle is frame-rate dependent, so it's usually close to 0.2
color[3] = 0.5f; // more opaque toward the center
glColor4fv(color);
glVertex3fv(o);
color[3] = 0.f;
glColor4fv(color);
sub_v3_v3v3(end, o, scaled_axis);
glVertex3fv(end);
glEnd();
// -- draw ring around rotation center --
{
#define ROT_AXIS_DETAIL 13
const float s = 0.05f * scale;
const float step = 2.f * M_PI / ROT_AXIS_DETAIL;
float angle;
int i;
float q[4]; // rotate ring so it's perpendicular to axis
const int upright = fabsf(rv3d->rot_axis[2]) >= 0.95f;
if (!upright)
{
const float up[3] = {0.f, 0.f, 1.f};
float vis_angle, vis_axis[3];
cross_v3_v3v3(vis_axis, up, rv3d->rot_axis);
vis_angle = acosf(dot_v3v3(up, rv3d->rot_axis));
axis_angle_to_quat(q, vis_axis, vis_angle);
}
color[3] = 0.25f; // somewhat faint
glColor4fv(color);
glBegin(GL_LINE_LOOP);
for (i = 0, angle = 0.f; i < ROT_AXIS_DETAIL; ++i, angle += step)
{
float p[3] = { s * cosf(angle), s * sinf(angle), 0.f };
if (!upright)
mul_qt_v3(q, p);
add_v3_v3(p, o);
glVertex3fv(p);
}
glEnd();
}
color[3] = 1.f; // solid dot
}
else
color[3] = 0.5f; // see-through dot
// -- draw rotation center --
glColor4fv(color);
glBegin(GL_POINTS);
glVertex3fv(o);
glEnd();
// find screen coordinates for rotation center, then draw pretty icon
// mul_m4_v3(rv3d->persinv, rot_center);
// UI_icon_draw(rot_center[0], rot_center[1], ICON_NDOF_TURN);
// ^^ just playing around, does not work
glDisable(GL_BLEND);
glDisable(GL_POINT_SMOOTH);
glDepthMask(1);
}
static void draw_view_icon(RegionView3D *rv3d)
{
@ -2618,6 +2716,10 @@ void view3d_main_area_draw(const bContext *C, ARegion *ar)
BDR_drawSketch(C);
}
if ((U.ndof_flag & NDOF_SHOW_GUIDE) && (rv3d->viewlock != RV3D_LOCKED) && (rv3d->persp != RV3D_CAMOB))
// TODO: draw something else (but not this) during fly mode
draw_rotation_guide(rv3d);
ED_region_pixelspace(ar);
// retopo_paint_view_update(v3d);

@ -928,6 +928,269 @@ void VIEW3D_OT_rotate(wmOperatorType *ot)
ot->flag= OPTYPE_BLOCKING|OPTYPE_GRAB_POINTER;
}
// 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;
}
void ndof_to_quat(struct wmNDOFMotionData* ndof, float q[4])
{
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;
// combined scaling factor -- normalize axis while converting to quaternion
float scale = sin(0.5f * angle) / angular_velocity;
// convert axis-angle to quaternion
q[0] = cos(0.5f * angle);
q[1] = scale * x;
q[2] = scale * y;
q[3] = scale * z;
}
static int ndof_orbit_invoke(bContext *C, wmOperator *op, wmEvent *event)
// -- "orbit" navigation (trackball/turntable)
// -- zooming
// -- panning in rotationally-locked views
{
RegionView3D* rv3d = CTX_wm_region_view3d(C);
wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
rv3d->rot_angle = 0.f; // off by default, until changed later this function
if (ndof->progress != P_FINISHING) {
const float dt = ndof->dt;
// tune these until everything feels right
const float rot_sensitivity = 1.f;
const float zoom_sensitivity = 1.f;
const float pan_sensitivity = 1.f;
// rather have bool, but...
int has_rotation = rv3d->viewlock != RV3D_LOCKED && (ndof->rx || ndof->ry || ndof->rz);
float view_inv[4];
invert_qt_qt(view_inv, rv3d->viewquat);
//#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",
ndof->tx, ndof->ty, ndof->tz, ndof->rx, ndof->ry, ndof->rz, ndof->dt);
#endif
if (ndof->tz) {
// Zoom!
// velocity should be proportional to the linear velocity attained by rotational motion of same strength
// [got that?]
// proportional to arclength = radius * angle
float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
rv3d->dist += zoom_distance;
}
if (rv3d->viewlock == RV3D_LOCKED) {
/* rotation not allowed -- explore panning options instead */
float pan_vec[3] = {ndof->tx, ndof->ty, 0};
mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
/* transform motion from view to world coordinates */
invert_qt_qt(view_inv, rv3d->viewquat);
mul_qt_v3(view_inv, pan_vec);
/* move center of view opposite of hand motion (this is camera mode, not object mode) */
sub_v3_v3(rv3d->ofs, pan_vec);
}
if (has_rotation) {
const int invert = U.ndof_flag & NDOF_ORBIT_INVERT_AXES;
rv3d->view = RV3D_VIEW_USER;
if (U.flag & USER_TRACKBALL) {
float rot[4];
#if 0 // -------------------------- Mike's nifty original version
float view_inv_conj[4];
ndof_to_quat(ndof, rot);
// mul_qt_fl(rot, rot_sensitivity);
// ^^ no apparent effect
if (invert)
invert_qt(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);
#else // ---------------------------------------- Mike's revised version
float axis[3];
float angle = rot_sensitivity * ndof_to_angle_axis(ndof, axis);
if (invert)
angle = -angle;
// transform rotation axis from view to world coordinates
mul_qt_v3(view_inv, axis);
// update the onscreen doo-dad
rv3d->rot_angle = angle;
copy_v3_v3(rv3d->rot_axis, axis);
axis_angle_to_quat(rot, axis, angle);
#endif // --------------------------------------------
// apply rotation
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
} else {
/* turntable view code by John Aughey, adapted for 3D mouse by [mce] */
float angle, rot[4];
float xvec[3] = {1,0,0};
/* Determine the direction of the x vector (for rotating up and down) */
mul_qt_v3(view_inv, xvec);
/* Perform the up/down rotation */
angle = rot_sensitivity * dt * ndof->rx;
if (invert)
angle = -angle;
rot[0] = cos(angle);
mul_v3_v3fl(rot+1, xvec, sin(angle));
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
/* Perform the orbital rotation */
angle = rot_sensitivity * dt * ndof->ry;
if (invert)
angle = -angle;
// update the onscreen doo-dad
rv3d->rot_angle = angle;
rv3d->rot_axis[0] = 0;
rv3d->rot_axis[1] = 0;
rv3d->rot_axis[2] = 1;
rot[0] = cos(angle);
rot[1] = rot[2] = 0.0;
rot[3] = sin(angle);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rot);
}
}
}
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "NDOF Orbit View";
ot->description = "Explore every angle of an object using the 3D mouse.";
ot->idname = "VIEW3D_OT_ndof_orbit";
/* api callbacks */
ot->invoke = ndof_orbit_invoke;
ot->poll = ED_operator_view3d_active;
/* flags */
ot->flag = 0;
}
static int ndof_pan_invoke(bContext *C, wmOperator *op, wmEvent *event)
// -- "pan" navigation
// -- zoom or dolly?
{
RegionView3D* rv3d = CTX_wm_region_view3d(C);
wmNDOFMotionData* ndof = (wmNDOFMotionData*) event->customdata;
rv3d->rot_angle = 0.f; // we're panning here! so erase any leftover rotation from other operators
if (ndof->progress != P_FINISHING) {
const float dt = ndof->dt;
float view_inv[4];
#if 0 // ------------------------------------------- zoom with Z
// tune these until everything feels right
const float zoom_sensitivity = 1.f;
const float pan_sensitivity = 1.f;
float pan_vec[3] = {
ndof->tx, ndof->ty, 0
};
// "zoom in" or "translate"? depends on zoom mode in user settings?
if (ndof->tz) {
float zoom_distance = zoom_sensitivity * rv3d->dist * dt * ndof->tz;
rv3d->dist += zoom_distance;
}
mul_v3_fl(pan_vec, pan_sensitivity * rv3d->dist * dt);
#else // ------------------------------------------------------- dolly with Z
float speed = 10.f; // blender units per second
// ^^ this is ok for default cube scene, but should scale with.. something
// tune these until everything feels right
const float forward_sensitivity = 1.f;
const float vertical_sensitivity = 0.4f;
const float lateral_sensitivity = 0.6f;
float pan_vec[3] = {
lateral_sensitivity * ndof->tx,
vertical_sensitivity * ndof->ty,
forward_sensitivity * ndof->tz
};
mul_v3_fl(pan_vec, speed * dt);
#endif
/* transform motion from view to world coordinates */
invert_qt_qt(view_inv, rv3d->viewquat);
mul_qt_v3(view_inv, pan_vec);
/* move center of view opposite of hand motion (this is camera mode, not object mode) */
sub_v3_v3(rv3d->ofs, pan_vec);
}
ED_region_tag_redraw(CTX_wm_region(C));
return OPERATOR_FINISHED;
}
void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot)
{
/* identifiers */
ot->name = "NDOF Pan View";
ot->description = "Position your viewpoint with the 3D mouse.";
ot->idname = "VIEW3D_OT_ndof_pan";
/* api callbacks */
ot->invoke = ndof_pan_invoke;
ot->poll = ED_operator_view3d_active;
/* flags */
ot->flag = 0;
}
/* ************************ viewmove ******************************** */
@ -3195,398 +3458,6 @@ int ED_view3d_autodist_depth_seg(struct ARegion *ar, const int mval_sta[2], cons
return (*depth==FLT_MAX) ? 0:1;
}
/* ********************* NDOF ************************ */
/* note: this code is confusing and unclear... (ton) */
/* **************************************************** */
// ndof scaling will be moved to user setting.
// In the mean time this is just a place holder.
// Note: scaling in the plugin and ghostwinlay.c
// should be removed. With driver default setting,
// each axis returns approx. +-200 max deflection.
// The values I selected are based on the older
// polling i/f. With event i/f, the sensistivity
// can be increased for improved response from
// small deflections of the device input.
// lukep notes : i disagree on the range.
// the normal 3Dconnection driver give +/-400
// on defaut range in other applications
// and up to +/- 1000 if set to maximum
// because i remove the scaling by delta,
// which was a bad idea as it depend of the system
// speed and os, i changed the scaling values, but
// those are still not ok
#if 0
static float ndof_axis_scale[6] = {
+0.01, // Tx
+0.01, // Tz
+0.01, // Ty
+0.0015, // Rx
+0.0015, // Rz
+0.0015 // Ry
};
static void filterNDOFvalues(float *sbval)
{
int i=0;
float max = 0.0;
for (i =0; i<6;i++)
if (fabs(sbval[i]) > max)
max = fabs(sbval[i]);
for (i =0; i<6;i++)
if (fabs(sbval[i]) != max )
sbval[i]=0.0;
}
// statics for controlling rv3d->dist corrections.
// viewmoveNDOF zeros and adjusts rv3d->ofs.
// viewmove restores based on dz_flag state.
int dz_flag = 0;
float m_dist;
void viewmoveNDOFfly(ARegion *ar, View3D *v3d, int UNUSED(mode))
{
RegionView3D *rv3d= ar->regiondata;
int i;
float phi;
float dval[7];
// static fval[6] for low pass filter; device input vector is dval[6]
static float fval[6];
float tvec[3],rvec[3];
float q1[4];
float mat[3][3];
float upvec[3];
/*----------------------------------------------------
* sometimes this routine is called from headerbuttons
* viewmove needs to refresh the screen
*/
// XXX areawinset(ar->win);
// fetch the current state of the ndof device
// XXX getndof(dval);
if (v3d->ndoffilter)
filterNDOFvalues(fval);
// Scale input values
// if(dval[6] == 0) return; // guard against divide by zero
for(i=0;i<6;i++) {
// user scaling
dval[i] = dval[i] * ndof_axis_scale[i];
}
// low pass filter with zero crossing reset
for(i=0;i<6;i++) {
if((dval[i] * fval[i]) >= 0)
dval[i] = (fval[i] * 15 + dval[i]) / 16;
else
fval[i] = 0;
}
// force perspective mode. This is a hack and is
// incomplete. It doesn't actually effect the view
// until the first draw and doesn't update the menu
// to reflect persp mode.
rv3d->persp = RV3D_PERSP;
// Correct the distance jump if rv3d->dist != 0
// This is due to a side effect of the original
// mouse view rotation code. The rotation point is
// set a distance in front of the viewport to
// make rotating with the mouse look better.
// The distance effect is written at a low level
// in the view management instead of the mouse
// view function. This means that all other view
// movement devices must subtract this from their
// view transformations.
if(rv3d->dist != 0.0) {
dz_flag = 1;
m_dist = rv3d->dist;
upvec[0] = upvec[1] = 0;
upvec[2] = rv3d->dist;
copy_m3_m4(mat, rv3d->viewinv);
mul_m3_v3(mat, upvec);
sub_v3_v3(rv3d->ofs, upvec);
rv3d->dist = 0.0;
}
// Apply rotation
// Rotations feel relatively faster than translations only in fly mode, so
// we have no choice but to fix that here (not in the plugins)
rvec[0] = -0.5 * dval[3];
rvec[1] = -0.5 * dval[4];
rvec[2] = -0.5 * dval[5];
// rotate device x and y by view z
copy_m3_m4(mat, rv3d->viewinv);
mat[2][2] = 0.0f;
mul_m3_v3(mat, rvec);
// rotate the view
phi = normalize_v3(rvec);
if(phi != 0) {
axis_angle_to_quat(q1,rvec,phi);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
}
// Apply translation
tvec[0] = dval[0];
tvec[1] = dval[1];
tvec[2] = -dval[2];
// the next three lines rotate the x and y translation coordinates
// by the current z axis angle
copy_m3_m4(mat, rv3d->viewinv);
mat[2][2] = 0.0f;
mul_m3_v3(mat, tvec);
// translate the view
sub_v3_v3(rv3d->ofs, tvec);
/*----------------------------------------------------
* refresh the screen XXX
*/
// update render preview window
// XXX BIF_view3d_previewrender_signal(ar, PR_DBASE|PR_DISPRECT);
}
void viewmoveNDOF(Scene *scene, ARegion *ar, View3D *v3d, int UNUSED(mode))
{
RegionView3D *rv3d= ar->regiondata;
float fval[7];
float dvec[3];
float sbadjust = 1.0f;
float len;
short use_sel = 0;
Object *ob = OBACT;
float m[3][3];
float m_inv[3][3];
float xvec[3] = {1,0,0};
float yvec[3] = {0,-1,0};
float zvec[3] = {0,0,1};
float phi;
float q1[4];
float obofs[3];
float reverse;
//float diff[4];
float d, curareaX, curareaY;
float mat[3][3];
float upvec[3];
/* Sensitivity will control how fast the view rotates. The value was
* obtained experimentally by tweaking until the author didn't get dizzy watching.
* Perhaps this should be a configurable user parameter.
*/
float psens = 0.005f * (float) U.ndof_pan; /* pan sensitivity */
float rsens = 0.005f * (float) U.ndof_rotate; /* rotate sensitivity */
float zsens = 0.3f; /* zoom sensitivity */
const float minZoom = -30.0f;
const float maxZoom = 300.0f;
//reset view type
rv3d->view = 0;
//printf("passing here \n");
//
if (scene->obedit==NULL && ob && !(ob->mode & OB_MODE_POSE)) {
use_sel = 1;
}
if((dz_flag)||rv3d->dist==0) {
dz_flag = 0;
rv3d->dist = m_dist;
upvec[0] = upvec[1] = 0;
upvec[2] = rv3d->dist;
copy_m3_m4(mat, rv3d->viewinv);
mul_m3_v3(mat, upvec);
add_v3_v3(rv3d->ofs, upvec);
}
/*----------------------------------------------------
* sometimes this routine is called from headerbuttons
* viewmove needs to refresh the screen
*/
// XXX areawinset(curarea->win);
/*----------------------------------------------------
* record how much time has passed. clamp at 10 Hz
* pretend the previous frame occurred at the clamped time
*/
// now = PIL_check_seconds_timer();
// frametime = (now - prevTime);
// if (frametime > 0.1f){ /* if more than 1/10s */
// frametime = 1.0f/60.0; /* clamp at 1/60s so no jumps when starting to move */
// }
// prevTime = now;
// sbadjust *= 60 * frametime; /* normalize ndof device adjustments to 100Hz for framerate independence */
/* fetch the current state of the ndof device & enforce dominant mode if selected */
// XXX getndof(fval);
if (v3d->ndoffilter)
filterNDOFvalues(fval);
// put scaling back here, was previously in ghostwinlay
fval[0] = fval[0] * (1.0f/600.0f);
fval[1] = fval[1] * (1.0f/600.0f);
fval[2] = fval[2] * (1.0f/1100.0f);
fval[3] = fval[3] * 0.00005f;
fval[4] =-fval[4] * 0.00005f;
fval[5] = fval[5] * 0.00005f;
fval[6] = fval[6] / 1000000.0f;
// scale more if not in perspective mode
if (rv3d->persp == RV3D_ORTHO) {
fval[0] = fval[0] * 0.05f;
fval[1] = fval[1] * 0.05f;
fval[2] = fval[2] * 0.05f;
fval[3] = fval[3] * 0.9f;
fval[4] = fval[4] * 0.9f;
fval[5] = fval[5] * 0.9f;
zsens *= 8;
}
/* set object offset */
if (ob) {
obofs[0] = -ob->obmat[3][0];
obofs[1] = -ob->obmat[3][1];
obofs[2] = -ob->obmat[3][2];
}
else {
copy_v3_v3(obofs, rv3d->ofs);
}
/* calc an adjustment based on distance from camera
disabled per patch 14402 */
d = 1.0f;
/* if (ob) {
sub_v3_v3v3(diff, obofs, rv3d->ofs);
d = len_v3(diff);
}
*/
reverse = (rv3d->persmat[2][1] < 0.0f) ? -1.0f : 1.0f;
/*----------------------------------------------------
* ndof device pan
*/
psens *= 1.0f + d;
curareaX = sbadjust * psens * fval[0];
curareaY = sbadjust * psens * fval[1];
dvec[0] = curareaX * rv3d->persinv[0][0] + curareaY * rv3d->persinv[1][0];
dvec[1] = curareaX * rv3d->persinv[0][1] + curareaY * rv3d->persinv[1][1];
dvec[2] = curareaX * rv3d->persinv[0][2] + curareaY * rv3d->persinv[1][2];
add_v3_v3(rv3d->ofs, dvec);
/*----------------------------------------------------
* ndof device dolly
*/
len = zsens * sbadjust * fval[2];
if (rv3d->persp==RV3D_CAMOB) {
if(rv3d->persp==RV3D_CAMOB) { /* This is stupid, please fix - TODO */
rv3d->camzoom+= 10.0f * -len;
}
if (rv3d->camzoom < minZoom) rv3d->camzoom = minZoom;
else if (rv3d->camzoom > maxZoom) rv3d->camzoom = maxZoom;
}
else if ((rv3d->dist> 0.001*v3d->grid) && (rv3d->dist<10.0*v3d->far)) {
rv3d->dist*=(1.0 + len);
}
/*----------------------------------------------------
* ndof device turntable
* derived from the turntable code in viewmove
*/
/* Get the 3x3 matrix and its inverse from the quaternion */
quat_to_mat3( m,rv3d->viewquat);
invert_m3_m3(m_inv,m);
/* Determine the direction of the x vector (for rotating up and down) */
/* This can likely be compuated directly from the quaternion. */
mul_m3_v3(m_inv,xvec);
mul_m3_v3(m_inv,yvec);
mul_m3_v3(m_inv,zvec);
/* Perform the up/down rotation */
phi = sbadjust * rsens * /*0.5f * */ fval[3]; /* spin vertically half as fast as horizontally */
q1[0] = cos(phi);
mul_v3_v3fl(q1+1, xvec, sin(phi));
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
if (use_sel) {
conjugate_qt(q1); /* conj == inv for unit quat */
sub_v3_v3(rv3d->ofs, obofs);
mul_qt_v3(q1, rv3d->ofs);
add_v3_v3(rv3d->ofs, obofs);
}
/* Perform the orbital rotation */
/* Perform the orbital rotation
If the seen Up axis is parallel to the zoom axis, rotation should be
achieved with a pure Roll motion (no Spin) on the device. When you start
to tilt, moving from Top to Side view, Spinning will increasingly become
more relevant while the Roll component will decrease. When a full
Side view is reached, rotations around the world's Up axis are achieved
with a pure Spin-only motion. In other words the control of the spinning
around the world's Up axis should move from the device's Spin axis to the
device's Roll axis depending on the orientation of the world's Up axis
relative to the screen. */
//phi = sbadjust * rsens * reverse * fval[4]; /* spin the knob, y axis */
phi = sbadjust * rsens * (yvec[2] * fval[4] + zvec[2] * fval[5]);
q1[0] = cos(phi);
q1[1] = q1[2] = 0.0;
q1[3] = sin(phi);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, q1);
if (use_sel) {
conjugate_qt(q1);
sub_v3_v3(rv3d->ofs, obofs);
mul_qt_v3(q1, rv3d->ofs);
add_v3_v3(rv3d->ofs, obofs);
}
/*----------------------------------------------------
* refresh the screen
*/
// XXX scrarea_do_windraw(curarea);
}
#endif // if 0, unused NDof code
/* Gets the view trasnformation from a camera
* currently dosnt take camzoom into account
*

@ -29,6 +29,8 @@
/* defines VIEW3D_OT_fly modal operator */
//#define NDOF_FLY_DEBUG
#include "DNA_anim_types.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@ -143,7 +145,6 @@ void fly_modal_keymap(wmKeyConfig *keyconf)
/* assign map to operators */
WM_modalkeymap_assign(keymap, "VIEW3D_OT_fly");
}
typedef struct FlyInfo {
@ -158,7 +159,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 +260,10 @@ static int initFlyInfo (bContext *C, FlyInfo *fly, wmOperator *op, wmEvent *even
fly->ar = CTX_wm_region(C);
fly->scene= CTX_data_scene(C);
#ifdef NDOF_FLY_DEBUG
puts("\n-- fly begin --");
#endif
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 +289,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 +338,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 +356,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 +373,10 @@ static int flyEnd(bContext *C, FlyInfo *fly)
if(fly->state == FLY_RUNNING)
return OPERATOR_RUNNING_MODAL;
#ifdef NDOF_FLY_DEBUG
puts("\n-- fly end --");
#endif
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 +422,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 +436,54 @@ 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
#ifdef NDOF_FLY_DEBUG
puts("start keeping track of 3D mouse position");
#endif
// fall through...
case P_IN_PROGRESS:
// update 3D mouse position
#ifdef NDOF_FLY_DEBUG
putchar('.'); fflush(stdout);
#endif
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
#ifdef NDOF_FLY_DEBUG
puts("stop keeping track of 3D mouse position");
#endif
if (fly->ndof)
{
MEM_freeN(fly->ndof);
// free(fly->ndof);
fly->ndof = NULL;
}
/* update the time else the view will jump when 2D mouse/timer resume */
fly->time_lastdraw= PIL_check_seconds_timer();
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,14 +594,81 @@ static void flyEvent(FlyInfo *fly, wmEvent *event)
case FLY_MODAL_PRECISION_DISABLE:
fly->use_precision= FALSE;
break;
}
}
}
static void move_camera(bContext* C, RegionView3D* rv3d, FlyInfo* fly, int orientationChanged, int positionChanged)
{
/* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
View3D* v3d = fly->v3d;
Scene *scene= fly->scene;
ID *id_key;
/* transform the parent or the camera? */
if(fly->root_parent) {
Object *ob_update;
float view_mat[4][4];
float prev_view_mat[4][4];
float prev_view_imat[4][4];
float diff_mat[4][4];
float parent_mat[4][4];
ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
invert_m4_m4(prev_view_imat, prev_view_mat);
ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
// where_is_object(scene, fly->root_parent);
ob_update= v3d->camera->parent;
while(ob_update) {
DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
ob_update= ob_update->parent;
}
id_key= &fly->root_parent->id;
}
else {
float view_mat[4][4];
ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
id_key= &v3d->camera->id;
}
/* record the motion */
if (autokeyframe_cfra_can_key(scene, id_key)) {
ListBase dsources = {NULL, NULL};
/* add datasource override for the camera object */
ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL);
/* insert keyframes
* 1) on the first frame
* 2) on each subsequent frame
* TODO: need to check in future that frame changed before doing this
*/
if (orientationChanged) {
KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
}
if (positionChanged) {
KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
}
/* free temp data */
BLI_freelistN(&dsources);
}
}
static int flyApply(bContext *C, FlyInfo *fly)
{
#define FLY_ROTATE_FAC 2.5f /* more is faster */
#define FLY_ZUP_CORRECT_FAC 0.1f /* amount to correct per step */
#define FLY_ZUP_CORRECT_ACCEL 0.05f /* increase upright momentum each step */
@ -545,11 +678,7 @@ static int flyApply(bContext *C, FlyInfo *fly)
a fly loop where the user can move move the view as if they are flying
*/
RegionView3D *rv3d= fly->rv3d;
View3D *v3d = fly->v3d;
ARegion *ar = fly->ar;
Scene *scene= fly->scene;
float prev_view_mat[4][4];
float mat[3][3], /* 3x3 copy of the view matrix so we can move allong the view axis */
dvec[3]={0,0,0}, /* this is the direction thast added to the view offset per redraw */
@ -567,15 +696,11 @@ 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*/
if(fly->root_parent)
ED_view3d_to_m4(prev_view_mat, fly->rv3d->ofs, fly->rv3d->viewquat, fly->rv3d->dist);
#ifdef NDOF_FLY_DEBUG
static unsigned int iteration = 1;
printf("fly timer %d\n", iteration++);
#endif
/* 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 +747,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 */
@ -690,7 +817,7 @@ static int flyApply(bContext *C, FlyInfo *fly)
mul_m3_v3(mat, upvec);
}
axis_angle_to_quat( tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
axis_angle_to_quat(tmp_quat, upvec, (float)moffset[0] * time_redraw * FLY_ROTATE_FAC); /* Rotate about the relative up vec */
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, tmp_quat);
if (fly->xlock) fly->xlock = 2;/*check for rotation*/
@ -784,69 +911,9 @@ static int flyApply(bContext *C, FlyInfo *fly)
ED_area_headerprint(fly->ar, "FlyKeys Speed:(+/- | Wheel), Upright Axis:X off/Z off, Slow:Shift, Direction:WASDRF, Ok:LMB, Pan:MMB, Cancel:RMB");
#endif
/* we are in camera view so apply the view ofs and quat to the view matrix and set the camera to the view */
if (rv3d->persp==RV3D_CAMOB) {
ID *id_key;
/* transform the parent or the camera? */
if(fly->root_parent) {
Object *ob_update;
if (rv3d->persp==RV3D_CAMOB)
move_camera(C, rv3d, fly, (fly->xlock || fly->zlock || moffset[0] || moffset[1]), fly->speed);
float view_mat[4][4];
float prev_view_imat[4][4];
float diff_mat[4][4];
float parent_mat[4][4];
invert_m4_m4(prev_view_imat, prev_view_mat);
ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
mul_m4_m4m4(diff_mat, prev_view_imat, view_mat);
mul_m4_m4m4(parent_mat, fly->root_parent->obmat, diff_mat);
object_apply_mat4(fly->root_parent, parent_mat, TRUE, FALSE);
// where_is_object(scene, fly->root_parent);
ob_update= v3d->camera->parent;
while(ob_update) {
DAG_id_tag_update(&ob_update->id, OB_RECALC_OB);
ob_update= ob_update->parent;
}
copy_m4_m4(prev_view_mat, view_mat);
id_key= &fly->root_parent->id;
}
else {
float view_mat[4][4];
ED_view3d_to_m4(view_mat, rv3d->ofs, rv3d->viewquat, rv3d->dist);
object_apply_mat4(v3d->camera, view_mat, TRUE, FALSE);
id_key= &v3d->camera->id;
}
/* record the motion */
if (autokeyframe_cfra_can_key(scene, id_key)) {
ListBase dsources = {NULL, NULL};
/* add datasource override for the camera object */
ANIM_relative_keyingset_add_source(&dsources, id_key, NULL, NULL);
/* insert keyframes
* 1) on the first frame
* 2) on each subsequent frame
* TODO: need to check in future that frame changed before doing this
*/
if (fly->xlock || fly->zlock || moffset[0] || moffset[1]) {
KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Rotation");
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
}
if (fly->speed) {
KeyingSet *ks= ANIM_builtin_keyingset_get_named(NULL, "Location");
ANIM_apply_keyingset(C, &dsources, NULL, ks, MODIFYKEY_MODE_INSERT, (float)CFRA);
}
/* free temp data */
BLI_freelistN(&dsources);
}
}
} else
/*were not redrawing but we need to update the time else the view will jump */
fly->time_lastdraw= PIL_check_seconds_timer();
@ -854,11 +921,141 @@ 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 flag = U.ndof_flag;
// int shouldRotate = (flag & NDOF_SHOULD_ROTATE) && (fly->pan_view == FALSE),
// shouldTranslate = (flag & (NDOF_SHOULD_PAN | NDOF_SHOULD_ZOOM));
int shouldRotate = (fly->pan_view == FALSE),
shouldTranslate = TRUE;
float view_inv[4];
invert_qt_qt(view_inv, rv3d->viewquat);
rv3d->rot_angle = 0.f; // disable onscreen rotation doo-dad
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
};
if (fly->use_precision)
speed *= 0.2f;
mul_v3_fl(trans, speed * dt);
// transform motion from view to world coordinates
mul_qt_v3(view_inv, trans);
if (flag & NDOF_FLY_HELICOPTER)
{
// replace world z component with device y (yes it makes sense)
trans[2] = speed * dt * vertical_sensitivity * ndof->ty;
}
if (rv3d->persp==RV3D_CAMOB) {
// respect camera position locks
Object *lock_ob= fly->root_parent ? fly->root_parent : fly->v3d->camera;
if (lock_ob->protectflag & OB_LOCK_LOCX) trans[0] = 0.f;
if (lock_ob->protectflag & OB_LOCK_LOCY) trans[1] = 0.f;
if (lock_ob->protectflag & OB_LOCK_LOCZ) trans[2] = 0.f;
}
if (trans[0] || trans[1] || trans[2])
{
// move center of view opposite of hand motion (this is camera mode, not object mode)
sub_v3_v3(rv3d->ofs, trans);
shouldTranslate = TRUE;
}
else
shouldTranslate = FALSE;
}
if (shouldRotate)
{
const float turn_sensitivity = 1.f;
float rotation[4];
float axis[3];
float angle = turn_sensitivity * ndof_to_angle_axis(ndof, axis);
if (fabsf(angle) > 0.0001f)
{
shouldRotate = TRUE;
if (fly->use_precision)
angle *= 0.2f;
// 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);
if (flag & NDOF_LOCK_HORIZON)
// force an upright viewpoint
// TODO: make this less... sudden
{
float view_horizon[3] = {1.f, 0.f, 0.f}; // view +x
float view_direction[3] = {0.f, 0.f, -1.f}; // view -z (into screen)
// find new inverse since viewquat has changed
invert_qt_qt(view_inv, rv3d->viewquat);
// could apply reverse rotation to existing view_inv to save a few cycles
// transform view vectors to world coordinates
mul_qt_v3(view_inv, view_horizon);
mul_qt_v3(view_inv, view_direction);
// find difference between view & world horizons
// true horizon lives in world xy plane, so look only at difference in z
angle = -asinf(view_horizon[2]);
#ifdef NDOF_FLY_DEBUG
printf("lock horizon: adjusting %.1f degrees\n\n", RAD2DEG(angle));
#endif
// rotate view so view horizon = world horizon
axis_angle_to_quat(rotation, view_direction, angle);
mul_qt_qtqt(rv3d->viewquat, rv3d->viewquat, rotation);
}
rv3d->view = RV3D_VIEW_USER;
}
else
shouldRotate = FALSE;
}
if (shouldTranslate || shouldRotate)
{
fly->redraw = TRUE;
if (rv3d->persp==RV3D_CAMOB)
move_camera(C, rv3d, fly, shouldRotate, shouldTranslate);
}
return OPERATOR_FINISHED;
}
static int fly_invoke(bContext *C, wmOperator *op, wmEvent *event)
@ -908,7 +1105,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;
@ -923,6 +1125,7 @@ static int fly_modal(bContext *C, wmOperator *op, wmEvent *event)
WM_event_add_notifier(C, NC_OBJECT|ND_TRANSFORM, fly_object);
}
// puts("redraw!"); // too frequent, fix tomorrow.
ED_region_tag_redraw(CTX_wm_region(C));
}

@ -51,6 +51,7 @@ struct ARegionType;
struct bPoseChannel;
struct bAnimVizSettings;
struct bMotionPath;
struct wmNDOFMotionData;
#define BL_NEAR_CLIP 0.001
@ -72,6 +73,8 @@ void VIEW3D_OT_dolly(struct wmOperatorType *ot);
void VIEW3D_OT_zoom_camera_1_to_1(struct wmOperatorType *ot);
void VIEW3D_OT_move(struct wmOperatorType *ot);
void VIEW3D_OT_rotate(struct wmOperatorType *ot);
void VIEW3D_OT_ndof_orbit(struct wmOperatorType *ot);
void VIEW3D_OT_ndof_pan(struct wmOperatorType *ot);
void VIEW3D_OT_view_all(struct wmOperatorType *ot);
void VIEW3D_OT_viewnumpad(struct wmOperatorType *ot);
void VIEW3D_OT_view_selected(struct wmOperatorType *ot);
@ -91,6 +94,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);

@ -64,6 +64,8 @@ void view3d_operatortypes(void)
WM_operatortype_append(VIEW3D_OT_zoom);
WM_operatortype_append(VIEW3D_OT_zoom_camera_1_to_1);
WM_operatortype_append(VIEW3D_OT_dolly);
WM_operatortype_append(VIEW3D_OT_ndof_orbit);
WM_operatortype_append(VIEW3D_OT_ndof_pan);
WM_operatortype_append(VIEW3D_OT_view_all);
WM_operatortype_append(VIEW3D_OT_viewnumpad);
WM_operatortype_append(VIEW3D_OT_view_orbit);
@ -161,6 +163,17 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_all", HOMEKEY, KM_PRESS, 0, 0)->ptr, "center", 0); /* only without camera view */
RNA_boolean_set(WM_keymap_add_item(keymap, "VIEW3D_OT_view_all", CKEY, KM_PRESS, KM_SHIFT, 0)->ptr, "center", 1);
/* 3D mouse */
WM_keymap_add_item(keymap, "VIEW3D_OT_ndof_orbit", NDOF_MOTION, 0, 0, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_ndof_pan", NDOF_MOTION, 0, KM_SHIFT, 0);
WM_keymap_add_item(keymap, "VIEW3D_OT_view_selected", NDOF_BUTTON_FIT, KM_PRESS, 0, 0);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_FRONT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_BACK, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_BACK);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_LEFT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_LEFT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_RIGHT, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_RIGHT);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_TOP);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_BOTTOM, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_BOTTOM);
/* numpad view hotkeys*/
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD0, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_CAMERA);
RNA_enum_set(WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", PAD1, KM_PRESS, 0, 0)->ptr, "type", RV3D_VIEW_FRONT);
@ -210,6 +223,17 @@ void view3d_keymap(wmKeyConfig *keyconf)
RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_BOTTOM);
RNA_boolean_set(kmi->ptr, "align_active", TRUE);
/* 3D mouse align */
kmi= WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_FRONT, KM_PRESS, KM_SHIFT, 0);
RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_FRONT);
RNA_boolean_set(kmi->ptr, "align_active", TRUE);
kmi= WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_RIGHT, KM_PRESS, KM_SHIFT, 0);
RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_RIGHT);
RNA_boolean_set(kmi->ptr, "align_active", TRUE);
kmi= WM_keymap_add_item(keymap, "VIEW3D_OT_viewnumpad", NDOF_BUTTON_TOP, KM_PRESS, KM_SHIFT, 0);
RNA_enum_set(kmi->ptr, "type", RV3D_VIEW_TOP);
RNA_boolean_set(kmi->ptr, "align_active", TRUE);
WM_keymap_add_item(keymap, "VIEW3D_OT_localview", PADSLASHKEY, KM_PRESS, 0, 0);
/* layers, shift + alt are properties set in invoke() */

@ -1006,9 +1006,11 @@ int transformEvent(TransInfo *t, wmEvent *event)
else view_editmove(event->type);
t->redraw= 1;
break;
// case NDOFMOTION:
// viewmoveNDOF(1);
// break;
#if 0
case NDOF_MOTION:
// should have been caught by tranform_modal
return OPERATOR_PASS_THROUGH;
#endif
default:
handled = 0;
break;
@ -1017,43 +1019,6 @@ int transformEvent(TransInfo *t, wmEvent *event)
// Numerical input events
t->redraw |= handleNumInput(&(t->num), event);
// NDof input events
switch(handleNDofInput(&(t->ndof), event))
{
case NDOF_CONFIRM:
if ((t->options & CTX_NDOF) == 0)
{
/* Confirm on normal transform only */
t->state = TRANS_CONFIRM;
}
break;
case NDOF_CANCEL:
if (t->options & CTX_NDOF)
{
/* Cancel on pure NDOF transform */
t->state = TRANS_CANCEL;
}
else
{
/* Otherwise, just redraw, NDof input was cancelled */
t->redraw |= TREDRAW_HARD;
}
break;
case NDOF_NOMOVE:
if (t->options & CTX_NDOF)
{
/* Confirm on pure NDOF transform */
t->state = TRANS_CONFIRM;
}
break;
case NDOF_REFRESH:
t->redraw |= TREDRAW_HARD;
break;
default:
handled = 0;
break;
}
// Snapping events
t->redraw |= handleSnapping(t, event);
@ -2886,10 +2851,6 @@ void initRotation(TransInfo *t)
setInputPostFct(&t->mouse, postInputRotation);
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
t->ndof.axis = 16;
/* Scale down and flip input for rotation */
t->ndof.factor[0] = -0.2f;
t->idx_max = 0;
t->num.idx_max = 0;
t->snap[0] = 0.0f;
@ -3161,8 +3122,6 @@ int Rotation(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
applyNDofInput(&t->ndof, &final);
snapGrid(t, &final);
if ((t->con.mode & CON_APPLY) && t->con.applyRot) {
@ -3216,11 +3175,6 @@ void initTrackball(TransInfo *t)
initMouseInputMode(t, &t->mouse, INPUT_TRACKBALL);
t->ndof.axis = 40;
/* Scale down input for rotation */
t->ndof.factor[0] = 0.2f;
t->ndof.factor[1] = 0.2f;
t->idx_max = 1;
t->num.idx_max = 1;
t->snap[0] = 0.0f;
@ -3276,8 +3230,6 @@ int Trackball(TransInfo *t, const int UNUSED(mval[2]))
phi[0] = t->values[0];
phi[1] = t->values[1];
applyNDofInput(&t->ndof, phi);
snapGrid(t, phi);
if (hasNumInput(&t->num)) {
@ -3331,8 +3283,6 @@ void initTranslation(TransInfo *t)
t->num.flag = 0;
t->num.idx_max = t->idx_max;
t->ndof.axis = (t->flag & T_2D_EDIT)? 1|2: 1|2|4;
if(t->spacetype == SPACE_VIEW3D) {
RegionView3D *rv3d = t->ar->regiondata;
@ -3507,7 +3457,6 @@ int Translation(TransInfo *t, const int UNUSED(mval[2]))
headerTranslation(t, pvec, str);
}
else {
applyNDofInput(&t->ndof, t->values);
snapGrid(t, t->values);
applyNumInput(&t->num, t->values);
if (hasNumInput(&t->num)) {
@ -3616,10 +3565,6 @@ void initTilt(TransInfo *t)
initMouseInputMode(t, &t->mouse, INPUT_ANGLE);
t->ndof.axis = 16;
/* Scale down and flip input for rotation */
t->ndof.factor[0] = -0.2f;
t->idx_max = 0;
t->num.idx_max = 0;
t->snap[0] = 0.0f;
@ -3643,8 +3588,6 @@ int Tilt(TransInfo *t, const int UNUSED(mval[2]))
final = t->values[0];
applyNDofInput(&t->ndof, &final);
snapGrid(t, &final);
if (hasNumInput(&t->num)) {
@ -3759,10 +3702,6 @@ void initPushPull(TransInfo *t)
initMouseInputMode(t, &t->mouse, INPUT_VERTICAL_ABSOLUTE);
t->ndof.axis = 4;
/* Flip direction */
t->ndof.factor[0] = -1.0f;
t->idx_max = 0;
t->num.idx_max = 0;
t->snap[0] = 0.0f;
@ -3783,8 +3722,6 @@ int PushPull(TransInfo *t, const int UNUSED(mval[2]))
distance = t->values[0];
applyNDofInput(&t->ndof, &distance);
snapGrid(t, &distance);
applyNumInput(&t->num, &distance);
@ -5309,8 +5246,6 @@ void initSeqSlide(TransInfo *t)
t->num.flag = 0;
t->num.idx_max = t->idx_max;
t->ndof.axis = 1|2;
t->snap[0] = 0.0f;
t->snap[1] = floor(t->scene->r.frs_sec / t->scene->r.frs_sec_base);
t->snap[2] = 10.0f;
@ -5365,7 +5300,6 @@ int SeqSlide(TransInfo *t, const int UNUSED(mval[2]))
VECCOPY(t->values, tvec);
}
else {
applyNDofInput(&t->ndof, t->values);
snapGrid(t, t->values);
applyNumInput(&t->num, t->values);
}
@ -5925,54 +5859,3 @@ void BIF_TransformSetUndo(char *UNUSED(str))
// TRANSFORM_FIX_ME
//Trans.undostr= str;
}
#if 0 // TRANSFORM_FIX_ME
static void NDofTransform(void)
{
float fval[7];
float maxval = 50.0f; // also serves as threshold
int axis = -1;
int mode = 0;
int i;
getndof(fval);
for(i = 0; i < 6; i++)
{
float val = fabs(fval[i]);
if (val > maxval)
{
axis = i;
maxval = val;
}
}
switch(axis)
{
case -1:
/* No proper axis found */
break;
case 0:
case 1:
case 2:
mode = TFM_TRANSLATION;
break;
case 4:
mode = TFM_ROTATION;
break;
case 3:
case 5:
mode = TFM_TRACKBALL;
break;
default:
printf("ndof: what we are doing here ?");
}
if (mode != 0)
{
initTransform(mode, CTX_NDOF);
Transform();
}
}
#endif

@ -360,6 +360,12 @@ static int transform_modal(bContext *C, wmOperator *op, wmEvent *event)
TransInfo *t = op->customdata;
if (event->type == NDOF_MOTION)
{
// puts("transform_modal: passing through NDOF_MOTION");
return OPERATOR_PASS_THROUGH;
}
/* XXX insert keys are called here, and require context */
t->context= C;
exit_code = transformEvent(t, event);

@ -365,7 +365,6 @@ typedef struct UserDef {
short recent_files; /* maximum number of recently used files to remember */
short smooth_viewtx; /* miliseconds to spend spinning the view */
short glreslimit;
short ndof_pan, ndof_rotate;
short curssize;
short color_picker_type;
short ipo_new; /* interpolation mode for newly added F-Curves */
@ -376,6 +375,10 @@ typedef struct UserDef {
short widget_unit; /* defaults to 20 for 72 DPI setting */
short anisotropic_filter;
/*short pad[3]; */
float ndof_sensitivity; /* overall sensitivity of 3D mouse */
int ndof_flag; /* flags for 3D mouse */
char versemaster[160];
char verseuser[160];
@ -384,7 +387,7 @@ typedef struct UserDef {
short autokey_mode; /* autokeying mode */
short autokey_flag; /* flags for autokeying */
short text_render, pad9; /*options for text rendering*/
short text_render, pad9[3]; /*options for text rendering*/
struct ColorBand coba_weight; /* from texture.h */
@ -577,6 +580,28 @@ extern UserDef U; /* from blenkernel blender.c */
#define TH_OLDSKOOL 3
#define TH_SHADED 4
/* ndof_flag (3D mouse options) */
#define NDOF_SHOW_GUIDE (1 << 0)
#define NDOF_FLY_HELICOPTER (1 << 1)
#define NDOF_LOCK_HORIZON (1 << 2)
/* the following might not need to be saved between sessions,
but they do need to live somewhere accessible... */
#define NDOF_SHOULD_PAN (1 << 3)
#define NDOF_SHOULD_ZOOM (1 << 4)
#define NDOF_SHOULD_ROTATE (1 << 5)
/* orbit navigation modes
only two options, so it's sort of a hyrbrid bool/enum
if ((U.ndof_flag & NDOF_ORBIT_MODE) == NDOF_OM_OBJECT)... */
/*
#define NDOF_ORBIT_MODE (1 << 6)
#define NDOF_OM_TARGETCAMERA 0
#define NDOF_OM_OBJECT NDOF_ORBIT_MODE
*/
/* actually... users probably don't care about what the mode
is called, just that it feels right */
#define NDOF_ORBIT_INVERT_AXES (1 << 6)
#ifdef __cplusplus
}
#endif

@ -130,7 +130,11 @@ typedef struct RegionView3D {
float twangle[3];
float padf;
/* active rotation from NDOF or elsewhere */
float rot_angle;
float rot_axis[3];
char pad2[4];
} RegionView3D;
@ -190,11 +194,10 @@ typedef struct View3D {
/* drawflags, denoting state */
short zbuf, transp, xray;
char ndofmode; /* mode of transform for 6DOF devices -1 not found, 0 normal, 1 fly, 2 ob transform */
char ndoffilter; /* filter for 6DOF devices 0 normal, 1 dominant */
char pad3[2];
void *properties_storage; /* Nkey panel stores stuff here (runtime only!) */
/* XXX depricated? */
struct bGPdata *gpd; /* Grease-Pencil Data (annotation layers) */

@ -2737,17 +2737,30 @@ static void rna_def_userdef_input(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "dragthreshold");
RNA_def_property_range(prop, 3, 40);
RNA_def_property_ui_text(prop, "Drag Threshold", "Amount of pixels you have to drag before dragging UI items happens");
prop= RNA_def_property(srna, "ndof_pan_speed", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "ndof_pan");
RNA_def_property_range(prop, 0, 200);
RNA_def_property_ui_text(prop, "NDof Pan Speed", "The overall panning speed of an NDOF device, as percent of standard");
prop= RNA_def_property(srna, "ndof_rotate_speed", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "ndof_rotate");
RNA_def_property_range(prop, 0, 200);
RNA_def_property_ui_text(prop, "NDof Rotation Speed", "The overall rotation speed of an NDOF device, as percent of standard");
/* 3D mouse settings */
prop= RNA_def_property(srna, "ndof_sensitivity", PROP_FLOAT, PROP_NONE);
RNA_def_property_range(prop, 0.25f, 4.0f);
RNA_def_property_ui_text(prop, "Sensitivity", "Overall sensitivity of the 3D Mouse");
prop= RNA_def_property(srna, "ndof_show_guide", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_SHOW_GUIDE);
RNA_def_property_ui_text(prop, "Show Navigation Guide", "Display the center and axis during rotation");
/* TODO: update description when fly-mode visuals are in place ("projected position in fly mode")*/
prop= RNA_def_property(srna, "ndof_orbit_invert_axes", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_ORBIT_INVERT_AXES);
RNA_def_property_ui_text(prop, "Invert Axes", "Toggle between moving the viewpoint or moving the scene being viewed");
prop= RNA_def_property(srna, "ndof_lock_horizon", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_LOCK_HORIZON);
RNA_def_property_ui_text(prop, "Lock Horizon", "Keep horizon level while flying with 3D Mouse");
prop= RNA_def_property(srna, "ndof_fly_helicopter", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "ndof_flag", NDOF_FLY_HELICOPTER);
RNA_def_property_ui_text(prop, "Helicopter Mode", "Device up/down directly controls your Z position");
prop= RNA_def_property(srna, "mouse_double_click_time", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "dbl_click_time");
RNA_def_property_range(prop, 1, 1000);

@ -106,6 +106,46 @@ EnumPropertyItem event_timer_type_items[]= {
{TIMER2, "TIMER2", 0, "Timer 2", ""},
{0, NULL, 0, NULL, NULL}};
EnumPropertyItem event_ndof_type_items[]= {
/* buttons on all 3dconnexion devices */
{NDOF_BUTTON_MENU, "NDOF_BUTTON_MENU", 0, "Menu", ""},
{NDOF_BUTTON_FIT, "NDOF_BUTTON_FIT", 0, "Fit", ""},
/* view buttons */
{NDOF_BUTTON_TOP, "NDOF_BUTTON_TOP", 0, "Top", ""},
{NDOF_BUTTON_BOTTOM, "NDOF_BUTTON_BOTTOM", 0, "Bottom", ""},
{NDOF_BUTTON_LEFT, "NDOF_BUTTON_LEFT", 0, "Left", ""},
{NDOF_BUTTON_RIGHT, "NDOF_BUTTON_RIGHT", 0, "Right", ""},
{NDOF_BUTTON_FRONT, "NDOF_BUTTON_FRONT", 0, "Front", ""},
{NDOF_BUTTON_BACK, "NDOF_BUTTON_BACK", 0, "Back", ""},
/* more views */
{NDOF_BUTTON_ISO1, "NDOF_BUTTON_ISO1", 0, "ISO 1", ""},
{NDOF_BUTTON_ISO2, "NDOF_BUTTON_ISO2", 0, "ISO 2", ""},
/* 90 degree rotations */
{NDOF_BUTTON_ROLL_CW, "NDOF_BUTTON_ROLL_CW", 0, "Roll CW", ""},
{NDOF_BUTTON_ROLL_CCW, "NDOF_BUTTON_ROLL_CCW", 0, "Roll CCW", ""},
{NDOF_BUTTON_SPIN_CW, "NDOF_BUTTON_SPIN_CW", 0, "Spin CW", ""},
{NDOF_BUTTON_SPIN_CCW, "NDOF_BUTTON_SPIN_CCW", 0, "Spin CCW", ""},
{NDOF_BUTTON_TILT_CW, "NDOF_BUTTON_TILT_CW", 0, "Tilt CW", ""},
{NDOF_BUTTON_TILT_CCW, "NDOF_BUTTON_TILT_CCW", 0, "Tilt CCW", ""},
/* device control */
{NDOF_BUTTON_ROTATE, "NDOF_BUTTON_ROTATE", 0, "Rotate", ""},
{NDOF_BUTTON_PANZOOM, "NDOF_BUTTON_PANZOOM", 0, "Pan/Zoom", ""},
{NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "Dominant", ""},
{NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "Plus", ""},
{NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "Minus", ""},
/* general-purpose buttons */
{NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "Button 1", ""},
{NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "Button 2", ""},
{NDOF_BUTTON_3, "NDOF_BUTTON_3", 0, "Button 3", ""},
{NDOF_BUTTON_4, "NDOF_BUTTON_4", 0, "Button 4", ""},
{NDOF_BUTTON_5, "NDOF_BUTTON_5", 0, "Button 5", ""},
{NDOF_BUTTON_6, "NDOF_BUTTON_6", 0, "Button 6", ""},
{NDOF_BUTTON_7, "NDOF_BUTTON_7", 0, "Button 7", ""},
{NDOF_BUTTON_8, "NDOF_BUTTON_8", 0, "Button 8", ""},
{NDOF_BUTTON_9, "NDOF_BUTTON_9", 0, "Button 9", ""},
{NDOF_BUTTON_10, "NDOF_BUTTON_10", 0, "Button 10", ""},
{0, NULL, 0, NULL, NULL}};
/* not returned: CAPSLOCKKEY, UNKNOWNKEY */
EnumPropertyItem event_type_items[] = {
@ -256,6 +296,44 @@ EnumPropertyItem event_type_items[] = {
{TIMER0, "TIMER0", 0, "Timer 0", ""},
{TIMER1, "TIMER1", 0, "Timer 1", ""},
{TIMER2, "TIMER2", 0, "Timer 2", ""},
{0, "", 0, NULL, NULL},
/* buttons on all 3dconnexion devices */
{NDOF_BUTTON_MENU, "NDOF_BUTTON_MENU", 0, "Menu", ""},
{NDOF_BUTTON_FIT, "NDOF_BUTTON_FIT", 0, "Fit", ""},
/* view buttons */
{NDOF_BUTTON_TOP, "NDOF_BUTTON_TOP", 0, "Top", ""},
{NDOF_BUTTON_BOTTOM, "NDOF_BUTTON_BOTTOM", 0, "Bottom", ""},
{NDOF_BUTTON_LEFT, "NDOF_BUTTON_LEFT", 0, "Left", ""},
{NDOF_BUTTON_RIGHT, "NDOF_BUTTON_RIGHT", 0, "Right", ""},
{NDOF_BUTTON_FRONT, "NDOF_BUTTON_FRONT", 0, "Front", ""},
{NDOF_BUTTON_BACK, "NDOF_BUTTON_BACK", 0, "Back", ""},
/* more views */
{NDOF_BUTTON_ISO1, "NDOF_BUTTON_ISO1", 0, "ISO 1", ""},
{NDOF_BUTTON_ISO2, "NDOF_BUTTON_ISO2", 0, "ISO 2", ""},
/* 90 degree rotations */
{NDOF_BUTTON_ROLL_CW, "NDOF_BUTTON_ROLL_CW", 0, "Roll CW", ""},
{NDOF_BUTTON_ROLL_CCW, "NDOF_BUTTON_ROLL_CCW", 0, "Roll CCW", ""},
{NDOF_BUTTON_SPIN_CW, "NDOF_BUTTON_SPIN_CW", 0, "Spin CW", ""},
{NDOF_BUTTON_SPIN_CCW, "NDOF_BUTTON_SPIN_CCW", 0, "Spin CCW", ""},
{NDOF_BUTTON_TILT_CW, "NDOF_BUTTON_TILT_CW", 0, "Tilt CW", ""},
{NDOF_BUTTON_TILT_CCW, "NDOF_BUTTON_TILT_CCW", 0, "Tilt CCW", ""},
/* device control */
{NDOF_BUTTON_ROTATE, "NDOF_BUTTON_ROTATE", 0, "Rotate", ""},
{NDOF_BUTTON_PANZOOM, "NDOF_BUTTON_PANZOOM", 0, "Pan/Zoom", ""},
{NDOF_BUTTON_DOMINANT, "NDOF_BUTTON_DOMINANT", 0, "Dominant", ""},
{NDOF_BUTTON_PLUS, "NDOF_BUTTON_PLUS", 0, "Plus", ""},
{NDOF_BUTTON_MINUS, "NDOF_BUTTON_MINUS", 0, "Minus", ""},
/* general-purpose buttons */
{NDOF_BUTTON_1, "NDOF_BUTTON_1", 0, "Button 1", ""},
{NDOF_BUTTON_2, "NDOF_BUTTON_2", 0, "Button 2", ""},
{NDOF_BUTTON_3, "NDOF_BUTTON_3", 0, "Button 3", ""},
{NDOF_BUTTON_4, "NDOF_BUTTON_4", 0, "Button 4", ""},
{NDOF_BUTTON_5, "NDOF_BUTTON_5", 0, "Button 5", ""},
{NDOF_BUTTON_6, "NDOF_BUTTON_6", 0, "Button 6", ""},
{NDOF_BUTTON_7, "NDOF_BUTTON_7", 0, "Button 7", ""},
{NDOF_BUTTON_8, "NDOF_BUTTON_8", 0, "Button 8", ""},
{NDOF_BUTTON_9, "NDOF_BUTTON_9", 0, "Button 9", ""},
{NDOF_BUTTON_10, "NDOF_BUTTON_10", 0, "Button 10", ""},
{0, NULL, 0, NULL, NULL}};
EnumPropertyItem keymap_propvalue_items[] = {
@ -303,6 +381,7 @@ EnumPropertyItem wm_report_items[] = {
#define KMI_TYPE_TWEAK 2
#define KMI_TYPE_TEXTINPUT 3
#define KMI_TYPE_TIMER 4
#define KMI_TYPE_NDOF 5
#ifdef RNA_RUNTIME
@ -433,6 +512,7 @@ static int rna_wmKeyMapItem_map_type_get(PointerRNA *ptr)
if(ISKEYBOARD(kmi->type)) return KMI_TYPE_KEYBOARD;
if(ISTWEAK(kmi->type)) return KMI_TYPE_TWEAK;
if(ISMOUSE(kmi->type)) return KMI_TYPE_MOUSE;
if(ISNDOF(kmi->type)) return KMI_TYPE_NDOF;
if(kmi->type == KM_TEXTINPUT) return KMI_TYPE_TEXTINPUT;
return KMI_TYPE_KEYBOARD;
}
@ -464,6 +544,10 @@ static void rna_wmKeyMapItem_map_type_set(PointerRNA *ptr, int value)
kmi->type= TIMER;
kmi->val= KM_NOTHING;
break;
case KMI_TYPE_NDOF:
kmi->type = NDOF_BUTTON_MENU;
kmi->val = KM_NOTHING;
break;
}
}
}
@ -475,6 +559,7 @@ static EnumPropertyItem *rna_KeyMapItem_type_itemf(bContext *UNUSED(C), PointerR
if(map_type == KMI_TYPE_MOUSE) return event_mouse_type_items;
if(map_type == KMI_TYPE_TWEAK) return event_tweak_type_items;
if(map_type == KMI_TYPE_TIMER) return event_timer_type_items;
if(map_type == KMI_TYPE_NDOF) return event_ndof_type_items;
else return event_type_items;
}
@ -482,7 +567,7 @@ static EnumPropertyItem *rna_KeyMapItem_value_itemf(bContext *UNUSED(C), Pointer
{
int map_type= rna_wmKeyMapItem_map_type_get(ptr);
if(map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD) return event_keymouse_value_items;
if(map_type == KMI_TYPE_MOUSE || map_type == KMI_TYPE_KEYBOARD || map_type == KMI_TYPE_NDOF) return event_keymouse_value_items;
if(map_type == KMI_TYPE_TWEAK) return event_tweak_value_items;
else return event_value_items;
}
@ -1653,6 +1738,7 @@ static void rna_def_keyconfig(BlenderRNA *brna)
{KMI_TYPE_KEYBOARD, "KEYBOARD", 0, "Keyboard", ""},
{KMI_TYPE_TWEAK, "TWEAK", 0, "Tweak", ""},
{KMI_TYPE_MOUSE, "MOUSE", 0, "Mouse", ""},
{KMI_TYPE_NDOF, "NDOF", 0, "NDOF", ""},
{KMI_TYPE_TEXTINPUT, "TEXTINPUT", 0, "Text Input", ""},
{KMI_TYPE_TIMER, "TIMER", 0, "Timer", ""},
{0, NULL, 0, NULL, NULL}};

@ -377,6 +377,26 @@ typedef struct wmTabletData {
float Ytilt; /* as above */
} wmTabletData;
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.
// 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 {
struct wmTimer *next, *prev;

@ -1815,7 +1815,10 @@ void wm_event_do_handlers(bContext *C)
/* for regions having custom cursors */
wm_paintcursor_test(C, event);
}
else if (event->type==NDOF_MOTION) {
win->addmousemove = TRUE;
}
for(sa= win->screen->areabase.first; sa; sa= sa->next) {
if(wm_event_inside_i(event, &sa->totrct)) {
CTX_wm_area_set(C, sa);
@ -1879,7 +1882,10 @@ void wm_event_do_handlers(bContext *C)
if(doit && win->screen && win->screen->subwinactive != win->screen->mainwin) {
win->eventstate->prevx= event->x;
win->eventstate->prevy= event->y;
//printf("win->eventstate->prev = %d %d\n", event->x, event->y);
}
else
;//printf("not setting prev to %d %d\n", event->x, event->y);
}
/* store last event for this window */
@ -1922,6 +1928,7 @@ void wm_event_do_handlers(bContext *C)
/* only add mousemove when queue was read entirely */
if(win->addmousemove && win->eventstate) {
wmEvent tevent= *(win->eventstate);
//printf("adding MOUSEMOVE %d %d\n", tevent.x, tevent.y);
tevent.type= MOUSEMOVE;
tevent.prevx= tevent.x;
tevent.prevy= tevent.y;
@ -2309,6 +2316,30 @@ static void update_tablet_data(wmWindow *win, wmEvent *event)
}
}
/* adds customdata to event */
static void attach_ndof_data(wmEvent* event, const GHOST_TEventNDOFMotionData* ghost)
{
wmNDOFMotionData* data = MEM_mallocN(sizeof(wmNDOFMotionData), "customdata NDOF");
const float s = U.ndof_sensitivity;
data->tx = s * ghost->tx;
data->ty = s * ghost->ty;
data->tz = s * ghost->tz;
data->rx = s * ghost->rx;
data->ry = s * ghost->ry;
data->rz = s * ghost->rz;
data->dt = ghost->dt;
data->progress = (wmProgress) ghost->progress;
event->custom = EVT_DATA_NDOF_MOTION;
event->customdata = data;
event->customdatafree = 1;
}
/* imperfect but probably usable... draw/enable drags to other windows */
static wmWindow *wm_event_cursor_other_windows(wmWindowManager *wm, wmWindow *win, wmEvent *evt)
{
@ -2355,7 +2386,7 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
{
wmWindow *owin;
wmEvent event, *evt= win->eventstate;
/* initialize and copy state (only mouse x y and modifiers) */
event= *evt;
@ -2384,6 +2415,8 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
update_tablet_data(win, &event);
wm_event_add(win, &event);
//printf("sending MOUSEMOVE %d %d\n", event.x, event.y);
/* also add to other window if event is there, this makes overdraws disappear nicely */
/* it remaps mousecoord to other window in event */
@ -2557,6 +2590,38 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
break;
}
case GHOST_kEventNDOFMotion: {
event.type = NDOF_MOTION;
attach_ndof_data(&event, customdata);
wm_event_add(win, &event);
//printf("sending NDOF_MOTION, prev = %d %d\n", event.x, event.y);
break;
}
case GHOST_kEventNDOFButton: {
GHOST_TEventNDOFButtonData* e = customdata;
event.type = NDOF_BUTTON_NONE + e->button;
switch (e->action) {
case GHOST_kPress:
event.val = KM_PRESS;
break;
case GHOST_kRelease:
event.val = KM_RELEASE;
break;
}
event.custom = 0;
event.customdata = NULL;
wm_event_add(win, &event);
break;
}
case GHOST_kEventUnknown:
case GHOST_kNumEventTypes:
break;

@ -183,8 +183,6 @@ void WM_init(bContext *C, int argc, const char **argv)
ED_preview_init_dbase();
G.ndofdevice = -1; /* XXX bad initializer, needs set otherwise buttons show! */
WM_read_history();
/* allow a path of "", this is what happens when making a new file */

@ -1411,6 +1411,22 @@ static void WM_OT_search_menu(wmOperatorType *ot)
ot->poll= wm_search_menu_poll;
}
static int wm_ndof_menu_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
{
uiPupMenuInvoke(C,"VIEW3D_MT_ndof_settings");
return OPERATOR_FINISHED; // <-- correct?
return OPERATOR_CANCELLED; // <-- correct?
}
static void WM_OT_ndof_menu(wmOperatorType *ot)
{
ot->name = "NDOF Menu";
ot->idname = "WM_OT_ndof_menu";
ot->invoke = wm_ndof_menu_invoke;
}
static int wm_call_menu_exec(bContext *C, wmOperator *op)
{
char idname[BKE_ST_MAXNAME];
@ -3416,7 +3432,55 @@ static void WM_OT_memory_statistics(wmOperatorType *ot)
}
/* ******************************************************* */
static int wm_ndof_sensitivity_exec(bContext *UNUSED(C), wmOperator *op)
{
const float min = 0.25f, max = 4.f; // TODO: get these from RNA property
float change;
float sensitivity = U.ndof_sensitivity;
if(RNA_boolean_get(op->ptr, "fast"))
change = 0.5f; // 50% change
else
change = 0.1f; // 10%
if(RNA_boolean_get(op->ptr, "decrease"))
{
sensitivity -= sensitivity * change;
if (sensitivity < min)
sensitivity = min;
}
else
{
sensitivity += sensitivity * change;
if (sensitivity > max)
sensitivity = max;
}
if (sensitivity != U.ndof_sensitivity)
{
U.ndof_sensitivity = sensitivity;
printf("new sensitivity: %f\n", U.ndof_sensitivity);
}
else
printf("same sensitivity: %f\n", U.ndof_sensitivity);
return OPERATOR_FINISHED;
}
static void WM_OT_ndof_sensitivity_change(wmOperatorType *ot)
{
ot->name= "Change NDOF sensitivity";
ot->idname= "WM_OT_ndof_sensitivity_change";
ot->description="Change NDOF sensitivity";
ot->exec= wm_ndof_sensitivity_exec;
RNA_def_boolean(ot->srna, "decrease", 1, "Decrease NDOF sensitivity", "If true then action decreases NDOF sensitivity instead of increasing");
RNA_def_boolean(ot->srna, "fast", 0, "Fast NDOF sensitivity change", "If true then sensitivity changes 50%, otherwise 10%");
}
/* ******************************************************* */
/* called on initialize WM_exit() */
void wm_operatortype_free(void)
{
@ -3453,8 +3517,10 @@ void wm_operatortype_init(void)
WM_operatortype_append(WM_OT_debug_menu);
WM_operatortype_append(WM_OT_splash);
WM_operatortype_append(WM_OT_search_menu);
WM_operatortype_append(WM_OT_ndof_menu);
WM_operatortype_append(WM_OT_call_menu);
WM_operatortype_append(WM_OT_radial_control);
WM_operatortype_append(WM_OT_ndof_sensitivity_change);
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
#endif
@ -3674,11 +3740,12 @@ void wm_window_keymap(wmKeyConfig *keyconf)
/* debug/testing */
WM_keymap_verify_item(keymap, "WM_OT_redraw_timer", TKEY, KM_PRESS, KM_ALT|KM_CTRL, 0);
WM_keymap_verify_item(keymap, "WM_OT_debug_menu", DKEY, KM_PRESS, KM_ALT|KM_CTRL, 0);
/* menus that can be accessed anywhere in blender */
WM_keymap_verify_item(keymap, "WM_OT_search_menu", SPACEKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "WM_OT_ndof_menu", NDOF_BUTTON_MENU, KM_PRESS, 0, 0);
/* Space switching */
kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F2KEY, KM_PRESS, KM_SHIFT, 0); /* new in 2.5x, was DXF export */
RNA_string_set(kmi->ptr, "data_path", "area.type");
RNA_string_set(kmi->ptr, "value", "LOGIC_EDITOR");
@ -3722,6 +3789,23 @@ void wm_window_keymap(wmKeyConfig *keyconf)
kmi = WM_keymap_add_item(keymap, "WM_OT_context_set_enum", F12KEY, KM_PRESS, KM_SHIFT, 0);
RNA_string_set(kmi->ptr, "data_path", "area.type");
RNA_string_set(kmi->ptr, "value", "DOPESHEET_EDITOR");
/* ndof speed */
kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_PLUS, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "decrease", FALSE);
RNA_boolean_set(kmi->ptr, "fast", FALSE);
kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_MINUS, KM_PRESS, 0, 0);
RNA_boolean_set(kmi->ptr, "decrease", TRUE);
RNA_boolean_set(kmi->ptr, "fast", FALSE);
kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_PLUS, KM_PRESS, KM_SHIFT, 0);
RNA_boolean_set(kmi->ptr, "decrease", FALSE);
RNA_boolean_set(kmi->ptr, "fast", TRUE);
kmi= WM_keymap_add_item(keymap, "WM_OT_ndof_sensitivity_change", NDOF_BUTTON_MINUS, KM_PRESS, KM_SHIFT, 0);
RNA_boolean_set(kmi->ptr, "decrease", TRUE);
RNA_boolean_set(kmi->ptr, "fast", TRUE);
gesture_circle_modal_keymap(keyconf);
gesture_border_modal_keymap(keyconf);

@ -621,12 +621,12 @@ static int ghost_event_proc(GHOST_EventHandle evt, GHOST_TUserDataPtr private)
if (!ghostwin) {
// XXX - should be checked, why are we getting an event here, and
// what is it?
puts("<!> event has no window");
return 1;
} else if (!GHOST_ValidWindow(g_system, ghostwin)) {
// XXX - should be checked, why are we getting an event here, and
// what is it?
puts("<!> event has invalid window");
return 1;
} else {
win= GHOST_GetWindowUserData(ghostwin);

@ -45,6 +45,7 @@
#define EVT_DATA_GESTURE 2
#define EVT_DATA_TIMER 3
#define EVT_DATA_LISTBASE 4
#define EVT_DATA_NDOF_MOTION 5
/* tablet active, matches GHOST_TTabletMode */
#define EVT_TABLET_NONE 0
@ -78,6 +79,56 @@
#define INBETWEEN_MOUSEMOVE 17
/* NDOF (from SpaceNavigator & friends)
These should be kept in sync with GHOST_NDOFManager.h
Ordering matters, exact values do not. */
#define NDOF_MOTION 400
enum {
// used internally, never sent
NDOF_BUTTON_NONE = NDOF_MOTION,
// these two are available from any 3Dconnexion device
NDOF_BUTTON_MENU,
NDOF_BUTTON_FIT,
// standard views
NDOF_BUTTON_TOP,
NDOF_BUTTON_BOTTOM,
NDOF_BUTTON_LEFT,
NDOF_BUTTON_RIGHT,
NDOF_BUTTON_FRONT,
NDOF_BUTTON_BACK,
// more views
NDOF_BUTTON_ISO1,
NDOF_BUTTON_ISO2,
// 90 degree rotations
NDOF_BUTTON_ROLL_CW,
NDOF_BUTTON_ROLL_CCW,
NDOF_BUTTON_SPIN_CW,
NDOF_BUTTON_SPIN_CCW,
NDOF_BUTTON_TILT_CW,
NDOF_BUTTON_TILT_CCW,
// device control
NDOF_BUTTON_ROTATE,
NDOF_BUTTON_PANZOOM,
NDOF_BUTTON_DOMINANT,
NDOF_BUTTON_PLUS,
NDOF_BUTTON_MINUS,
// general-purpose buttons
NDOF_BUTTON_1,
NDOF_BUTTON_2,
NDOF_BUTTON_3,
NDOF_BUTTON_4,
NDOF_BUTTON_5,
NDOF_BUTTON_6,
NDOF_BUTTON_7,
NDOF_BUTTON_8,
NDOF_BUTTON_9,
NDOF_BUTTON_10,
NDOF_LAST
};
/* SYSTEM : 0x01xx */
#define INPUTCHANGE 0x0103 /* input connected or disconnected */
#define WINDEACTIVATE 0x0104 /* window is deactivated, focus lost */
@ -240,8 +291,11 @@
/* test whether the event is tweak event */
#define ISTWEAK(event) (event >= EVT_TWEAK_L && event <= EVT_GESTURE)
/* test whether the event is a NDOF event */
#define ISNDOF(event) (event >= NDOF_MOTION && event < NDOF_LAST)
/* test whether event type is acceptable as hotkey, excluding modifiers */
#define ISHOTKEY(event) ((ISKEYBOARD(event) || ISMOUSE(event)) && event!=ESCKEY && !(event>=LEFTCTRLKEY && event<=LEFTSHIFTKEY) && !(event>=UNKNOWNKEY && event<=GRLESSKEY))
#define ISHOTKEY(event) ((ISKEYBOARD(event) || ISMOUSE(event) || ISNDOF(event)) && event!=ESCKEY && !(event>=LEFTCTRLKEY && event<=LEFTSHIFTKEY) && !(event>=UNKNOWNKEY && event<=GRLESSKEY))
/* **************** BLENDER GESTURE EVENTS (0x5000) **************** */