mirror of
https://github.com/overte-org/overte.git
synced 2025-08-09 22:28:37 +02:00
Merge pull request #3505 from birarda/hydra-crash
dynamically load sixense lib on OS X to make avoiding hid_init crash easier
This commit is contained in:
commit
3b0214358b
8 changed files with 280 additions and 162 deletions
|
@ -137,8 +137,9 @@ foreach(EXTERNAL ${OPTIONAL_EXTERNALS})
|
||||||
set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
|
set(${${EXTERNAL}_UPPERCASE}_LIBRARIES ${${${EXTERNAL}_UPPERCASE}_LIBRARY})
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
|
if (NOT APPLE OR NOT ${${EXTERNAL}_UPPERCASE} MATCHES "SIXENSE")
|
||||||
|
target_link_libraries(${TARGET_NAME} ${${${EXTERNAL}_UPPERCASE}_LIBRARIES})
|
||||||
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
|
|
|
@ -182,7 +182,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_lastNackTime(usecTimestampNow()),
|
_lastNackTime(usecTimestampNow()),
|
||||||
_lastSendDownstreamAudioStats(usecTimestampNow())
|
_lastSendDownstreamAudioStats(usecTimestampNow())
|
||||||
{
|
{
|
||||||
|
|
||||||
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
// read the ApplicationInfo.ini file for Name/Version/Domain information
|
||||||
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
QSettings applicationInfo(Application::resourcesPath() + "info/ApplicationInfo.ini", QSettings::IniFormat);
|
||||||
|
|
||||||
|
@ -378,12 +377,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
||||||
_particleEditSender.setPacketsPerSecond(3000); // super high!!
|
_particleEditSender.setPacketsPerSecond(3000); // super high!!
|
||||||
_entityEditSender.setPacketsPerSecond(3000); // super high!!
|
_entityEditSender.setPacketsPerSecond(3000); // super high!!
|
||||||
|
|
||||||
// Set the sixense filtering
|
|
||||||
_sixenseManager.setFilter(Menu::getInstance()->isOptionChecked(MenuOption::FilterSixense));
|
|
||||||
|
|
||||||
// Set hand controller velocity filtering
|
|
||||||
_sixenseManager.setLowVelocityFilter(Menu::getInstance()->isOptionChecked(MenuOption::LowVelocityFilter));
|
|
||||||
|
|
||||||
checkVersion();
|
checkVersion();
|
||||||
|
|
||||||
_overlays.init(_glWidget); // do this before scripts load
|
_overlays.init(_glWidget); // do this before scripts load
|
||||||
|
@ -1481,7 +1474,7 @@ void Application::setRenderVoxels(bool voxelRender) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
void Application::setLowVelocityFilter(bool lowVelocityFilter) {
|
||||||
getSixenseManager()->setLowVelocityFilter(lowVelocityFilter);
|
SixenseManager::getInstance().setLowVelocityFilter(lowVelocityFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::doKillLocalVoxels() {
|
void Application::doKillLocalVoxels() {
|
||||||
|
@ -1802,6 +1795,17 @@ void Application::init() {
|
||||||
if (urlIndex != -1) {
|
if (urlIndex != -1) {
|
||||||
AddressManager::getInstance().handleLookupString(arguments().value(urlIndex + 1));
|
AddressManager::getInstance().handleLookupString(arguments().value(urlIndex + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseEnabled)) {
|
||||||
|
// on OS X we only setup sixense if the user wants it on - this allows running without the hid_init crash
|
||||||
|
// if hydra support is temporarily not required
|
||||||
|
Menu::getInstance()->toggleSixense(true);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
// setup sixense
|
||||||
|
Menu::getInstance()->toggleSixense(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
// initialize our face trackers after loading the menu settings
|
// initialize our face trackers after loading the menu settings
|
||||||
_faceshift.init();
|
_faceshift.init();
|
||||||
|
@ -2178,7 +2182,7 @@ void Application::update(float deltaTime) {
|
||||||
DeviceTracker::updateAll();
|
DeviceTracker::updateAll();
|
||||||
updateFaceshift();
|
updateFaceshift();
|
||||||
updateVisage();
|
updateVisage();
|
||||||
_sixenseManager.update(deltaTime);
|
SixenseManager::getInstance().update(deltaTime);
|
||||||
JoystickScriptingInterface::getInstance().update();
|
JoystickScriptingInterface::getInstance().update();
|
||||||
_prioVR.update(deltaTime);
|
_prioVR.update(deltaTime);
|
||||||
|
|
||||||
|
|
|
@ -219,7 +219,6 @@ public:
|
||||||
DdeFaceTracker* getDDE() { return &_dde; }
|
DdeFaceTracker* getDDE() { return &_dde; }
|
||||||
CaraFaceTracker* getCara() { return &_cara; }
|
CaraFaceTracker* getCara() { return &_cara; }
|
||||||
FaceTracker* getActiveFaceTracker();
|
FaceTracker* getActiveFaceTracker();
|
||||||
SixenseManager* getSixenseManager() { return &_sixenseManager; }
|
|
||||||
PrioVR* getPrioVR() { return &_prioVR; }
|
PrioVR* getPrioVR() { return &_prioVR; }
|
||||||
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
BandwidthMeter* getBandwidthMeter() { return &_bandwidthMeter; }
|
||||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||||
|
@ -510,7 +509,6 @@ private:
|
||||||
CaraFaceTracker _cara;
|
CaraFaceTracker _cara;
|
||||||
DdeFaceTracker _dde;
|
DdeFaceTracker _dde;
|
||||||
|
|
||||||
SixenseManager _sixenseManager;
|
|
||||||
PrioVR _prioVR;
|
PrioVR _prioVR;
|
||||||
|
|
||||||
Camera _myCamera; // My view onto the world
|
Camera _myCamera; // My view onto the world
|
||||||
|
|
|
@ -431,11 +431,18 @@ Menu::Menu() :
|
||||||
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
addCheckableActionToQMenuAndActionHash(handOptionsMenu, MenuOption::ShowIKConstraints, 0, false);
|
||||||
|
|
||||||
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
QMenu* sixenseOptionsMenu = handOptionsMenu->addMenu("Sixense");
|
||||||
|
#ifdef __APPLE__
|
||||||
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||||
|
MenuOption::SixenseEnabled,
|
||||||
|
0, true,
|
||||||
|
this,
|
||||||
|
SLOT(toggleSixense(bool)));
|
||||||
|
#endif
|
||||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||||
MenuOption::FilterSixense,
|
MenuOption::FilterSixense,
|
||||||
0,
|
0,
|
||||||
true,
|
true,
|
||||||
appInstance->getSixenseManager(),
|
&SixenseManager::getInstance(),
|
||||||
SLOT(setFilter(bool)));
|
SLOT(setFilter(bool)));
|
||||||
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
addCheckableActionToQMenuAndActionHash(sixenseOptionsMenu,
|
||||||
MenuOption::LowVelocityFilter,
|
MenuOption::LowVelocityFilter,
|
||||||
|
@ -1134,6 +1141,18 @@ void Menu::editAnimations() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Menu::toggleSixense(bool shouldEnable) {
|
||||||
|
SixenseManager& sixenseManager = SixenseManager::getInstance();
|
||||||
|
|
||||||
|
if (shouldEnable && !sixenseManager.isInitialized()) {
|
||||||
|
sixenseManager.initialize();
|
||||||
|
sixenseManager.setFilter(isOptionChecked(MenuOption::FilterSixense));
|
||||||
|
sixenseManager.setLowVelocityFilter(isOptionChecked(MenuOption::LowVelocityFilter));
|
||||||
|
}
|
||||||
|
|
||||||
|
sixenseManager.setIsEnabled(shouldEnable);
|
||||||
|
}
|
||||||
|
|
||||||
void Menu::changePrivateKey() {
|
void Menu::changePrivateKey() {
|
||||||
// setup the dialog
|
// setup the dialog
|
||||||
QInputDialog privateKeyDialog(Application::getInstance()->getWindow());
|
QInputDialog privateKeyDialog(Application::getInstance()->getWindow());
|
||||||
|
|
|
@ -186,6 +186,7 @@ public slots:
|
||||||
void pasteToVoxel();
|
void pasteToVoxel();
|
||||||
|
|
||||||
void toggleLoginMenuItem();
|
void toggleLoginMenuItem();
|
||||||
|
void toggleSixense(bool shouldEnable);
|
||||||
|
|
||||||
QMenu* addMenu(const QString& menuName);
|
QMenu* addMenu(const QString& menuName);
|
||||||
void removeMenu(const QString& menuName);
|
void removeMenu(const QString& menuName);
|
||||||
|
@ -448,6 +449,7 @@ namespace MenuOption {
|
||||||
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
const QString ShowBordersVoxelNodes = "Show Voxel Nodes";
|
||||||
const QString ShowIKConstraints = "Show IK Constraints";
|
const QString ShowIKConstraints = "Show IK Constraints";
|
||||||
const QString SimpleShadows = "Simple";
|
const QString SimpleShadows = "Simple";
|
||||||
|
const QString SixenseEnabled = "Enable Hydra Support";
|
||||||
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
const QString SixenseMouseInput = "Enable Sixense Mouse Input";
|
||||||
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
const QString SixenseLasers = "Enable Sixense UI Lasers";
|
||||||
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
const QString StandOnNearbyFloors = "Stand on nearby floors";
|
||||||
|
|
|
@ -30,23 +30,28 @@ const int CALIBRATION_STATE_COMPLETE = 4;
|
||||||
const float NECK_X = 0.25f; // meters
|
const float NECK_X = 0.25f; // meters
|
||||||
const float NECK_Y = 0.3f; // meters
|
const float NECK_Y = 0.3f; // meters
|
||||||
const float NECK_Z = 0.3f; // meters
|
const float NECK_Z = 0.3f; // meters
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
typedef int (*SixenseBaseFunction)();
|
||||||
|
typedef int (*SixenseTakeIntFunction)(int);
|
||||||
|
typedef int (*SixenseTakeIntAndSixenseControllerData)(int, sixenseControllerData*);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
SixenseManager::SixenseManager() {
|
|
||||||
#ifdef HAVE_SIXENSE
|
|
||||||
_lastMovement = 0;
|
|
||||||
_amountMoved = glm::vec3(0.0f);
|
|
||||||
_lowVelocityFilter = false;
|
|
||||||
|
|
||||||
_calibrationState = CALIBRATION_STATE_IDLE;
|
|
||||||
// By default we assume the _neckBase (in orb frame) is as high above the orb
|
|
||||||
// as the "torso" is below it.
|
|
||||||
_neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
|
|
||||||
|
|
||||||
sixenseInit();
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
_hydrasConnected = false;
|
|
||||||
|
SixenseManager& SixenseManager::getInstance() {
|
||||||
|
static SixenseManager sharedInstance;
|
||||||
|
return sharedInstance;
|
||||||
|
}
|
||||||
|
|
||||||
|
SixenseManager::SixenseManager() :
|
||||||
|
#ifdef __APPLE__
|
||||||
|
_sixenseLibrary(NULL),
|
||||||
|
#endif
|
||||||
|
_isInitialized(false),
|
||||||
|
_isEnabled(true),
|
||||||
|
_hydrasConnected(false)
|
||||||
|
{
|
||||||
_triggerPressed[0] = false;
|
_triggerPressed[0] = false;
|
||||||
_bumperPressed[0] = false;
|
_bumperPressed[0] = false;
|
||||||
_oldX[0] = -1;
|
_oldX[0] = -1;
|
||||||
|
@ -58,155 +63,227 @@ SixenseManager::SixenseManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SixenseManager::~SixenseManager() {
|
SixenseManager::~SixenseManager() {
|
||||||
|
#ifdef HAVE_SIXENSE_
|
||||||
|
|
||||||
|
if (_isInitialized) {
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseBaseFunction sixenseExit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseExit");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sixenseExit();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
delete _sixenseLibrary;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void SixenseManager::initialize() {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
sixenseExit();
|
|
||||||
|
if (!_isInitialized) {
|
||||||
|
_lastMovement = 0;
|
||||||
|
_amountMoved = glm::vec3(0.0f);
|
||||||
|
_lowVelocityFilter = false;
|
||||||
|
|
||||||
|
_calibrationState = CALIBRATION_STATE_IDLE;
|
||||||
|
// By default we assume the _neckBase (in orb frame) is as high above the orb
|
||||||
|
// as the "torso" is below it.
|
||||||
|
_neckBase = glm::vec3(NECK_X, -NECK_Y, NECK_Z);
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
|
||||||
|
if (!_sixenseLibrary) {
|
||||||
|
const QString SIXENSE_LIBRARY_NAME = "libsixense_x64.dylib";
|
||||||
|
_sixenseLibrary = new QLibrary(SIXENSE_LIBRARY_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
SixenseBaseFunction sixenseInit = (SixenseBaseFunction) _sixenseLibrary->resolve("sixenseInit");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
sixenseInit();
|
||||||
|
|
||||||
|
_isInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SixenseManager::setFilter(bool filter) {
|
void SixenseManager::setFilter(bool filter) {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
if (filter) {
|
|
||||||
sixenseSetFilterEnabled(1);
|
if (_isInitialized) {
|
||||||
} else {
|
#ifdef __APPLE__
|
||||||
sixenseSetFilterEnabled(0);
|
SixenseTakeIntFunction sixenseSetFilterEnabled = (SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseSetFilterEnabled");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (filter) {
|
||||||
|
sixenseSetFilterEnabled(1);
|
||||||
|
} else {
|
||||||
|
sixenseSetFilterEnabled(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SixenseManager::update(float deltaTime) {
|
void SixenseManager::update(float deltaTime) {
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
// if the controllers haven't been moved in a while, disable
|
if (_isInitialized && _isEnabled) {
|
||||||
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
// if the controllers haven't been moved in a while, disable
|
||||||
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
const unsigned int MOVEMENT_DISABLE_SECONDS = 3;
|
||||||
Hand* hand = Application::getInstance()->getAvatar()->getHand();
|
if (usecTimestampNow() - _lastMovement > (MOVEMENT_DISABLE_SECONDS * USECS_PER_SECOND)) {
|
||||||
for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
|
Hand* hand = Application::getInstance()->getAvatar()->getHand();
|
||||||
it->setActive(false);
|
for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
|
||||||
}
|
it->setActive(false);
|
||||||
_lastMovement = usecTimestampNow();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sixenseGetNumActiveControllers() == 0) {
|
|
||||||
_hydrasConnected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PerformanceTimer perfTimer("sixense");
|
|
||||||
if (!_hydrasConnected) {
|
|
||||||
_hydrasConnected = true;
|
|
||||||
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
|
|
||||||
}
|
|
||||||
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
|
||||||
Hand* hand = avatar->getHand();
|
|
||||||
|
|
||||||
int maxControllers = sixenseGetMaxControllers();
|
|
||||||
|
|
||||||
// we only support two controllers
|
|
||||||
sixenseControllerData controllers[2];
|
|
||||||
|
|
||||||
int numActiveControllers = 0;
|
|
||||||
for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) {
|
|
||||||
if (!sixenseIsControllerEnabled(i)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
sixenseControllerData* data = controllers + numActiveControllers;
|
|
||||||
++numActiveControllers;
|
|
||||||
sixenseGetNewestData(i, data);
|
|
||||||
|
|
||||||
// Set palm position and normal based on Hydra position/orientation
|
|
||||||
|
|
||||||
// Either find a palm matching the sixense controller, or make a new one
|
|
||||||
PalmData* palm;
|
|
||||||
bool foundHand = false;
|
|
||||||
for (size_t j = 0; j < hand->getNumPalms(); j++) {
|
|
||||||
if (hand->getPalms()[j].getSixenseID() == data->controller_index) {
|
|
||||||
palm = &(hand->getPalms()[j]);
|
|
||||||
foundHand = true;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!foundHand) {
|
|
||||||
PalmData newPalm(hand);
|
|
||||||
hand->getPalms().push_back(newPalm);
|
|
||||||
palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
|
|
||||||
palm->setSixenseID(data->controller_index);
|
|
||||||
qDebug("Found new Sixense controller, ID %i", data->controller_index);
|
|
||||||
}
|
|
||||||
|
|
||||||
palm->setActive(true);
|
|
||||||
|
|
||||||
// Read controller buttons and joystick into the hand
|
|
||||||
palm->setControllerButtons(data->buttons);
|
|
||||||
palm->setTrigger(data->trigger);
|
|
||||||
palm->setJoystick(data->joystick_x, data->joystick_y);
|
|
||||||
|
|
||||||
|
|
||||||
// Emulate the mouse so we can use scripts
|
|
||||||
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
|
|
||||||
emulateMouse(palm, numActiveControllers - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
|
||||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
|
||||||
position *= METERS_PER_MILLIMETER;
|
|
||||||
|
|
||||||
// Transform the measured position into body frame.
|
|
||||||
glm::vec3 neck = _neckBase;
|
|
||||||
// Zeroing y component of the "neck" effectively raises the measured position a little bit.
|
|
||||||
neck.y = 0.f;
|
|
||||||
position = _orbRotation * (position - neck);
|
|
||||||
|
|
||||||
// Rotation of Palm
|
|
||||||
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
|
||||||
rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation;
|
|
||||||
|
|
||||||
// Compute current velocity from position change
|
|
||||||
glm::vec3 rawVelocity;
|
|
||||||
if (deltaTime > 0.f) {
|
|
||||||
rawVelocity = (position - palm->getRawPosition()) / deltaTime;
|
|
||||||
} else {
|
|
||||||
rawVelocity = glm::vec3(0.0f);
|
|
||||||
}
|
|
||||||
palm->setRawVelocity(rawVelocity); // meters/sec
|
|
||||||
|
|
||||||
// adjustment for hydra controllers fit into hands
|
|
||||||
float sign = (i == 0) ? -1.0f : 1.0f;
|
|
||||||
rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
|
||||||
|
|
||||||
if (_lowVelocityFilter) {
|
|
||||||
// Use a velocity sensitive filter to damp small motions and preserve large ones with
|
|
||||||
// no latency.
|
|
||||||
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
|
|
||||||
position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
|
|
||||||
rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter);
|
|
||||||
palm->setRawPosition(position);
|
|
||||||
palm->setRawRotation(rotation);
|
|
||||||
} else {
|
|
||||||
palm->setRawPosition(position);
|
|
||||||
palm->setRawRotation(rotation);
|
|
||||||
}
|
|
||||||
|
|
||||||
// use the velocity to determine whether there's any movement (if the hand isn't new)
|
|
||||||
const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f;
|
|
||||||
_amountMoved += rawVelocity * deltaTime;
|
|
||||||
if (glm::length(_amountMoved) > MOVEMENT_DISTANCE_THRESHOLD && foundHand) {
|
|
||||||
_lastMovement = usecTimestampNow();
|
_lastMovement = usecTimestampNow();
|
||||||
_amountMoved = glm::vec3(0.0f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Store the one fingertip in the palm structure so we can track velocity
|
#ifdef __APPLE__
|
||||||
const float FINGER_LENGTH = 0.3f; // meters
|
SixenseBaseFunction sixenseGetNumActiveControllers =
|
||||||
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
|
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetNumActiveControllers");
|
||||||
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
|
#endif
|
||||||
glm::vec3 oldTipPosition = palm->getTipRawPosition();
|
|
||||||
if (deltaTime > 0.f) {
|
if (sixenseGetNumActiveControllers() == 0) {
|
||||||
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
|
_hydrasConnected = false;
|
||||||
} else {
|
return;
|
||||||
palm->setTipVelocity(glm::vec3(0.f));
|
}
|
||||||
|
|
||||||
|
PerformanceTimer perfTimer("sixense");
|
||||||
|
if (!_hydrasConnected) {
|
||||||
|
_hydrasConnected = true;
|
||||||
|
UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra");
|
||||||
|
}
|
||||||
|
MyAvatar* avatar = Application::getInstance()->getAvatar();
|
||||||
|
Hand* hand = avatar->getHand();
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseBaseFunction sixenseGetMaxControllers =
|
||||||
|
(SixenseBaseFunction) _sixenseLibrary->resolve("sixenseGetMaxControllers");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int maxControllers = sixenseGetMaxControllers();
|
||||||
|
|
||||||
|
// we only support two controllers
|
||||||
|
sixenseControllerData controllers[2];
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
SixenseTakeIntFunction sixenseIsControllerEnabled =
|
||||||
|
(SixenseTakeIntFunction) _sixenseLibrary->resolve("sixenseIsControllerEnabled");
|
||||||
|
|
||||||
|
SixenseTakeIntAndSixenseControllerData sixenseGetNewestData =
|
||||||
|
(SixenseTakeIntAndSixenseControllerData) _sixenseLibrary->resolve("sixenseGetNewestData");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int numActiveControllers = 0;
|
||||||
|
for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) {
|
||||||
|
if (!sixenseIsControllerEnabled(i)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
sixenseControllerData* data = controllers + numActiveControllers;
|
||||||
|
++numActiveControllers;
|
||||||
|
sixenseGetNewestData(i, data);
|
||||||
|
|
||||||
|
// Set palm position and normal based on Hydra position/orientation
|
||||||
|
|
||||||
|
// Either find a palm matching the sixense controller, or make a new one
|
||||||
|
PalmData* palm;
|
||||||
|
bool foundHand = false;
|
||||||
|
for (size_t j = 0; j < hand->getNumPalms(); j++) {
|
||||||
|
if (hand->getPalms()[j].getSixenseID() == data->controller_index) {
|
||||||
|
palm = &(hand->getPalms()[j]);
|
||||||
|
foundHand = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!foundHand) {
|
||||||
|
PalmData newPalm(hand);
|
||||||
|
hand->getPalms().push_back(newPalm);
|
||||||
|
palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
|
||||||
|
palm->setSixenseID(data->controller_index);
|
||||||
|
qDebug("Found new Sixense controller, ID %i", data->controller_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
palm->setActive(true);
|
||||||
|
|
||||||
|
// Read controller buttons and joystick into the hand
|
||||||
|
palm->setControllerButtons(data->buttons);
|
||||||
|
palm->setTrigger(data->trigger);
|
||||||
|
palm->setJoystick(data->joystick_x, data->joystick_y);
|
||||||
|
|
||||||
|
|
||||||
|
// Emulate the mouse so we can use scripts
|
||||||
|
if (Menu::getInstance()->isOptionChecked(MenuOption::SixenseMouseInput)) {
|
||||||
|
emulateMouse(palm, numActiveControllers - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Sixense API returns pos data in millimeters but we IMMEDIATELY convert to meters.
|
||||||
|
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||||
|
position *= METERS_PER_MILLIMETER;
|
||||||
|
|
||||||
|
// Transform the measured position into body frame.
|
||||||
|
glm::vec3 neck = _neckBase;
|
||||||
|
// Zeroing y component of the "neck" effectively raises the measured position a little bit.
|
||||||
|
neck.y = 0.f;
|
||||||
|
position = _orbRotation * (position - neck);
|
||||||
|
|
||||||
|
// Rotation of Palm
|
||||||
|
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
|
||||||
|
rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation;
|
||||||
|
|
||||||
|
// Compute current velocity from position change
|
||||||
|
glm::vec3 rawVelocity;
|
||||||
|
if (deltaTime > 0.f) {
|
||||||
|
rawVelocity = (position - palm->getRawPosition()) / deltaTime;
|
||||||
|
} else {
|
||||||
|
rawVelocity = glm::vec3(0.0f);
|
||||||
|
}
|
||||||
|
palm->setRawVelocity(rawVelocity); // meters/sec
|
||||||
|
|
||||||
|
// adjustment for hydra controllers fit into hands
|
||||||
|
float sign = (i == 0) ? -1.0f : 1.0f;
|
||||||
|
rotation *= glm::angleAxis(sign * PI/4.0f, glm::vec3(0.0f, 0.0f, 1.0f));
|
||||||
|
|
||||||
|
if (_lowVelocityFilter) {
|
||||||
|
// Use a velocity sensitive filter to damp small motions and preserve large ones with
|
||||||
|
// no latency.
|
||||||
|
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
|
||||||
|
position = palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter);
|
||||||
|
rotation = safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter);
|
||||||
|
palm->setRawPosition(position);
|
||||||
|
palm->setRawRotation(rotation);
|
||||||
|
} else {
|
||||||
|
palm->setRawPosition(position);
|
||||||
|
palm->setRawRotation(rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the velocity to determine whether there's any movement (if the hand isn't new)
|
||||||
|
const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f;
|
||||||
|
_amountMoved += rawVelocity * deltaTime;
|
||||||
|
if (glm::length(_amountMoved) > MOVEMENT_DISTANCE_THRESHOLD && foundHand) {
|
||||||
|
_lastMovement = usecTimestampNow();
|
||||||
|
_amountMoved = glm::vec3(0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the one fingertip in the palm structure so we can track velocity
|
||||||
|
const float FINGER_LENGTH = 0.3f; // meters
|
||||||
|
const glm::vec3 FINGER_VECTOR(0.0f, 0.0f, FINGER_LENGTH);
|
||||||
|
const glm::vec3 newTipPosition = position + rotation * FINGER_VECTOR;
|
||||||
|
glm::vec3 oldTipPosition = palm->getTipRawPosition();
|
||||||
|
if (deltaTime > 0.f) {
|
||||||
|
palm->setTipVelocity((newTipPosition - oldTipPosition) / deltaTime);
|
||||||
|
} else {
|
||||||
|
palm->setTipVelocity(glm::vec3(0.f));
|
||||||
|
}
|
||||||
|
palm->setTipPosition(newTipPosition);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numActiveControllers == 2) {
|
||||||
|
updateCalibration(controllers);
|
||||||
}
|
}
|
||||||
palm->setTipPosition(newTipPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numActiveControllers == 2) {
|
|
||||||
updateCalibration(controllers);
|
|
||||||
}
|
}
|
||||||
#endif // HAVE_SIXENSE
|
#endif // HAVE_SIXENSE
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <glm/gtc/quaternion.hpp>
|
#include <glm/gtc/quaternion.hpp>
|
||||||
#include "sixense.h"
|
#include "sixense.h"
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
#include <qlibrary.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
|
||||||
|
@ -38,9 +43,12 @@ const bool DEFAULT_INVERT_SIXENSE_MOUSE_BUTTONS = false;
|
||||||
class SixenseManager : public QObject {
|
class SixenseManager : public QObject {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
|
static SixenseManager& getInstance();
|
||||||
|
|
||||||
SixenseManager();
|
void initialize();
|
||||||
~SixenseManager();
|
bool isInitialized() const { return _isInitialized; }
|
||||||
|
|
||||||
|
void setIsEnabled(bool isEnabled) { _isEnabled = isEnabled; }
|
||||||
|
|
||||||
void update(float deltaTime);
|
void update(float deltaTime);
|
||||||
float getCursorPixelRangeMult() const;
|
float getCursorPixelRangeMult() const;
|
||||||
|
@ -51,6 +59,9 @@ public slots:
|
||||||
void setLowVelocityFilter(bool lowVelocityFilter) { _lowVelocityFilter = lowVelocityFilter; };
|
void setLowVelocityFilter(bool lowVelocityFilter) { _lowVelocityFilter = lowVelocityFilter; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
SixenseManager();
|
||||||
|
~SixenseManager();
|
||||||
|
|
||||||
#ifdef HAVE_SIXENSE
|
#ifdef HAVE_SIXENSE
|
||||||
void updateCalibration(const sixenseControllerData* controllers);
|
void updateCalibration(const sixenseControllerData* controllers);
|
||||||
void emulateMouse(PalmData* palm, int index);
|
void emulateMouse(PalmData* palm, int index);
|
||||||
|
@ -72,7 +83,13 @@ private:
|
||||||
glm::vec3 _reachForward;
|
glm::vec3 _reachForward;
|
||||||
float _lastDistance;
|
float _lastDistance;
|
||||||
|
|
||||||
|
#ifdef __APPLE__
|
||||||
|
QLibrary* _sixenseLibrary;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
bool _isInitialized;
|
||||||
|
bool _isEnabled;
|
||||||
bool _hydrasConnected;
|
bool _hydrasConnected;
|
||||||
quint64 _lastMovement;
|
quint64 _lastMovement;
|
||||||
glm::vec3 _amountMoved;
|
glm::vec3 _amountMoved;
|
||||||
|
|
|
@ -671,7 +671,7 @@ void ApplicationOverlay::renderControllerPointers() {
|
||||||
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
float yAngle = 0.5f - ((atan2(direction.z, direction.y) + M_PI_2));
|
||||||
|
|
||||||
// Get the pixel range over which the xAngle and yAngle are scaled
|
// Get the pixel range over which the xAngle and yAngle are scaled
|
||||||
float cursorRange = glWidget->width() * application->getSixenseManager()->getCursorPixelRangeMult();
|
float cursorRange = glWidget->width() * SixenseManager::getInstance().getCursorPixelRangeMult();
|
||||||
|
|
||||||
mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle);
|
mouseX = (glWidget->width() / 2.0f + cursorRange * xAngle);
|
||||||
mouseY = (glWidget->height() / 2.0f + cursorRange * yAngle);
|
mouseY = (glWidget->height() / 2.0f + cursorRange * yAngle);
|
||||||
|
|
Loading…
Reference in a new issue