This commit is contained in:
Geenz 2014-03-14 19:38:32 -04:00
commit fe124f5c03
43 changed files with 625 additions and 383 deletions

View file

@ -74,7 +74,7 @@ function checkCamera(deltaTime) {
if (yaw < -360) { if (yaw < -360) {
yaw += 360; yaw += 360;
} }
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll); var orientation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);
Camera.setOrientation(orientation); Camera.setOrientation(orientation);
} }
} }

View file

@ -14,10 +14,10 @@ var cumulativeTime = 0.0;
Script.update.connect(function(deltaTime) { Script.update.connect(function(deltaTime) {
cumulativeTime += deltaTime; cumulativeTime += deltaTime;
MyAvatar.setJointData("joint_R_hip", Quat.fromPitchYawRoll(0.0, 0.0, AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY))); MyAvatar.setJointData("joint_R_hip", Quat.fromPitchYawRollDegrees(0.0, 0.0, AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY)));
MyAvatar.setJointData("joint_L_hip", Quat.fromPitchYawRoll(0.0, 0.0, -AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY))); MyAvatar.setJointData("joint_L_hip", Quat.fromPitchYawRollDegrees(0.0, 0.0, -AMPLITUDE * Math.sin(cumulativeTime * FREQUENCY)));
MyAvatar.setJointData("joint_R_knee", Quat.fromPitchYawRoll(0.0, 0.0, MyAvatar.setJointData("joint_R_knee", Quat.fromPitchYawRollDegrees(0.0, 0.0,
AMPLITUDE * (1.0 + Math.sin(cumulativeTime * FREQUENCY)))); AMPLITUDE * (1.0 + Math.sin(cumulativeTime * FREQUENCY))));
MyAvatar.setJointData("joint_L_knee", Quat.fromPitchYawRoll(0.0, 0.0, MyAvatar.setJointData("joint_L_knee", Quat.fromPitchYawRollDegrees(0.0, 0.0,
AMPLITUDE * (1.0 - Math.sin(cumulativeTime * FREQUENCY)))); AMPLITUDE * (1.0 - Math.sin(cumulativeTime * FREQUENCY))));
}); });

View file

@ -78,7 +78,7 @@ function update(deltaTime) {
// Check for mouseLook movement, update rotation // Check for mouseLook movement, update rotation
// rotate body yaw for yaw received from mouse // rotate body yaw for yaw received from mouse
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } ));
MyAvatar.orientation = newOrientation; MyAvatar.orientation = newOrientation;
yawFromMouse = 0; yawFromMouse = 0;

View file

@ -184,7 +184,7 @@ function flyWithHydra(deltaTime) {
// change the body yaw based on our x controller // change the body yaw based on our x controller
var orientation = MyAvatar.orientation; var orientation = MyAvatar.orientation;
var deltaOrientation = Quat.fromPitchYawRoll(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0); var deltaOrientation = Quat.fromPitchYawRollDegrees(0, (-1 * viewJoystickPosition.x * JOYSTICK_YAW_MAG * deltaTime), 0);
MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation); MyAvatar.orientation = Quat.multiply(orientation, deltaOrientation);
// change the headPitch based on our x controller // change the headPitch based on our x controller

View file

@ -54,7 +54,7 @@ function update(deltaTime) {
print("update()..."); print("update()...");
} }
// rotate body yaw for yaw received from mouse // rotate body yaw for yaw received from mouse
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMouse, z: 0 } ));
if (wantDebugging) { if (wantDebugging) {
print("changing orientation" print("changing orientation"
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","

View file

@ -45,7 +45,7 @@ function touchUpdateEvent(event) {
function update(deltaTime) { function update(deltaTime) {
// rotate body yaw for yaw received from mouse // rotate body yaw for yaw received from mouse
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMouse, z: 0 } )); var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromPitchYawRollRadians(0, yawFromMouse, 0));
if (wantDebugging) { if (wantDebugging) {
print("changing orientation" print("changing orientation"
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","

View file

@ -92,7 +92,7 @@ Controller.touchEndEvent.connect(touchEndEvent);
function update(deltaTime) { function update(deltaTime) {
// rotate body yaw for yaw received from multitouch rotate // rotate body yaw for yaw received from multitouch rotate
var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3( { x: 0, y: yawFromMultiTouch, z: 0 } )); var newOrientation = Quat.multiply(MyAvatar.orientation, Quat.fromVec3Radians( { x: 0, y: yawFromMultiTouch, z: 0 } ));
if (wantDebugging) { if (wantDebugging) {
print("changing orientation" print("changing orientation"
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + "," + " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","

View file

@ -17,7 +17,7 @@ var yawMin = 20;
var isLocal = false; var isLocal = false;
// set up our VoxelViewer with a position and orientation // set up our VoxelViewer with a position and orientation
var orientation = Quat.fromPitchYawRoll(0, yaw, 0); var orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
function init() { function init() {
if (isLocal) { if (isLocal) {
@ -40,7 +40,7 @@ function keepLooking(deltaTime) {
count++; count++;
if (count % 10 == 0) { if (count % 10 == 0) {
yaw += yawDirection; yaw += yawDirection;
orientation = Quat.fromPitchYawRoll(0, yaw, 0); orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
if (yaw > yawMax || yaw < yawMin) { if (yaw > yawMax || yaw < yawMin) {
yawDirection = yawDirection * -1; yawDirection = yawDirection * -1;
} }

View file

@ -4,22 +4,22 @@
<context> <context>
<name>Application</name> <name>Application</name>
<message> <message>
<location filename="src/Application.cpp" line="1362"/> <location filename="src/Application.cpp" line="1354"/>
<source>Export Voxels</source> <source>Export Voxels</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Application.cpp" line="1363"/> <location filename="src/Application.cpp" line="1355"/>
<source>Sparse Voxel Octree Files (*.svo)</source> <source>Sparse Voxel Octree Files (*.svo)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Application.cpp" line="3569"/> <location filename="src/Application.cpp" line="3565"/>
<source>Open Script</source> <source>Open Script</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Application.cpp" line="3570"/> <location filename="src/Application.cpp" line="3566"/>
<source>JavaScript Files (*.js)</source> <source>JavaScript Files (*.js)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -45,7 +45,7 @@
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="124"/> <location filename="src/ui/ChatWindow.cpp" line="128"/>
<source>day</source> <source>day</source>
<translation> <translation>
<numerusform>%n day</numerusform> <numerusform>%n day</numerusform>
@ -53,7 +53,7 @@
</translation> </translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="124"/> <location filename="src/ui/ChatWindow.cpp" line="128"/>
<source>hour</source> <source>hour</source>
<translation> <translation>
<numerusform>%n hour</numerusform> <numerusform>%n hour</numerusform>
@ -61,7 +61,7 @@
</translation> </translation>
</message> </message>
<message numerus="yes"> <message numerus="yes">
<location filename="src/ui/ChatWindow.cpp" line="124"/> <location filename="src/ui/ChatWindow.cpp" line="128"/>
<source>minute</source> <source>minute</source>
<translation> <translation>
<numerusform>%n minute</numerusform> <numerusform>%n minute</numerusform>
@ -76,7 +76,7 @@
</translation> </translation>
</message> </message>
<message> <message>
<location filename="src/ui/ChatWindow.cpp" line="179"/> <location filename="src/ui/ChatWindow.cpp" line="183"/>
<source>%1 online now:</source> <source>%1 online now:</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
@ -113,18 +113,18 @@
<context> <context>
<name>Menu</name> <name>Menu</name>
<message> <message>
<location filename="src/Menu.cpp" line="424"/> <location filename="src/Menu.cpp" line="429"/>
<source>Open .ini config file</source> <source>Open .ini config file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Menu.cpp" line="426"/> <location filename="src/Menu.cpp" line="431"/>
<location filename="src/Menu.cpp" line="438"/> <location filename="src/Menu.cpp" line="443"/>
<source>Text files (*.ini)</source> <source>Text files (*.ini)</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>
<message> <message>
<location filename="src/Menu.cpp" line="436"/> <location filename="src/Menu.cpp" line="441"/>
<source>Save .ini config file</source> <source>Save .ini config file</source>
<translation type="unfinished"></translation> <translation type="unfinished"></translation>
</message> </message>

View file

@ -27,7 +27,6 @@
#include <QColorDialog> #include <QColorDialog>
#include <QDesktopWidget> #include <QDesktopWidget>
#include <QCheckBox> #include <QCheckBox>
#include <QHBoxLayout>
#include <QImage> #include <QImage>
#include <QKeyEvent> #include <QKeyEvent>
#include <QMainWindow> #include <QMainWindow>
@ -292,14 +291,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
ResourceCache::setNetworkAccessManager(_networkAccessManager); ResourceCache::setNetworkAccessManager(_networkAccessManager);
ResourceCache::setRequestLimit(3); ResourceCache::setRequestLimit(3);
QWidget* centralWidget = new QWidget(); _window->setCentralWidget(_glWidget);
QHBoxLayout* mainLayout = new QHBoxLayout();
mainLayout->setSpacing(0);
mainLayout->setContentsMargins(0, 0, 0, 0);
centralWidget->setLayout(mainLayout);
_glWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
centralWidget->layout()->addWidget(_glWidget);
_window->setCentralWidget(centralWidget);
restoreSizeAndPosition(); restoreSizeAndPosition();
@ -1534,6 +1526,10 @@ void Application::init() {
} }
qDebug("Loaded settings"); qDebug("Loaded settings");
// initialize Visage and Faceshift after loading the menu settings
_faceshift.init();
_visage.init();
// fire off an immediate domain-server check in now that settings are loaded // fire off an immediate domain-server check in now that settings are loaded
NodeList::getInstance()->sendDomainServerCheckIn(); NodeList::getInstance()->sendDomainServerCheckIn();

View file

@ -10,6 +10,7 @@
#include "Util.h" #include "Util.h"
#include "world.h" #include "world.h"
#include "devices/SixenseManager.h"
const int NUM_ELEMENTS = 3; const int NUM_ELEMENTS = 3;
const float RANGE_BBALLS = 0.5f; const float RANGE_BBALLS = 0.5f;

View file

@ -8,6 +8,7 @@
#include <HandData.h> #include <HandData.h>
#include "Application.h" #include "Application.h"
#include "devices/SixenseManager.h"
#include "ControllerScriptingInterface.h" #include "ControllerScriptingInterface.h"
ControllerScriptingInterface::ControllerScriptingInterface() : ControllerScriptingInterface::ControllerScriptingInterface() :

View file

@ -272,11 +272,16 @@ Menu::Menu() :
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
MenuOption::FaceshiftTCP, MenuOption::Faceshift,
0, 0,
false, true,
appInstance->getFaceshift(), appInstance->getFaceshift(),
SLOT(setTCPEnabled(bool))); SLOT(setTCPEnabled(bool)));
#ifdef HAVE_VISAGE
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, true,
appInstance->getVisage(), SLOT(updateEnabled()));
#endif
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false); addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options"); QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
@ -1039,13 +1044,11 @@ void Menu::showMetavoxelEditor() {
void Menu::showChat() { void Menu::showChat() {
if (!_chatWindow) { if (!_chatWindow) {
_chatWindow = new ChatWindow(); Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow());
QMainWindow* mainWindow = Application::getInstance()->getWindow();
QBoxLayout* boxLayout = static_cast<QBoxLayout*>(mainWindow->centralWidget()->layout());
boxLayout->addWidget(_chatWindow, 0, Qt::AlignRight);
} else { } else {
if (!_chatWindow->isVisible()) { if (!_chatWindow->toggleViewAction()->isChecked()) {
_chatWindow->show(); _chatWindow->toggleViewAction()->trigger();
} }
} }
} }
@ -1053,8 +1056,8 @@ void Menu::showChat() {
void Menu::toggleChat() { void Menu::toggleChat() {
#ifdef HAVE_QXMPP #ifdef HAVE_QXMPP
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected()); _chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
if (!_chatAction->isEnabled() && _chatWindow) { if (!_chatAction->isEnabled() && _chatWindow && _chatWindow->toggleViewAction()->isChecked()) {
_chatWindow->hide(); _chatWindow->toggleViewAction()->trigger();
} }
#endif #endif
} }

View file

@ -239,7 +239,7 @@ namespace MenuOption {
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes"; const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
const QString HeadMouse = "Head Mouse"; const QString HeadMouse = "Head Mouse";
const QString HandsCollideWithSelf = "Collide With Self"; const QString HandsCollideWithSelf = "Collide With Self";
const QString FaceshiftTCP = "Faceshift (TCP)"; const QString Faceshift = "Faceshift";
const QString FirstPerson = "First Person"; const QString FirstPerson = "First Person";
const QString FrameTimer = "Show Timer"; const QString FrameTimer = "Show Timer";
const QString FrustumRenderMode = "Render Mode"; const QString FrustumRenderMode = "Render Mode";
@ -293,6 +293,7 @@ namespace MenuOption {
const QString TransmitterDrive = "Transmitter Drive"; const QString TransmitterDrive = "Transmitter Drive";
const QString UploaderAvatarHead = "Upload Avatar Head"; const QString UploaderAvatarHead = "Upload Avatar Head";
const QString UploaderAvatarSkeleton = "Upload Avatar Skeleton"; const QString UploaderAvatarSkeleton = "Upload Avatar Skeleton";
const QString Visage = "Visage";
const QString Quit = "Quit"; const QString Quit = "Quit";
const QString Voxels = "Voxels"; const QString Voxels = "Voxels";
const QString VoxelMode = "Cycle Voxel Mode"; const QString VoxelMode = "Cycle Voxel Mode";

View file

@ -56,7 +56,8 @@ Avatar::Avatar() :
_owningAvatarMixer(), _owningAvatarMixer(),
_collisionFlags(0), _collisionFlags(0),
_initialized(false), _initialized(false),
_shouldRenderBillboard(true) _shouldRenderBillboard(true),
_modelsDirty(true)
{ {
// we may have been created in the network thread, but we live in the main thread // we may have been created in the network thread, but we live in the main thread
moveToThread(Application::getInstance()->thread()); moveToThread(Application::getInstance()->thread());
@ -109,6 +110,11 @@ void Avatar::simulate(float deltaTime) {
_shouldRenderBillboard = true; _shouldRenderBillboard = true;
} }
// simple frustum check
float boundingRadius = getBillboardSize();
bool inViewFrustum = Application::getInstance()->getViewFrustum()->sphereInFrustum(_position, boundingRadius) !=
ViewFrustum::OUTSIDE;
getHand()->simulate(deltaTime, false); getHand()->simulate(deltaTime, false);
_skeletonModel.setLODDistance(getLODDistance()); _skeletonModel.setLODDistance(getLODDistance());
@ -118,8 +124,9 @@ void Avatar::simulate(float deltaTime) {
_skeletonModel.setJointState(i, data.valid, data.rotation); _skeletonModel.setJointState(i, data.valid, data.rotation);
} }
glm::vec3 headPosition = _position; glm::vec3 headPosition = _position;
if (!_shouldRenderBillboard) { if (!_shouldRenderBillboard && inViewFrustum) {
_skeletonModel.simulate(deltaTime); _skeletonModel.simulate(deltaTime, _modelsDirty);
_modelsDirty = false;
_skeletonModel.getHeadPosition(headPosition); _skeletonModel.getHeadPosition(headPosition);
} }
Head* head = getHead(); Head* head = getHead();
@ -183,6 +190,12 @@ static TextRenderer* textRenderer(TextRendererType type) {
} }
void Avatar::render(bool forShadowMap) { void Avatar::render(bool forShadowMap) {
// simple frustum check
float boundingRadius = getBillboardSize();
if (Application::getInstance()->getViewFrustum()->sphereInFrustum(_position, boundingRadius) == ViewFrustum::OUTSIDE) {
return;
}
glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition(); glm::vec3 toTarget = _position - Application::getInstance()->getAvatar()->getPosition();
float lengthToTarget = glm::length(toTarget); float lengthToTarget = glm::length(toTarget);
@ -336,7 +349,7 @@ void Avatar::renderBillboard() {
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
// compute the size from the billboard camera parameters and scale // compute the size from the billboard camera parameters and scale
float size = _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f)); float size = getBillboardSize();
glScalef(size, size, size); glScalef(size, size, size);
glColor3f(1.0f, 1.0f, 1.0f); glColor3f(1.0f, 1.0f, 1.0f);
@ -361,6 +374,10 @@ void Avatar::renderBillboard() {
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
} }
float Avatar::getBillboardSize() const {
return _scale * BILLBOARD_DISTANCE * tanf(glm::radians(BILLBOARD_FIELD_OF_VIEW / 2.0f));
}
void Avatar::renderDisplayName() { void Avatar::renderDisplayName() {
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) { if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
@ -618,6 +635,9 @@ int Avatar::parseData(const QByteArray& packet) {
const float MOVE_DISTANCE_THRESHOLD = 0.001f; const float MOVE_DISTANCE_THRESHOLD = 0.001f;
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD; _moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
// note that we need to update our models
_modelsDirty = true;
return bytesRead; return bytesRead;
} }

View file

@ -187,9 +187,12 @@ private:
bool _initialized; bool _initialized;
QScopedPointer<Texture> _billboardTexture; QScopedPointer<Texture> _billboardTexture;
bool _shouldRenderBillboard; bool _shouldRenderBillboard;
bool _modelsDirty;
void renderBody(); void renderBody();
void renderBillboard(); void renderBillboard();
float getBillboardSize() const;
}; };
#endif #endif

View file

@ -18,9 +18,9 @@ FaceModel::FaceModel(Head* owningHead) :
{ {
} }
void FaceModel::simulate(float deltaTime, bool delayLoad) { void FaceModel::simulate(float deltaTime) {
QVector<JointState> newJointStates = updateGeometry();
if (!isActive()) { if (!isActive()) {
Model::simulate(deltaTime, delayLoad);
return; return;
} }
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar); Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
@ -36,12 +36,13 @@ void FaceModel::simulate(float deltaTime, bool delayLoad) {
setRotation(neckRotation); setRotation(neckRotation);
const float MODEL_SCALE = 0.0006f; const float MODEL_SCALE = 0.0006f;
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE);
setOffset(-_geometry->getFBXGeometry().neckPivot); setOffset(-_geometry->getFBXGeometry().neckPivot);
setPupilDilation(_owningHead->getPupilDilation()); setPupilDilation(_owningHead->getPupilDilation());
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients()); setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
Model::simulate(deltaTime, delayLoad); Model::simulate(deltaTime, true, newJointStates);
} }
bool FaceModel::render(float alpha) { bool FaceModel::render(float alpha) {

View file

@ -21,7 +21,7 @@ public:
FaceModel(Head* owningHead); FaceModel(Head* owningHead);
void simulate(float deltaTime, bool delayLoad = false); void simulate(float deltaTime);
bool render(float alpha); bool render(float alpha);
protected: protected:

View file

@ -206,8 +206,8 @@ void Hand::collideAgainstOurself() {
} }
// ignoring everything below the parent of the parent of the last free joint // ignoring everything below the parent of the parent of the last free joint
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex( int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() : skeletonModel.getLastFreeJointIndex(((int)i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1))); ((int)i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
handCollisions.clear(); handCollisions.clear();
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) { if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) {

View file

@ -36,9 +36,9 @@ Head::Head(Avatar* owningAvatar) :
_leftEyeBlinkVelocity(0.0f), _leftEyeBlinkVelocity(0.0f),
_rightEyeBlinkVelocity(0.0f), _rightEyeBlinkVelocity(0.0f),
_timeWithoutTalking(0.0f), _timeWithoutTalking(0.0f),
_tweakedPitch(0.f), _pitchTweak(0.f),
_tweakedYaw(0.f), _yawTweak(0.f),
_tweakedRoll(0.f), _rollTweak(0.f),
_isCameraMoving(false), _isCameraMoving(false),
_faceModel(this) _faceModel(this)
{ {
@ -202,15 +202,15 @@ glm::vec3 Head::getScalePivot() const {
} }
float Head::getTweakedYaw() const { float Head::getTweakedYaw() const {
return glm::clamp(_yaw + _tweakedYaw, MIN_HEAD_YAW, MAX_HEAD_YAW); return glm::clamp(_yaw + _yawTweak, MIN_HEAD_YAW, MAX_HEAD_YAW);
} }
float Head::getTweakedPitch() const { float Head::getTweakedPitch() const {
return glm::clamp(_pitch + _tweakedPitch, MIN_HEAD_PITCH, MAX_HEAD_PITCH); return glm::clamp(_pitch + _pitchTweak, MIN_HEAD_PITCH, MAX_HEAD_PITCH);
} }
float Head::getTweakedRoll() const { float Head::getTweakedRoll() const {
return glm::clamp(_roll + _tweakedRoll, MIN_HEAD_ROLL, MAX_HEAD_ROLL); return glm::clamp(_roll + _rollTweak, MIN_HEAD_ROLL, MAX_HEAD_ROLL);
} }
void Head::applyCollision(CollisionInfo& collision) { void Head::applyCollision(CollisionInfo& collision) {

View file

@ -70,9 +70,14 @@ public:
/// Returns the point about which scaling occurs. /// Returns the point about which scaling occurs.
glm::vec3 getScalePivot() const; glm::vec3 getScalePivot() const;
void tweakPitch(float pitch) { _tweakedPitch = pitch; } void setPitchTweak(float pitch) { _pitchTweak = pitch; }
void tweakYaw(float yaw) { _tweakedYaw = yaw; } float getPitchTweak() const { return _pitchTweak; }
void tweakRoll(float roll) { _tweakedRoll = roll; }
void setYawTweak(float yaw) { _yawTweak = yaw; }
float getYawTweak() const { return _yawTweak; }
void setRollTweak(float roll) { _rollTweak = roll; }
float getRollTweak() const { return _rollTweak; }
virtual float getTweakedPitch() const; virtual float getTweakedPitch() const;
virtual float getTweakedYaw() const; virtual float getTweakedYaw() const;
@ -104,9 +109,9 @@ private:
float _timeWithoutTalking; float _timeWithoutTalking;
// tweaked angles affect the rendered head, but not the camera // tweaked angles affect the rendered head, but not the camera
float _tweakedPitch; float _pitchTweak;
float _tweakedYaw; float _yawTweak;
float _tweakedRoll; float _rollTweak;
bool _isCameraMoving; bool _isCameraMoving;
FaceModel _faceModel; FaceModel _faceModel;

View file

@ -363,8 +363,9 @@ void MyAvatar::updateFromGyros(float deltaTime) {
// restore rotation, lean to neutral positions // restore rotation, lean to neutral positions
const float RESTORE_PERIOD = 1.f; // seconds const float RESTORE_PERIOD = 1.f; // seconds
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f); float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
head->setYaw(glm::mix(head->getYaw(), 0.0f, restorePercentage)); head->setPitchTweak(glm::mix(head->getPitchTweak(), 0.0f, restorePercentage));
head->setRoll(glm::mix(head->getRoll(), 0.0f, restorePercentage)); head->setYawTweak(glm::mix(head->getYawTweak(), 0.0f, restorePercentage));
head->setRollTweak(glm::mix(head->getRollTweak(), 0.0f, restorePercentage));
head->setLeanSideways(glm::mix(head->getLeanSideways(), 0.0f, restorePercentage)); head->setLeanSideways(glm::mix(head->getLeanSideways(), 0.0f, restorePercentage));
head->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage)); head->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage));
return; return;
@ -375,9 +376,9 @@ void MyAvatar::updateFromGyros(float deltaTime) {
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f; const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f; const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f; const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
head->tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY); head->setPitchTweak(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
head->tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY); head->setYawTweak(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
head->tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY); head->setRollTweak(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
// Update torso lean distance based on accelerometer data // Update torso lean distance based on accelerometer data
const float TORSO_LENGTH = 0.5f; const float TORSO_LENGTH = 0.5f;

View file

@ -18,13 +18,13 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
_owningAvatar(owningAvatar) { _owningAvatar(owningAvatar) {
} }
void SkeletonModel::simulate(float deltaTime, bool delayLoad) { void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
setTranslation(_owningAvatar->getPosition()); setTranslation(_owningAvatar->getPosition());
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)));
const float MODEL_SCALE = 0.0006f; const float MODEL_SCALE = 0.0006f;
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE); setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningAvatar->getScale() * MODEL_SCALE);
Model::simulate(deltaTime, delayLoad); Model::simulate(deltaTime, fullUpdate);
if (!(isActive() && _owningAvatar->isMyAvatar())) { if (!(isActive() && _owningAvatar->isMyAvatar())) {
return; // only simulate for own avatar return; // only simulate for own avatar
@ -123,6 +123,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
return; return;
} }
const FBXGeometry& geometry = _geometry->getFBXGeometry(); const FBXGeometry& geometry = _geometry->getFBXGeometry();
setJointPosition(jointIndex, palm.getPosition());
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
glm::quat palmRotation; glm::quat palmRotation;
getJointRotation(jointIndex, palmRotation, true); getJointRotation(jointIndex, palmRotation, true);
@ -154,7 +155,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
// no point in continuing if there are no fingers // no point in continuing if there are no fingers
if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) { if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) {
stretchArm(jointIndex, palm.getPosition());
return; return;
} }
@ -172,8 +172,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true); setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true);
} }
stretchArm(jointIndex, palm.getPosition());
} }
void SkeletonModel::updateJointState(int index) { void SkeletonModel::updateJointState(int index) {
@ -200,41 +198,3 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
glm::normalize(inverse * axes[0])) * joint.rotation; glm::normalize(inverse * axes[0])) * joint.rotation;
} }
void SkeletonModel::stretchArm(int jointIndex, const glm::vec3& position) {
// find out where the hand is pointing
glm::quat handRotation;
getJointRotation(jointIndex, handRotation, true);
const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::vec3 forwardVector(jointIndex == geometry.rightHandJointIndex ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::vec3 handVector = handRotation * forwardVector;
// align elbow with hand
const FBXJoint& joint = geometry.joints.at(jointIndex);
if (joint.parentIndex == -1) {
return;
}
glm::quat elbowRotation;
getJointRotation(joint.parentIndex, elbowRotation, true);
applyRotationDelta(joint.parentIndex, rotationBetween(elbowRotation * forwardVector, handVector), false);
// set position according to normal length
float scale = extractUniformScale(_scale);
glm::vec3 handPosition = position - _translation;
glm::vec3 elbowPosition = handPosition - handVector * joint.distanceToParent * scale;
// set shoulder orientation to point to elbow
const FBXJoint& parentJoint = geometry.joints.at(joint.parentIndex);
if (parentJoint.parentIndex == -1) {
return;
}
glm::quat shoulderRotation;
getJointRotation(parentJoint.parentIndex, shoulderRotation, true);
applyRotationDelta(parentJoint.parentIndex, rotationBetween(shoulderRotation * forwardVector,
elbowPosition - extractTranslation(_jointStates.at(parentJoint.parentIndex).transform)), false);
// update the shoulder state
updateJointState(parentJoint.parentIndex);
// adjust the elbow's local translation
setJointTranslation(joint.parentIndex, elbowPosition);
}

View file

@ -22,7 +22,7 @@ public:
SkeletonModel(Avatar* owningAvatar); SkeletonModel(Avatar* owningAvatar);
void simulate(float deltaTime, bool delayLoad = false); void simulate(float deltaTime, bool fullUpdate = true);
bool render(float alpha); bool render(float alpha);
/// \param jointIndex index of hand joint /// \param jointIndex index of hand joint
@ -43,10 +43,6 @@ protected:
private: private:
/// Using the current position and rotation of the identified (hand) joint, computes a
/// reasonable stretched configuration for the connected arm.
void stretchArm(int jointIndex, const glm::vec3& position);
Avatar* _owningAvatar; Avatar* _owningAvatar;
}; };

View file

@ -21,7 +21,7 @@ using namespace std;
const quint16 FACESHIFT_PORT = 33433; const quint16 FACESHIFT_PORT = 33433;
Faceshift::Faceshift() : Faceshift::Faceshift() :
_tcpEnabled(false), _tcpEnabled(true),
_tcpRetryCount(0), _tcpRetryCount(0),
_lastTrackingStateReceived(0), _lastTrackingStateReceived(0),
_eyeGazeLeftPitch(0.0f), _eyeGazeLeftPitch(0.0f),
@ -49,12 +49,22 @@ Faceshift::Faceshift() :
connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected())); connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected()));
connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError))); connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError)));
connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket())); connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket()));
connect(&_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(connectionStateChanged()));
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams())); connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
_udpSocket.bind(FACESHIFT_PORT); _udpSocket.bind(FACESHIFT_PORT);
} }
void Faceshift::init() {
setTCPEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift));
}
bool Faceshift::isConnectedOrConnecting() const {
return _tcpSocket.state() == QAbstractSocket::ConnectedState ||
(_tcpRetryCount == 0 && _tcpSocket.state() != QAbstractSocket::UnconnectedState);
}
bool Faceshift::isActive() const { bool Faceshift::isActive() const {
const quint64 ACTIVE_TIMEOUT_USECS = 1000000; const quint64 ACTIVE_TIMEOUT_USECS = 1000000;
return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS; return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS;

View file

@ -27,6 +27,10 @@ public:
Faceshift(); Faceshift();
void init();
bool isConnectedOrConnecting() const;
bool isActive() const; bool isActive() const;
const glm::quat& getHeadRotation() const { return _headRotation; } const glm::quat& getHeadRotation() const { return _headRotation; }
@ -66,6 +70,10 @@ public:
void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp, void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp,
float jawOpen, std::vector<float>& coefficients) const; float jawOpen, std::vector<float>& coefficients) const;
signals:
void connectionStateChanged();
public slots: public slots:
void setTCPEnabled(bool enabled); void setTCPEnabled(bool enabled);

View file

@ -6,17 +6,33 @@
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
// //
#ifdef HAVE_SIXENSE #include <vector>
#include "sixense.h"
#endif
#include "Application.h" #include "Application.h"
#include "SixenseManager.h" #include "SixenseManager.h"
using namespace std;
SixenseManager::SixenseManager() : _lastMovement(0) {
#ifdef HAVE_SIXENSE #ifdef HAVE_SIXENSE
const int CALIBRATION_STATE_IDLE = 0;
const int CALIBRATION_STATE_X = 1;
const int CALIBRATION_STATE_Y = 2;
const int CALIBRATION_STATE_Z = 3;
const int CALIBRATION_STATE_COMPLETE = 4;
// default (expected) location of neck in sixense space
const float NECK_X = 250.f; // millimeters
const float NECK_Y = 300.f; // millimeters
const float NECK_Z = 300.f; // millimeters
#endif
SixenseManager::SixenseManager() {
#ifdef HAVE_SIXENSE
_lastMovement = 0;
_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(); sixenseInit();
#endif #endif
} }
@ -48,12 +64,18 @@ void SixenseManager::update(float deltaTime) {
Hand* hand = avatar->getHand(); Hand* hand = avatar->getHand();
int maxControllers = sixenseGetMaxControllers(); int maxControllers = sixenseGetMaxControllers();
for (int i = 0; i < maxControllers; i++) {
// we only support two controllers
sixenseControllerData controllers[2];
int numActiveControllers = 0;
for (int i = 0; i < maxControllers && numActiveControllers < 2; i++) {
if (!sixenseIsControllerEnabled(i)) { if (!sixenseIsControllerEnabled(i)) {
continue; continue;
} }
sixenseControllerData data; sixenseControllerData* data = controllers + numActiveControllers;
sixenseGetNewestData(i, &data); ++numActiveControllers;
sixenseGetNewestData(i, data);
// Set palm position and normal based on Hydra position/orientation // Set palm position and normal based on Hydra position/orientation
@ -61,7 +83,7 @@ void SixenseManager::update(float deltaTime) {
PalmData* palm; PalmData* palm;
bool foundHand = false; bool foundHand = false;
for (size_t j = 0; j < hand->getNumPalms(); j++) { for (size_t j = 0; j < hand->getNumPalms(); j++) {
if (hand->getPalms()[j].getSixenseID() == data.controller_index) { if (hand->getPalms()[j].getSixenseID() == data->controller_index) {
palm = &(hand->getPalms()[j]); palm = &(hand->getPalms()[j]);
foundHand = true; foundHand = true;
} }
@ -70,26 +92,27 @@ void SixenseManager::update(float deltaTime) {
PalmData newPalm(hand); PalmData newPalm(hand);
hand->getPalms().push_back(newPalm); hand->getPalms().push_back(newPalm);
palm = &(hand->getPalms()[hand->getNumPalms() - 1]); palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
palm->setSixenseID(data.controller_index); palm->setSixenseID(data->controller_index);
printf("Found new Sixense controller, ID %i\n", data.controller_index); printf("Found new Sixense controller, ID %i\n", data->controller_index);
} }
palm->setActive(true); palm->setActive(true);
// Read controller buttons and joystick into the hand // Read controller buttons and joystick into the hand
palm->setControllerButtons(data.buttons); palm->setControllerButtons(data->buttons);
palm->setTrigger(data.trigger); palm->setTrigger(data->trigger);
palm->setJoystick(data.joystick_x, data.joystick_y); palm->setJoystick(data->joystick_x, data->joystick_y);
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]); glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
// Adjust for distance between acquisition 'orb' and the user's torso // Transform the measured position into body frame.
// (distance to the right of body center, distance below torso, distance behind torso) glm::vec3 neck = _neckBase;
const glm::vec3 SPHERE_TO_TORSO(-250.f, -300.f, -300.f); // Zeroing y component of the "neck" effectively raises the measured position a little bit.
position = SPHERE_TO_TORSO + position; neck.y = 0.f;
position = _orbRotation * (position - neck);
// Rotation of Palm // Rotation of Palm
glm::quat rotation(data.rot_quat[3], -data.rot_quat[0], data.rot_quat[1], -data.rot_quat[2]); 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)) * rotation; rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation;
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f); const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
glm::vec3 newNormal = rotation * PALM_VECTOR; glm::vec3 newNormal = rotation * PALM_VECTOR;
palm->setRawNormal(newNormal); palm->setRawNormal(newNormal);
@ -127,13 +150,158 @@ void SixenseManager::update(float deltaTime) {
palm->getFingers().push_back(finger); palm->getFingers().push_back(finger);
} }
if (numActiveControllers == 2) {
updateCalibration(controllers);
}
// if the controllers haven't been moved in a while, disable // if the controllers haven't been moved in a while, disable
const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000; const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000;
if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) { if (usecTimestampNow() - _lastMovement > MOVEMENT_DISABLE_DURATION) {
for (vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) { for (std::vector<PalmData>::iterator it = hand->getPalms().begin(); it != hand->getPalms().end(); it++) {
it->setActive(false); it->setActive(false);
} }
} }
#endif #endif // HAVE_SIXENSE
} }
#ifdef HAVE_SIXENSE
// the calibration sequence is:
// (1) press BUTTON_FWD on both hands
// (2) reach arm straight out to the side (X)
// (3) lift arms staight up above head (Y)
// (4) move arms a bit forward (Z)
// (5) release BUTTON_FWD on both hands
const float MINIMUM_ARM_REACH = 300.f; // millimeters
const float MAXIMUM_NOISE_LEVEL = 50.f; // millimeters
const quint64 LOCK_DURATION = USECS_PER_SECOND / 4; // time for lock to be acquired
void SixenseManager::updateCalibration(const sixenseControllerData* controllers) {
const sixenseControllerData* dataLeft = controllers;
const sixenseControllerData* dataRight = controllers + 1;
// calibration only happpens while both hands are holding BUTTON_FORWARD
if (dataLeft->buttons != BUTTON_FWD || dataRight->buttons != BUTTON_FWD) {
if (_calibrationState == CALIBRATION_STATE_IDLE) {
return;
}
switch (_calibrationState) {
case CALIBRATION_STATE_Y:
case CALIBRATION_STATE_Z:
case CALIBRATION_STATE_COMPLETE:
{
// compute calibration results
// ATM we only handle the case where the XAxis has been measured, and we assume the rest
// (i.e. that the orb is on a level surface)
// TODO: handle COMPLETE state where all three axes have been defined. This would allow us
// to also handle the case where left and right controllers have been reversed.
_neckBase = 0.5f * (_reachLeft + _reachRight); // neck is midway between right and left reaches
glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
glm::vec3 yAxis(0.f, 1.f, 0.f);
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
xAxis = glm::normalize(glm::cross(yAxis, zAxis));
_orbRotation = glm::inverse(glm::quat_cast(glm::mat3(xAxis, yAxis, zAxis)));
qDebug("succeess: sixense calibration");
}
break;
default:
qDebug("failed: sixense calibration");
break;
}
_calibrationState = CALIBRATION_STATE_IDLE;
return;
}
const float* pos = dataLeft->pos;
glm::vec3 positionLeft(pos[0], pos[1], pos[2]);
pos = dataRight->pos;
glm::vec3 positionRight(pos[0], pos[1], pos[2]);
if (_calibrationState == CALIBRATION_STATE_IDLE) {
float reach = glm::distance(positionLeft, positionRight);
if (reach > 2.f * MINIMUM_ARM_REACH) {
qDebug("started: sixense calibration");
_averageLeft = positionLeft;
_averageRight = positionRight;
_reachLeft = _averageLeft;
_reachRight = _averageRight;
_lastDistance = reach;
_lockExpiry = usecTimestampNow() + LOCK_DURATION;
// move to next state
_calibrationState = CALIBRATION_STATE_X;
}
return;
}
quint64 now = usecTimestampNow() + LOCK_DURATION;
// these are weighted running averages
_averageLeft = 0.9f * _averageLeft + 0.1f * positionLeft;
_averageRight = 0.9f * _averageRight + 0.1f * positionRight;
if (_calibrationState == CALIBRATION_STATE_X) {
// compute new sliding average
float distance = glm::distance(_averageLeft, _averageRight);
if (fabs(distance - _lastDistance) > MAXIMUM_NOISE_LEVEL) {
// distance is increasing so acquire the data and push the expiry out
_reachLeft = _averageLeft;
_reachRight = _averageRight;
_lastDistance = distance;
_lockExpiry = now + LOCK_DURATION;
} else if (now > _lockExpiry) {
// lock has expired so clamp the data and move on
_lockExpiry = now + LOCK_DURATION;
_lastDistance = 0.f;
_reachUp = 0.5f * (_reachLeft + _reachRight);
_calibrationState = CALIBRATION_STATE_Y;
qDebug("success: sixense calibration: left");
}
}
else if (_calibrationState == CALIBRATION_STATE_Y) {
glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
float distance = (averagePosition - torso).y;
if (fabs(distance) > fabs(_lastDistance) + MAXIMUM_NOISE_LEVEL) {
// distance is increasing so acquire the data and push the expiry out
_reachUp = averagePosition;
_lastDistance = distance;
_lockExpiry = now + LOCK_DURATION;
} else if (now > _lockExpiry) {
if (_lastDistance > MINIMUM_ARM_REACH) {
// lock has expired so clamp the data and move on
_reachForward = _reachUp;
_lastDistance = 0.f;
_lockExpiry = now + LOCK_DURATION;
_calibrationState = CALIBRATION_STATE_Z;
qDebug("success: sixense calibration: up");
}
}
}
else if (_calibrationState == CALIBRATION_STATE_Z) {
glm::vec3 xAxis = glm::normalize(_reachRight - _reachLeft);
glm::vec3 torso = 0.5f * (_reachLeft + _reachRight);
//glm::vec3 yAxis = glm::normalize(_reachUp - torso);
glm::vec3 yAxis(0.f, 1.f, 0.f);
glm::vec3 zAxis = glm::normalize(glm::cross(xAxis, yAxis));
glm::vec3 averagePosition = 0.5f * (_averageLeft + _averageRight);
float distance = glm::dot((averagePosition - torso), zAxis);
if (fabs(distance) > fabs(_lastDistance)) {
// distance is increasing so acquire the data and push the expiry out
_reachForward = averagePosition;
_lastDistance = distance;
_lockExpiry = now + LOCK_DURATION;
} else if (now > _lockExpiry) {
if (fabs(_lastDistance) > 0.05f * MINIMUM_ARM_REACH) {
// lock has expired so clamp the data and move on
_calibrationState = CALIBRATION_STATE_COMPLETE;
qDebug("success: sixense calibration: forward");
// TODO: it is theoretically possible to detect that the controllers have been
// accidentally switched (left hand is holding right controller) and to swap the order.
}
}
}
}
#endif // HAVE_SIXENSE

View file

@ -9,6 +9,21 @@
#ifndef __interface__SixenseManager__ #ifndef __interface__SixenseManager__
#define __interface__SixenseManager__ #define __interface__SixenseManager__
#include <QObject>
#ifdef HAVE_SIXENSE
#include <glm/glm.hpp>
#include <glm/gtc/quaternion.hpp>
#include "sixense.h"
#endif
const unsigned int BUTTON_0 = 1U << 0; // the skinny button between 1 and 2
const unsigned int BUTTON_1 = 1U << 5;
const unsigned int BUTTON_2 = 1U << 6;
const unsigned int BUTTON_3 = 1U << 3;
const unsigned int BUTTON_4 = 1U << 4;
const unsigned int BUTTON_FWD = 1U << 7;
/// Handles interaction with the Sixense SDK (e.g., Razer Hydra). /// Handles interaction with the Sixense SDK (e.g., Razer Hydra).
class SixenseManager : public QObject { class SixenseManager : public QObject {
Q_OBJECT Q_OBJECT
@ -24,7 +39,27 @@ public slots:
void setFilter(bool filter); void setFilter(bool filter);
private: private:
#ifdef HAVE_SIXENSE
void updateCalibration(const sixenseControllerData* controllers);
int _calibrationState;
// these are calibration results
glm::vec3 _neckBase; // midpoint between controllers during X-axis calibration
glm::quat _orbRotation; // rotates from orb frame into body frame
float _armLength;
// these are measured values used to compute the calibration results
quint64 _lockExpiry;
glm::vec3 _averageLeft;
glm::vec3 _averageRight;
glm::vec3 _reachLeft;
glm::vec3 _reachRight;
glm::vec3 _reachUp;
glm::vec3 _reachForward;
float _lastDistance;
#endif
quint64 _lastMovement; quint64 _lastMovement;
}; };

View file

@ -32,6 +32,7 @@ using namespace VisageSDK;
const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f); const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f);
Visage::Visage() : Visage::Visage() :
_enabled(false),
_active(false), _active(false),
_headOrigin(DEFAULT_HEAD_ORIGIN), _headOrigin(DEFAULT_HEAD_ORIGIN),
_estimatedEyePitch(0.0f), _estimatedEyePitch(0.0f),
@ -41,23 +42,15 @@ Visage::Visage() :
QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc"; QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc";
initializeLicenseManager(licensePath.data()); initializeLicenseManager(licensePath.data());
_tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg"); _tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg");
if (_tracker->trackFromCam()) {
_data = new FaceData(); _data = new FaceData();
} else {
delete _tracker;
_tracker = NULL;
}
#endif #endif
} }
Visage::~Visage() { Visage::~Visage() {
#ifdef HAVE_VISAGE #ifdef HAVE_VISAGE
if (_tracker) {
_tracker->stop(); _tracker->stop();
delete _tracker; delete _tracker;
delete _data; delete _data;
}
#endif #endif
} }
@ -117,9 +110,14 @@ static const QMultiHash<QByteArray, QPair<int, float> >& getActionUnitNameMap()
const float TRANSLATION_SCALE = 20.0f; const float TRANSLATION_SCALE = 20.0f;
void Visage::init() {
connect(Application::getInstance()->getFaceshift(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled()));
updateEnabled();
}
void Visage::update() { void Visage::update() {
#ifdef HAVE_VISAGE #ifdef HAVE_VISAGE
_active = (_tracker && _tracker->getTrackingData(_data) == TRACK_STAT_OK); _active = (_tracker->getTrackingData(_data) == TRACK_STAT_OK);
if (!_active) { if (!_active) {
return; return;
} }
@ -160,3 +158,22 @@ void Visage::update() {
void Visage::reset() { void Visage::reset() {
_headOrigin += _headTranslation / TRANSLATION_SCALE; _headOrigin += _headTranslation / TRANSLATION_SCALE;
} }
void Visage::updateEnabled() {
setEnabled(Menu::getInstance()->isOptionChecked(MenuOption::Visage) &&
!(Menu::getInstance()->isOptionChecked(MenuOption::Faceshift) &&
Application::getInstance()->getFaceshift()->isConnectedOrConnecting()));
}
void Visage::setEnabled(bool enabled) {
#ifdef HAVE_VISAGE
if (_enabled == enabled) {
return;
}
if ((_enabled = enabled)) {
_tracker->trackFromCam();
} else {
_tracker->stop();
}
#endif
}

View file

@ -24,11 +24,15 @@ namespace VisageSDK {
} }
/// Handles input from the Visage webcam feature tracking software. /// Handles input from the Visage webcam feature tracking software.
class Visage { class Visage : public QObject {
Q_OBJECT
public: public:
Visage(); Visage();
~Visage(); virtual ~Visage();
void init();
bool isActive() const { return _active; } bool isActive() const { return _active; }
@ -43,6 +47,10 @@ public:
void update(); void update();
void reset(); void reset();
public slots:
void updateEnabled();
private: private:
#ifdef HAVE_VISAGE #ifdef HAVE_VISAGE
@ -51,6 +59,9 @@ private:
QMultiHash<int, QPair<int, float> > _actionUnitIndexMap; QMultiHash<int, QPair<int, float> > _actionUnitIndexMap;
#endif #endif
void setEnabled(bool enabled);
bool _enabled;
bool _active; bool _active;
glm::quat _headRotation; glm::quat _headRotation;
glm::vec3 _headTranslation; glm::vec3 _headTranslation;

View file

@ -156,142 +156,9 @@ void Model::updateShapePositions() {
} }
} }
void Model::simulate(float deltaTime, bool delayLoad) { void Model::simulate(float deltaTime, bool fullUpdate) {
// update our LOD // update our LOD, then simulate
QVector<JointState> newJointStates = updateGeometry(delayLoad); simulate(deltaTime, fullUpdate, updateGeometry());
if (!isActive()) {
return;
}
// set up world vertices on first simulate after load
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (_jointStates.isEmpty()) {
_jointStates = newJointStates.isEmpty() ? createJointStates(geometry) : newJointStates;
foreach (const FBXMesh& mesh, geometry.meshes) {
MeshState state;
state.clusterMatrices.resize(mesh.clusters.size());
if (mesh.springiness > 0.0f) {
state.worldSpaceVertices.resize(mesh.vertices.size());
state.vertexVelocities.resize(mesh.vertices.size());
state.worldSpaceNormals.resize(mesh.vertices.size());
}
_meshStates.append(state);
}
foreach (const FBXAttachment& attachment, geometry.attachments) {
Model* model = new Model(this);
model->init();
model->setURL(attachment.url);
_attachments.append(model);
}
_resetStates = true;
createCollisionShapes();
}
// update the world space transforms for all joints
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
}
// update the attachment transforms and simulate them
for (int i = 0; i < _attachments.size(); i++) {
const FBXAttachment& attachment = geometry.attachments.at(i);
Model* model = _attachments.at(i);
glm::vec3 jointTranslation = _translation;
glm::quat jointRotation = _rotation;
getJointPosition(attachment.jointIndex, jointTranslation);
getJointRotation(attachment.jointIndex, jointRotation);
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
model->setRotation(jointRotation * attachment.rotation);
model->setScale(_scale * attachment.scale);
model->simulate(deltaTime);
}
for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix;
}
int vertexCount = state.worldSpaceVertices.size();
if (vertexCount == 0) {
continue;
}
glm::vec3* destVertices = state.worldSpaceVertices.data();
glm::vec3* destVelocities = state.vertexVelocities.data();
glm::vec3* destNormals = state.worldSpaceNormals.data();
const glm::vec3* sourceVertices = mesh.vertices.constData();
if (!mesh.blendshapes.isEmpty()) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
// blend in each coefficient
for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) {
float coefficient = _blendshapeCoefficients[j];
if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
continue;
}
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
for (const int* index = mesh.blendshapes[j].indices.constData(),
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++) {
_blendedVertices[*index] += *vertex * coefficient;
}
}
sourceVertices = _blendedVertices.constData();
}
glm::mat4 transform = glm::translate(_translation);
if (mesh.clusters.size() > 1) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
// skin each vertex
const glm::vec4* clusterIndices = mesh.clusterIndices.constData();
const glm::vec4* clusterWeights = mesh.clusterWeights.constData();
for (int j = 0; j < vertexCount; j++) {
_blendedVertices[j] =
glm::vec3(state.clusterMatrices[clusterIndices[j][0]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][0] +
glm::vec3(state.clusterMatrices[clusterIndices[j][1]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][1] +
glm::vec3(state.clusterMatrices[clusterIndices[j][2]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][2] +
glm::vec3(state.clusterMatrices[clusterIndices[j][3]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][3];
}
sourceVertices = _blendedVertices.constData();
} else {
transform = state.clusterMatrices[0];
}
if (_resetStates) {
for (int j = 0; j < vertexCount; j++) {
destVertices[j] = glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f));
destVelocities[j] = glm::vec3();
}
} else {
const float SPRINGINESS_MULTIPLIER = 200.0f;
const float DAMPING = 5.0f;
for (int j = 0; j < vertexCount; j++) {
destVelocities[j] += ((glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f)) - destVertices[j]) *
mesh.springiness * SPRINGINESS_MULTIPLIER - destVelocities[j] * DAMPING) * deltaTime;
destVertices[j] += destVelocities[j] * deltaTime;
}
}
for (int j = 0; j < vertexCount; j++) {
destNormals[j] = glm::vec3();
const glm::vec3& middle = destVertices[j];
for (QVarLengthArray<QPair<int, int>, 4>::const_iterator connection = mesh.vertexConnections.at(j).constBegin();
connection != mesh.vertexConnections.at(j).constEnd(); connection++) {
destNormals[j] += glm::normalize(glm::cross(destVertices[connection->second] - middle,
destVertices[connection->first] - middle));
}
}
}
_resetStates = false;
} }
bool Model::render(float alpha) { bool Model::render(float alpha) {
@ -572,6 +439,186 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi
return collided; return collided;
} }
QVector<Model::JointState> Model::updateGeometry() {
QVector<JointState> newJointStates;
if (_nextGeometry) {
_nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis);
_nextGeometry->setLoadPriority(this, -_lodDistance);
_nextGeometry->ensureLoading();
if (_nextGeometry->isLoaded()) {
applyNextGeometry();
return newJointStates;
}
}
if (!_geometry) {
return newJointStates;
}
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis);
if (_geometry != geometry) {
if (!_jointStates.isEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
newJointStates = createJointStates(newGeometry);
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
int oldIndex = it.value() - 1;
int newIndex = newGeometry.getJointIndex(it.key());
if (newIndex != -1) {
newJointStates[newIndex] = _jointStates.at(oldIndex);
}
}
}
deleteGeometry();
_dilatedTextures.clear();
_geometry = geometry;
}
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
return newJointStates;
}
void Model::simulate(float deltaTime, bool fullUpdate, const QVector<JointState>& newJointStates) {
if (!isActive()) {
return;
}
// set up world vertices on first simulate after load
const FBXGeometry& geometry = _geometry->getFBXGeometry();
if (_jointStates.isEmpty()) {
_jointStates = newJointStates.isEmpty() ? createJointStates(geometry) : newJointStates;
foreach (const FBXMesh& mesh, geometry.meshes) {
MeshState state;
state.clusterMatrices.resize(mesh.clusters.size());
if (mesh.springiness > 0.0f) {
state.worldSpaceVertices.resize(mesh.vertices.size());
state.vertexVelocities.resize(mesh.vertices.size());
state.worldSpaceNormals.resize(mesh.vertices.size());
}
_meshStates.append(state);
}
foreach (const FBXAttachment& attachment, geometry.attachments) {
Model* model = new Model(this);
model->init();
model->setURL(attachment.url);
_attachments.append(model);
}
_resetStates = fullUpdate = true;
createCollisionShapes();
}
// exit early if we don't have to perform a full update
if (!(fullUpdate || _resetStates)) {
return;
}
// update the world space transforms for all joints
for (int i = 0; i < _jointStates.size(); i++) {
updateJointState(i);
}
// update the attachment transforms and simulate them
for (int i = 0; i < _attachments.size(); i++) {
const FBXAttachment& attachment = geometry.attachments.at(i);
Model* model = _attachments.at(i);
glm::vec3 jointTranslation = _translation;
glm::quat jointRotation = _rotation;
getJointPosition(attachment.jointIndex, jointTranslation);
getJointRotation(attachment.jointIndex, jointRotation);
model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale);
model->setRotation(jointRotation * attachment.rotation);
model->setScale(_scale * attachment.scale);
model->simulate(deltaTime);
}
for (int i = 0; i < _meshStates.size(); i++) {
MeshState& state = _meshStates[i];
const FBXMesh& mesh = geometry.meshes.at(i);
for (int j = 0; j < mesh.clusters.size(); j++) {
const FBXCluster& cluster = mesh.clusters.at(j);
state.clusterMatrices[j] = _jointStates[cluster.jointIndex].transform * cluster.inverseBindMatrix;
}
int vertexCount = state.worldSpaceVertices.size();
if (vertexCount == 0) {
continue;
}
glm::vec3* destVertices = state.worldSpaceVertices.data();
glm::vec3* destVelocities = state.vertexVelocities.data();
glm::vec3* destNormals = state.worldSpaceNormals.data();
const glm::vec3* sourceVertices = mesh.vertices.constData();
if (!mesh.blendshapes.isEmpty()) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
memcpy(_blendedVertices.data(), mesh.vertices.constData(), vertexCount * sizeof(glm::vec3));
// blend in each coefficient
for (unsigned int j = 0; j < _blendshapeCoefficients.size(); j++) {
float coefficient = _blendshapeCoefficients[j];
if (coefficient == 0.0f || j >= (unsigned int)mesh.blendshapes.size() || mesh.blendshapes[j].vertices.isEmpty()) {
continue;
}
const glm::vec3* vertex = mesh.blendshapes[j].vertices.constData();
for (const int* index = mesh.blendshapes[j].indices.constData(),
*end = index + mesh.blendshapes[j].indices.size(); index != end; index++, vertex++) {
_blendedVertices[*index] += *vertex * coefficient;
}
}
sourceVertices = _blendedVertices.constData();
}
glm::mat4 transform = glm::translate(_translation);
if (mesh.clusters.size() > 1) {
_blendedVertices.resize(max(_blendedVertices.size(), vertexCount));
// skin each vertex
const glm::vec4* clusterIndices = mesh.clusterIndices.constData();
const glm::vec4* clusterWeights = mesh.clusterWeights.constData();
for (int j = 0; j < vertexCount; j++) {
_blendedVertices[j] =
glm::vec3(state.clusterMatrices[clusterIndices[j][0]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][0] +
glm::vec3(state.clusterMatrices[clusterIndices[j][1]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][1] +
glm::vec3(state.clusterMatrices[clusterIndices[j][2]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][2] +
glm::vec3(state.clusterMatrices[clusterIndices[j][3]] *
glm::vec4(sourceVertices[j], 1.0f)) * clusterWeights[j][3];
}
sourceVertices = _blendedVertices.constData();
} else {
transform = state.clusterMatrices[0];
}
if (_resetStates) {
for (int j = 0; j < vertexCount; j++) {
destVertices[j] = glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f));
destVelocities[j] = glm::vec3();
}
} else {
const float SPRINGINESS_MULTIPLIER = 200.0f;
const float DAMPING = 5.0f;
for (int j = 0; j < vertexCount; j++) {
destVelocities[j] += ((glm::vec3(transform * glm::vec4(sourceVertices[j], 1.0f)) - destVertices[j]) *
mesh.springiness * SPRINGINESS_MULTIPLIER - destVelocities[j] * DAMPING) * deltaTime;
destVertices[j] += destVelocities[j] * deltaTime;
}
}
for (int j = 0; j < vertexCount; j++) {
destNormals[j] = glm::vec3();
const glm::vec3& middle = destVertices[j];
for (QVarLengthArray<QPair<int, int>, 4>::const_iterator connection = mesh.vertexConnections.at(j).constBegin();
connection != mesh.vertexConnections.at(j).constEnd(); connection++) {
destNormals[j] += glm::normalize(glm::cross(destVertices[connection->second] - middle,
destVertices[connection->first] - middle));
}
}
}
_resetStates = false;
}
void Model::updateJointState(int index) { void Model::updateJointState(int index) {
_shapesAreDirty = true; _shapesAreDirty = true;
JointState& state = _jointStates[index]; JointState& state = _jointStates[index];
@ -868,49 +915,6 @@ void Model::applyCollision(CollisionInfo& collision) {
} }
} }
QVector<Model::JointState> Model::updateGeometry(bool delayLoad) {
QVector<JointState> newJointStates;
if (_nextGeometry) {
_nextGeometry = _nextGeometry->getLODOrFallback(_lodDistance, _nextLODHysteresis, delayLoad);
if (!delayLoad) {
_nextGeometry->setLoadPriority(this, -_lodDistance);
_nextGeometry->ensureLoading();
}
if (_nextGeometry->isLoaded()) {
applyNextGeometry();
return newJointStates;
}
}
if (!_geometry) {
return newJointStates;
}
QSharedPointer<NetworkGeometry> geometry = _geometry->getLODOrFallback(_lodDistance, _lodHysteresis, delayLoad);
if (_geometry != geometry) {
if (!_jointStates.isEmpty()) {
// copy the existing joint states
const FBXGeometry& oldGeometry = _geometry->getFBXGeometry();
const FBXGeometry& newGeometry = geometry->getFBXGeometry();
newJointStates = createJointStates(newGeometry);
for (QHash<QString, int>::const_iterator it = oldGeometry.jointIndices.constBegin();
it != oldGeometry.jointIndices.constEnd(); it++) {
int oldIndex = it.value() - 1;
int newIndex = newGeometry.getJointIndex(it.key());
if (newIndex != -1) {
newJointStates[newIndex] = _jointStates.at(oldIndex);
}
}
}
deleteGeometry();
_dilatedTextures.clear();
_geometry = geometry;
}
if (!delayLoad) {
_geometry->setLoadPriority(this, -_lodDistance);
_geometry->ensureLoading();
}
return newJointStates;
}
void Model::applyNextGeometry() { void Model::applyNextGeometry() {
// delete our local geometry and custom textures // delete our local geometry and custom textures
deleteGeometry(); deleteGeometry();

View file

@ -57,7 +57,7 @@ public:
void clearShapes(); void clearShapes();
void createCollisionShapes(); void createCollisionShapes();
void updateShapePositions(); void updateShapePositions();
void simulate(float deltaTime, bool delayLoad = false); void simulate(float deltaTime, bool fullUpdate = true);
bool render(float alpha); bool render(float alpha);
/// Sets the URL of the model to render. /// Sets the URL of the model to render.
@ -226,6 +226,9 @@ protected:
QVector<MeshState> _meshStates; QVector<MeshState> _meshStates;
QVector<JointState> updateGeometry();
void simulate(float deltaTime, bool fullUpdate, const QVector<JointState>& newJointStates);
/// Updates the state of the joint at the specified index. /// Updates the state of the joint at the specified index.
virtual void updateJointState(int index); virtual void updateJointState(int index);
@ -256,7 +259,6 @@ protected:
private: private:
QVector<JointState> updateGeometry(bool delayLoad);
void applyNextGeometry(); void applyNextGeometry();
void deleteGeometry(); void deleteGeometry();
void renderMeshes(float alpha, bool translucent); void renderMeshes(float alpha, bool translucent);

View file

@ -29,7 +29,7 @@ BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthMeter* model) :
this->QDialog::setLayout(form); this->QDialog::setLayout(form);
// Setup labels // Setup labels
for (int i = 0; i < BandwidthMeter::N_STREAMS; ++i) { for (size_t i = 0; i < BandwidthMeter::N_STREAMS; ++i) {
bool input = i % 2 == 0; bool input = i % 2 == 0;
BandwidthMeter::ChannelInfo& ch = _model->channelInfo(BandwidthMeter::ChannelIndex(i / 2)); BandwidthMeter::ChannelInfo& ch = _model->channelInfo(BandwidthMeter::ChannelIndex(i / 2));
QLabel* label = _labels[i] = new QLabel(); QLabel* label = _labels[i] = new QLabel();
@ -48,7 +48,7 @@ BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthMeter* model) :
} }
BandwidthDialog::~BandwidthDialog() { BandwidthDialog::~BandwidthDialog() {
for (int i = 0; i < BandwidthMeter::N_STREAMS; ++i) { for (size_t i = 0; i < BandwidthMeter::N_STREAMS; ++i) {
delete _labels[i]; delete _labels[i];
} }
} }
@ -57,7 +57,7 @@ void BandwidthDialog::paintEvent(QPaintEvent* event) {
// Update labels // Update labels
char strBuf[64]; char strBuf[64];
for (int i = 0; i < BandwidthMeter::N_STREAMS; ++i) { for (size_t i = 0; i < BandwidthMeter::N_STREAMS; ++i) {
BandwidthMeter::ChannelIndex chIdx = BandwidthMeter::ChannelIndex(i / 2); BandwidthMeter::ChannelIndex chIdx = BandwidthMeter::ChannelIndex(i / 2);
bool input = i % 2 == 0; bool input = i % 2 == 0;
BandwidthMeter::ChannelInfo& ch = _model->channelInfo(chIdx); BandwidthMeter::ChannelInfo& ch = _model->channelInfo(chIdx);

View file

@ -28,17 +28,21 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20;
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)"); const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
ChatWindow::ChatWindow() : ChatWindow::ChatWindow() :
QWidget(),
ui(new Ui::ChatWindow), ui(new Ui::ChatWindow),
numMessagesAfterLastTimeStamp(0) numMessagesAfterLastTimeStamp(0)
{ {
ui->setupUi(this); QWidget* widget = new QWidget();
setWidget(widget);
ui->setupUi(widget);
FlowLayout* flowLayout = new FlowLayout(0, 4, 4); FlowLayout* flowLayout = new FlowLayout(0, 4, 4);
ui->usersWidget->setLayout(flowLayout); ui->usersWidget->setLayout(flowLayout);
ui->messagePlainTextEdit->installEventFilter(this); ui->messagePlainTextEdit->installEventFilter(this);
ui->closeButton->hide();
#ifdef HAVE_QXMPP #ifdef HAVE_QXMPP
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient(); const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
if (xmppClient.isConnected()) { if (xmppClient.isConnected()) {

View file

@ -10,8 +10,8 @@
#define __interface__ChatWindow__ #define __interface__ChatWindow__
#include <QDateTime> #include <QDateTime>
#include <QDockWidget>
#include <QTimer> #include <QTimer>
#include <QWidget>
#include <Application.h> #include <Application.h>
@ -26,7 +26,7 @@ namespace Ui {
class ChatWindow; class ChatWindow;
} }
class ChatWindow : public QWidget { class ChatWindow : public QDockWidget {
Q_OBJECT Q_OBJECT
public: public:

View file

@ -41,7 +41,7 @@ void LocalVoxelsOverlay::update(float deltatime) {
} }
_tree->lockForRead(); _tree->lockForRead();
if (_visible && _voxelCount != _tree->getOctreeElementsCount()) { if (_visible && _voxelCount != (int)_tree->getOctreeElementsCount()) {
_voxelCount = _tree->getOctreeElementsCount(); _voxelCount = _tree->getOctreeElementsCount();
_voxelSystem->forceRedrawEntireTree(); _voxelSystem->forceRedrawEntireTree();
} }

View file

@ -25,10 +25,6 @@ HandData::HandData(AvatarData* owningAvatar) :
addNewPalm(); addNewPalm();
} }
glm::vec3 HandData::worldPositionToLeapPosition(const glm::vec3& worldPosition) const {
return glm::inverse(getBaseOrientation()) * (worldPosition - getBasePosition()) / LEAP_UNIT_SCALE;
}
glm::vec3 HandData::worldVectorToLeapVector(const glm::vec3& worldVector) const { glm::vec3 HandData::worldVectorToLeapVector(const glm::vec3& worldVector) const {
return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE; return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE;
} }
@ -272,9 +268,7 @@ glm::quat HandData::getBaseOrientation() const {
} }
glm::vec3 HandData::getBasePosition() const { glm::vec3 HandData::getBasePosition() const {
const glm::vec3 LEAP_HANDS_OFFSET_FROM_TORSO(0.0, 0.3, -0.3); return _owningAvatarData->getPosition();
return _owningAvatarData->getPosition() + getBaseOrientation() * LEAP_HANDS_OFFSET_FROM_TORSO *
_owningAvatarData->getTargetScale();
} }
void FingerData::setTrailLength(unsigned int length) { void FingerData::setTrailLength(unsigned int length) {

View file

@ -28,13 +28,6 @@ const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND;
const int LEAPID_INVALID = -1; const int LEAPID_INVALID = -1;
const int SIXENSEID_INVALID = -1; const int SIXENSEID_INVALID = -1;
const int BUTTON_0 = 1; // the skinny button between 1 and 2
const int BUTTON_1 = 32;
const int BUTTON_2 = 64;
const int BUTTON_3 = 8;
const int BUTTON_4 = 16;
const int BUTTON_FWD = 128;
const float LEAP_UNIT_SCALE = 0.001f; ///< convert mm to meters const float LEAP_UNIT_SCALE = 0.001f; ///< convert mm to meters
const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0; const int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0;
@ -55,7 +48,6 @@ public:
glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) { glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) {
return getBaseOrientation() * leapDirection; return getBaseOrientation() * leapDirection;
} }
glm::vec3 worldPositionToLeapPosition(const glm::vec3& worldPosition) const;
glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const; glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const;
std::vector<PalmData>& getPalms() { return _palms; } std::vector<PalmData>& getPalms() { return _palms; }
@ -182,11 +174,11 @@ public:
int getFramesWithoutData() const { return _numFramesWithoutData; } int getFramesWithoutData() const { return _numFramesWithoutData; }
// Controller buttons // Controller buttons
void setControllerButtons(int controllerButtons) { _controllerButtons = controllerButtons; } void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; }
void setLastControllerButtons(int controllerButtons) { _lastControllerButtons = controllerButtons; } void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; }
int getControllerButtons() const { return _controllerButtons; } unsigned int getControllerButtons() const { return _controllerButtons; }
int getLastControllerButtons() const { return _lastControllerButtons; } unsigned int getLastControllerButtons() const { return _lastControllerButtons; }
void setTrigger(float trigger) { _trigger = trigger; } void setTrigger(float trigger) { _trigger = trigger; }
float getTrigger() const { return _trigger; } float getTrigger() const { return _trigger; }
@ -217,8 +209,8 @@ private:
glm::vec3 _tipPosition; glm::vec3 _tipPosition;
glm::vec3 _tipVelocity; glm::vec3 _tipVelocity;
int _controllerButtons; unsigned int _controllerButtons;
int _lastControllerButtons; unsigned int _lastControllerButtons;
float _trigger; float _trigger;
float _joystickX, _joystickY; float _joystickX, _joystickY;

View file

@ -925,7 +925,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):" qDebug() << "(_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE):"
<< (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE); << (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE);
} }
if (missingItem <= std::max(0, (_incomingLastSequence - MAX_MISSING_SEQUENCE_OLD_AGE))) { if (missingItem <= (unsigned int)std::max(0, (int)_incomingLastSequence - (int)MAX_MISSING_SEQUENCE_OLD_AGE)) {
if (wantExtraDebugging) { if (wantExtraDebugging) {
qDebug() << "pruning really old missing sequence:" << missingItem; qDebug() << "pruning really old missing sequence:" << missingItem;
} }

View file

@ -22,14 +22,22 @@ glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& q2) {
return q1 * q2; return q1 * q2;
} }
glm::quat Quat::fromVec3(const glm::vec3& eulerAngles) { glm::quat Quat::fromVec3Degrees(const glm::vec3& eulerAngles) {
return glm::quat(glm::radians(eulerAngles)); return glm::quat(glm::radians(eulerAngles));
} }
glm::quat Quat::fromPitchYawRoll(float pitch, float yaw, float roll) { glm::quat Quat::fromVec3Radians(const glm::vec3& eulerAngles) {
return glm::quat(eulerAngles);
}
glm::quat Quat::fromPitchYawRollDegrees(float pitch, float yaw, float roll) {
return glm::quat(glm::radians(glm::vec3(pitch, yaw, roll))); return glm::quat(glm::radians(glm::vec3(pitch, yaw, roll)));
} }
glm::quat Quat::fromPitchYawRollRadians(float pitch, float yaw, float roll) {
return glm::quat(glm::vec3(pitch, yaw, roll));
}
glm::quat Quat::inverse(const glm::quat& q) { glm::quat Quat::inverse(const glm::quat& q) {
return glm::inverse(q); return glm::inverse(q);
} }

View file

@ -23,8 +23,10 @@ class Quat : public QObject {
public slots: public slots:
glm::quat multiply(const glm::quat& q1, const glm::quat& q2); glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
glm::quat fromVec3(const glm::vec3& vec3); glm::quat fromVec3Degrees(const glm::vec3& vec3); // degrees
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll); // degrees glm::quat fromVec3Radians(const glm::vec3& vec3); // radians
glm::quat fromPitchYawRollDegrees(float pitch, float yaw, float roll); // degrees
glm::quat fromPitchYawRollRadians(float pitch, float yaw, float roll); // radians
glm::quat inverse(const glm::quat& q); glm::quat inverse(const glm::quat& q);
glm::vec3 getFront(const glm::quat& orientation); glm::vec3 getFront(const glm::quat& orientation);
glm::vec3 getRight(const glm::quat& orientation); glm::vec3 getRight(const glm::quat& orientation);

View file

@ -57,10 +57,9 @@ static const float DEGREES_PER_RADIAN = 180.0f / PI;
static const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations static const float EPSILON = 0.000001f; //smallish positive number - used as margin of error for some computations
static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.f); static const float SQUARE_ROOT_OF_2 = (float)sqrt(2.f);
static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f); static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f);
static const float METER = 1.0f; static const float METERS_PER_DECIMETER = 0.1f;
static const float DECIMETER = 0.1f; static const float METERS_PER_CENTIMETER = 0.01f;
static const float CENTIMETER = 0.01f; static const float METERS_PER_MILLIMETER = 0.001f;
static const float MILLIIMETER = 0.001f;
static const quint64 USECS_PER_MSEC = 1000; static const quint64 USECS_PER_MSEC = 1000;
static const quint64 MSECS_PER_SECOND = 1000; static const quint64 MSECS_PER_SECOND = 1000;
static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND; static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;

View file

@ -174,7 +174,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
CapsuleShape capsuleB(radiusB, halfHeightB); CapsuleShape capsuleB(radiusB, halfHeightB);
// give the capsule some arbirary transform // give the capsule some arbirary transform
float angle = 37.8; float angle = 37.8f;
glm::vec3 axis = glm::normalize( glm::vec3(-7.f, 2.8f, 9.3f) ); glm::vec3 axis = glm::normalize( glm::vec3(-7.f, 2.8f, 9.3f) );
glm::quat rotation = glm::angleAxis(angle, axis); glm::quat rotation = glm::angleAxis(angle, axis);
glm::vec3 translation(15.1f, -27.1f, -38.6f); glm::vec3 translation(15.1f, -27.1f, -38.6f);