Commit 033d6150 authored by Robert Menzel's avatar Robert Menzel

added new camera class and space navigator support

parent 67d811a3
......@@ -7,6 +7,7 @@ Included third-party software:
* Includes lodepng by Lode Vandevenne, unchanged except for the include path in lodepng.cpp. lodepng is provided as-is, see license text in the lodepng source code.
* Includes OpenGL loader code created by a modified glLoadGen by Jason McKesson.
* Includes GLM by Christophe Riccio.
* Includes data type definitions from libspnav header originally by John Tsiombikas (see mini_spnav.h for details).
See LICENSE.TXT for licensing information.
......
/***********************************************************************
* Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#pragma once
#include <ACGL/ACGL.hh>
#include <ACGL/Scene/GenericCamera.hh>
namespace ACGL{
namespace HardwareSupport{
/// Controls a GenericCamera with a space navigator
class SpaceNavControl
{
public:
SpaceNavControl(Scene::GenericCamera *_cam, const glm::vec3 &_moveSensitivity = glm::vec3(1.0), const glm::vec3 &_rotateSensitivity = glm::vec3(1.0));
~SpaceNavControl();
/// Updates the camera: call this once a frame, it will poll the SpaceNavigator. The elapsed time is used to scale the movement to
/// to be framerate independent.
void update(float _elapsedSeconds);
private:
/// The referenced camera
Scene::GenericCamera *mCamera;
/// Sensitivity for moving the camera
glm::vec3 mMoveSensitivity;
/// Sensitivity for rotating the camera
glm::vec3 mRotateSensitivity;
};
} // HardwareSupport
} // ACGL
/***********************************************************************
* Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#pragma once
#include <ACGL/ACGL.hh>
namespace ACGL{
namespace HardwareSupport{
/*
* To compile:
* Linux: do nothing
* MacOS X: link to 3DconnexionClient,
* e.g. add 'SET(LIBRARIES -Wl,-framework,3DconnexionClient)' to CMakeLists.txt
* to build a version without space nav support, define NO_SPACE_NAVIGATOR_SUPPORT
* Windows: only dummy functionality will get build
*/
// Each listener should call this *once* before starting to poll,
// the first listener will start a connection to the driver.
// If the connection could be made, true will be returned, false otherwise.
// If false gets returned, SpaceNavUnregisterListener() should not get called
// and all SpaceNavPollEvent() calls will return 0 events.
bool SpaceNavRegisterListener();
// Each listener should call this *once* when stopping to listen iff the call
// to SpaceNavRegisterListener() returned true.
// Last unregister call will trigger disconnection from the driver.
void SpaceNavUnregisterListener();
enum SpaceNavEventType {
movement,
button,
other
};
// Values of the buttons:
// the 2 button space navigator:
static const int SNE_BUTTON_LEFT = 0;
static const int SNE_BUTTON_RIGHT = 1;
// the larger space pilot:
static const int SNE_BUTTON_1 = 0;
static const int SNE_BUTTON_2 = 1;
static const int SNE_BUTTON_3 = 2;
static const int SNE_BUTTON_4 = 3;
static const int SNE_BUTTON_5 = 4;
static const int SNE_BUTTON_6 = 5;
static const int SNE_BUTTON_ESC = 10;
static const int SNE_BUTTON_CTRL = 13;
static const int SNE_BUTTON_ALT = 11;
static const int SNE_BUTTON_SHIFT = 12;
static const int SNE_BUTTON_CONFIG = 20;
static const int SNE_BUTTON_PANEL = 15;
static const int SNE_BUTTON_VOL_DOWN = 17;
static const int SNE_BUTTON_VOL_UP = 16;
static const int SNE_BUTTON_DOM = 18;
static const int SNE_BUTTON_T = 6;
static const int SNE_BUTTON_L = 7;
static const int SNE_BUTTON_R = 8;
static const int SNE_BUTTON_F = 9;
static const int SNE_BUTTON_3D = 19;
static const int SNE_BUTTON_FIT = 14;
//
// translation and rotation can go from about -500 to 500.
// note that the neutral postion might not be 0,0,0
// not all axes have the same maximum value (-361 to 441 might be possible)
struct SpaceNavEvent {
SpaceNavEventType type;
int x,y,z; // translation, right-handed-coord-system (right/up/towards the user are positive)
int rx,ry,rz; // rotation, pitch-yaw-roll
int button; // button number, see values above
bool pressed; // button state, pressing the button down and release will create two events
};
// Call this to poll for new events.
// Return value is the number of valid events waiting, 0 means no event
// is waiting and the eventToFill will not get changed in that situation.
// If no device was found or the connection to the driver failed for other
// reasons, 0 will get returned.
// -> polling for events and only interpreting them if >0 gets returned
// should be always save!
unsigned int SpaceNavPollEvent( SpaceNavEvent &eventToFill );
} // HardwareSupport
} // ACGL
/*
This file is based on a part of libspnav, part of the spacenav project (spacenav.sf.net)
Original file (spnav.h) Copyright (C) 2007-2010 John Tsiombikas <nuclear@member.fsf.org>
This file (mini_spnav.h) Copyright (C) 2013 Robert Menzel
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/
#ifndef MINI_SPACENAV_H_
#define MINI_SPACENAV_H_
/*
* This file can be used as an alternative to spnav.h iff the library should get loaded
* dynamically. Add this to your project and the code will compile independent of the
* presence of libspnav (and also run independent of it).
*/
#include <dlfcn.h> // for dlopen
enum {
SPNAV_EVENT_ANY, /* used by spnav_remove_events() */
SPNAV_EVENT_MOTION,
SPNAV_EVENT_BUTTON /* includes both press and release */
};
struct spnav_event_motion {
int type;
int x, y, z;
int rx, ry, rz;
unsigned int period;
int *data;
};
struct spnav_event_button {
int type;
int press;
int bnum;
};
typedef union spnav_event {
int type;
struct spnav_event_motion motion;
struct spnav_event_button button;
} spnav_event;
#ifdef __cplusplus
extern "C" {
#endif
/* Open connection to the daemon via AF_UNIX socket.
* The unix domain socket interface is an alternative to the original magellan
* protocol, and it is *NOT* compatible with the 3D connexion driver. If you wish
* to remain compatible, use the X11 protocol (spnav_x11_open, see below).
* Returns -1 on failure.
*/
typedef int (*spnav_open_ptr)(void);
/* Close connection to the daemon. Use it for X11 or AF_UNIX connections.
* Returns -1 on failure
*/
typedef int (*spnav_close_ptr)(void);
/* Retrieves the file descriptor used for communication with the daemon, for
* use with select() by the application, if so required.
* If the X11 mode is used, the socket used to communicate with the X server is
* returned, so the result of this function is always reliable.
* If AF_UNIX mode is used, the fd of the socket is returned or -1 if
* no connection is open / failure occured.
*/
//int spnav_fd(void);
/* TODO: document */
//int spnav_sensitivity(double sens);
/* blocks waiting for space-nav events. returns 0 if an error occurs */
//int spnav_wait_event(spnav_event *event);
/* checks the availability of space-nav events (non-blocking)
* returns the event type if available, or 0 otherwise.
*/
typedef int (*spnav_poll_event_ptr)(spnav_event *event);
/* Removes any pending events from the specified type, or all pending events
* events if the type argument is SPNAV_EVENT_ANY. Returns the number of
* removed events.
*/
//int spnav_remove_events(int type);
#ifdef __cplusplus
}
#endif
#endif /* MINI_SPACENAV_H_ */
......@@ -7,6 +7,7 @@
#ifndef ACGL_SCENE_CAMERA_HH
#define ACGL_SCENE_CAMERA_HH
#ifdef ACGL_INCLUDE_DEPRECATED_FUNCTIONALITY
/*
* A generic camera class.
*/
......@@ -201,4 +202,6 @@ private:
} // Scene
} // ACGL
#endif
#endif // ACGL_SCENE_CAMERA_HH
This diff is collapsed.
/***********************************************************************
* Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#include <ACGL/HardwareSupport/SpaceNavControl.hh>
#include <ACGL/HardwareSupport/SpaceNavPollInterface.hh>
using namespace ACGL;
using namespace ACGL::HardwareSupport;
SpaceNavControl::SpaceNavControl(Scene::GenericCamera *_cam, const glm::vec3 &_moveSensitivity, const glm::vec3 &_rotateSensitivity)
: mCamera(_cam),
mMoveSensitivity(_moveSensitivity),
mRotateSensitivity(_rotateSensitivity)
{
assert(mCamera);
}
void SpaceNavControl::update(float _elapsedSeconds)
{
static bool firstRun = true;
if (firstRun) {
SpaceNavRegisterListener();
firstRun = false;
}
SpaceNavEvent event;
while ( SpaceNavPollEvent(event) > 0 )
{
if (event.type == movement)
{
//std::cout << "Motion event: " << std::endl;
//std::cout << " pos: " << event.x << " " << event.y << " " << event.z << std::endl;
//std::cout << " rot: " << event.rx << " " << event.ry << " " << event.rz << std::endl;
glm::vec3 moveSpeed = (1.0f/256.0f) * _elapsedSeconds * glm::vec3(event.x, event.y, event.z) * mMoveSensitivity;
mCamera->move(moveSpeed);
float rotSpeed = (1.0f/1024.0f)* _elapsedSeconds;
glm::mat3 ypr = glm::mat3(glm::yawPitchRoll(
event.ry * rotSpeed * mRotateSensitivity.y,
-event.rx * rotSpeed * mRotateSensitivity.x,
event.rz * rotSpeed * mRotateSensitivity.z));
mCamera->setRotationMatrix(ypr * mCamera->getRotationMatrix3());
} else if (event.type == button) {
//std::cout << "button ";
//if (!event.pressed) std::cout << "un";
//std::cout << "pressed: " << event.button << std::endl;
} else {
//std::cout << "unknown event " << std::endl;
}
}
}
SpaceNavControl::~SpaceNavControl()
{
SpaceNavUnregisterListener();
}
/***********************************************************************
* Copyright 2011-2013 Computer Graphics Group RWTH Aachen University. *
* All rights reserved. *
* Distributed under the terms of the MIT License (see LICENSE.TXT). *
**********************************************************************/
#include <ACGL/HardwareSupport/SpaceNavPollInterface.hh>
#include <iostream>
#include <string>
int g_SpaceNavListenerCount = 0;
using namespace std;
namespace ACGL{
namespace HardwareSupport{
//
// define NO_SPACE_NAVIGATOR_SUPPORT to only build dummy functions for the space nav support
// not needed on linux as the linux library gets loaded at runtime
//
#if !defined(NO_SPACE_NAVIGATOR_SUPPORT)
#if defined(__gnu_linux__)
#include <ACGL/HardwareSupport/mini_spnav.h>
// the functions we need to load:
spnav_open_ptr spnav_open = NULL;
spnav_close_ptr spnav_close = NULL;
spnav_poll_event_ptr spnav_poll_event = NULL;
// pointer to the library:
void *libspnav = NULL;
#define SPACENAV_SPNAV_LINUX
#elif defined(__APPLE__) && defined (__MACH__) // MacOS X with official drivers
#include <stack>
#include <ConnexionClientAPI.h>
#define SPACENAV_CONNEXION_MAC
extern const char *__progname;
UInt16 g_SpaceNavClientID;
#endif
#endif // dummy
#ifdef SPACENAV_SPNAV_LINUX
bool SpaceNavConnectToDriverLinux();
void SpaceNavDisconnectFromDriverLinux();
unsigned int SpaceNavPollEventLinux( SpaceNavEvent &eventToFill );
#elif defined(SPACENAV_CONNEXION_MAC)
bool SpaceNavConnectToDriverMac();
void SpaceNavDisconnectFromDriverMac();
unsigned int SpaceNavPollEventMac( SpaceNavEvent &eventToFill );
#endif
bool SpaceNavRegisterListener()
{
if ( g_SpaceNavListenerCount == 0 ) {
// init & connect to the driver:
bool success = false;
#ifdef SPACENAV_SPNAV_LINUX
success = SpaceNavConnectToDriverLinux();
#elif defined(SPACENAV_CONNEXION_MAC)
success = SpaceNavConnectToDriverMac();
#else
// unsupported OS: quietly(!) return false
return false;
#endif
if (!success)
{
ACGL::Utils::warning() << "Unable to connect to space navigator!" << std::endl;
return false; // don't increase the listener count!
}
else
{
ACGL::Utils::debug() << "Successfully connected to space navigator!" << std::endl;
}
}
g_SpaceNavListenerCount++;
return true;
}
void SpaceNavUnregisterListener()
{
if ( g_SpaceNavListenerCount == 0 ) {
// can't unregister if there are no listeners!
return;
}
g_SpaceNavListenerCount--;
if ( g_SpaceNavListenerCount == 0 ) {
// if there are now 0 listeners disconnect from the driver
#ifdef SPACENAV_SPNAV_LINUX
SpaceNavDisconnectFromDriverLinux();
#elif defined(SPACENAV_CONNEXION_MAC)
SpaceNavDisconnectFromDriverMac();
#endif
}
}
unsigned int SpaceNavPollEvent( SpaceNavEvent &eventToFill )
{
if ( g_SpaceNavListenerCount == 0 ) {
// In this case there was no (successful) connection to the driver!
// e.g. no drivers or no device is installed!
return 0;
}
#ifdef SPACENAV_SPNAV_LINUX
return SpaceNavPollEventLinux( eventToFill );
#elif defined(SPACENAV_CONNEXION_MAC)
return SpaceNavPollEventMac( eventToFill );
#else
// unsupported OS:
return 0;
#endif
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
#ifdef SPACENAV_SPNAV_LINUX
bool SpaceNavConnectToDriverLinux()
{
// try to open the library:
libspnav = dlopen("libspnav.so", RTLD_NOW);
if (libspnav == NULL) {
std::cerr << "can't locate libspnav.so" << std::endl;
return false;
}
// get the needed function pointers:
spnav_open = (spnav_open_ptr) dlsym(libspnav, "spnav_open");
spnav_close = (spnav_close_ptr) dlsym(libspnav, "spnav_close");
spnav_poll_event = (spnav_poll_event_ptr) dlsym(libspnav, "spnav_poll_event");
if (!spnav_open || !spnav_close || !spnav_poll_event) {
std::cerr << "could not load function pointers from libspnav.so" << std::endl;
return false;
}
int status = spnav_open();
return ( status != -1 );
}
void SpaceNavDisconnectFromDriverLinux()
{
spnav_close();
if (libspnav) dlclose( libspnav );
}
unsigned int SpaceNavPollEventLinux( SpaceNavEvent &eventToFill )
{
spnav_event event;
unsigned int eventsWaiting = spnav_poll_event(&event);
if ( eventsWaiting > 0 )
{
if (event.type == SPNAV_EVENT_MOTION )
{
eventToFill.type = movement;
eventToFill.x = event.motion.x;
eventToFill.y = event.motion.y;
eventToFill.z = -event.motion.z;
eventToFill.rx = event.motion.rx;
eventToFill.ry = -event.motion.ry;
eventToFill.rz = event.motion.rz;
} else if (event.type == SPNAV_EVENT_BUTTON ) {
eventToFill.type = button;
eventToFill.button = event.button.bnum;
eventToFill.pressed = event.button.press;
} else {
eventToFill.type = other;
}
//spnav_remove_events(SPNAV_EVENT_MOTION);
return eventsWaiting;
}
return 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////
#elif defined(SPACENAV_CONNEXION_MAC)
std::stack< SpaceNavEvent > g_SpaceNavEventList;
void SpaceNavCallbackMac( io_connect_t connection, natural_t messageType, void *messageArgument )
{
//cout << "callback" << endl;
static uint32_t oldButtonState = 0;
ConnexionDeviceState *state;
switch (messageType) {
case kConnexionMsgDeviceState:
state = (ConnexionDeviceState*)messageArgument;
if (state->client == g_SpaceNavClientID)
{
SpaceNavEvent newEvent;
// decipher what command/event is being reported by the driver
switch (state->command) {
case kConnexionCmdHandleAxis:{
newEvent.type = movement;
// scale down all values to approximately match the linux values
newEvent.x = state->axis[0] /= 2;
newEvent.y = state->axis[1] /= -2;
newEvent.z = state->axis[2] /= -2;
newEvent.rx = state->axis[3] /= 2;
newEvent.ry = state->axis[4] /= 2;
newEvent.rz = state->axis[5] /= 2;
g_SpaceNavEventList.push( newEvent );
}
break;
case kConnexionCmdHandleButtons: {
newEvent.type = button;
uint32_t newButtonState = state->buttons;
uint32_t buttonDiff = oldButtonState ^ newButtonState;
// on MacOS the buttons are set as a bitpattern representing
// the current state. Strangely only the first 7 buttons
// seem to work and it fails if many buttons are pressed at once!
for (int i = 0; i < 32; ++i) {
uint32_t mask = 1<<i;
if (buttonDiff & mask) {
bool pressed = (newButtonState & mask);
newEvent.pressed = pressed;
newEvent.button = i;
g_SpaceNavEventList.push( newEvent );
}
}
//cout << "button: " << state->buttons << " " << buttonDiff << endl;
oldButtonState = newButtonState;
break;
}
default:
newEvent.type = other;
g_SpaceNavEventList.push( newEvent );
}
}
break;
default:
break;
}
}
bool SpaceNavConnectToDriverMac()
{
// get a pascal string of the app name:
std::string progname = std::string( __progname );
unsigned char *appname = new unsigned char[ progname.length() + 1 ];
int len = progname.length();
if (len > 255) {
std::cerr << "app " << progname << " might be too long to support the space navigator" << std::endl;
len = 255;
}
appname[0] = len;
for (int i = 0; i < len; ++i) {
appname[i+1] = progname[i];
}
OSErr result = InstallConnexionHandlers( SpaceNavCallbackMac, 0L, 0L);
if (result != noErr) {
return false;
}
g_SpaceNavClientID = RegisterConnexionClient( kConnexionClientWildcard, appname, kConnexionClientModeTakeOver, kConnexionMaskAll);
delete[] appname;
return true;
}
void SpaceNavDisconnectFromDriverMac()
{
UnregisterConnexionClient( g_SpaceNavClientID );
CleanupConnexionHandlers();
}
unsigned int SpaceNavPollEventMac( SpaceNavEvent &eventToFill )
{
if (g_SpaceNavEventList.size() == 0) {
return 0;
}
eventToFill = g_SpaceNavEventList.top();
g_SpaceNavEventList.pop();
// return the number of waiting events including the one just returned!
return (g_SpaceNavEventList.size()+1);
}
#endif
} // HardwareSupport
} // ACGL
......@@ -6,6 +6,8 @@
#include <ACGL/Scene/Camera.hh>
#ifdef ACGL_INCLUDE_DEPRECATED_FUNCTIONALITY
using namespace ACGL;
using namespace ACGL::Scene;
......@@ -98,3 +100,5 @@ void Camera::updateOrientationByLocalCoordinateSystem(void)
mPitch = Math::Functions::atan2Deg( mForward.y, mUp.y);
mRoll = Math::Functions::asinDeg( mRight.y);
}
#endif
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment