mirror of
https://github.com/lubosz/overte.git
synced 2025-04-27 04:55:32 +02:00
Merge branch 'master' of https://github.com/highfidelity/hifi
This commit is contained in:
commit
fe124f5c03
43 changed files with 625 additions and 383 deletions
examples
cameraExample.jscrazylegs.jsgun.jshydraMove.jslookWithMouse.jslookWithTouch.jsmultitouchExample.jsseeingVoxelsExample.js
interface
interface_en.ts
src
libraries
avatars/src
octree/src
script-engine/src
shared/src
tests/physics/src
|
@ -74,7 +74,7 @@ function checkCamera(deltaTime) {
|
|||
if (yaw < -360) {
|
||||
yaw += 360;
|
||||
}
|
||||
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll);
|
||||
var orientation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);
|
||||
Camera.setOrientation(orientation);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,10 +14,10 @@ var cumulativeTime = 0.0;
|
|||
|
||||
Script.update.connect(function(deltaTime) {
|
||||
cumulativeTime += deltaTime;
|
||||
MyAvatar.setJointData("joint_R_hip", Quat.fromPitchYawRoll(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_R_knee", Quat.fromPitchYawRoll(0.0, 0.0,
|
||||
MyAvatar.setJointData("joint_R_hip", Quat.fromPitchYawRollDegrees(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.fromPitchYawRollDegrees(0.0, 0.0,
|
||||
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))));
|
||||
});
|
||||
|
|
|
@ -78,7 +78,7 @@ function update(deltaTime) {
|
|||
|
||||
// Check for mouseLook movement, update rotation
|
||||
// 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;
|
||||
yawFromMouse = 0;
|
||||
|
||||
|
|
|
@ -184,7 +184,7 @@ function flyWithHydra(deltaTime) {
|
|||
|
||||
// change the body yaw based on our x controller
|
||||
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);
|
||||
|
||||
// change the headPitch based on our x controller
|
||||
|
|
|
@ -54,7 +54,7 @@ function update(deltaTime) {
|
|||
print("update()...");
|
||||
}
|
||||
// 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) {
|
||||
print("changing orientation"
|
||||
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","
|
||||
|
|
|
@ -45,7 +45,7 @@ function touchUpdateEvent(event) {
|
|||
|
||||
function update(deltaTime) {
|
||||
// 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) {
|
||||
print("changing orientation"
|
||||
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","
|
||||
|
|
|
@ -92,7 +92,7 @@ Controller.touchEndEvent.connect(touchEndEvent);
|
|||
|
||||
function update(deltaTime) {
|
||||
// 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) {
|
||||
print("changing orientation"
|
||||
+ " [old]MyAvatar.orientation="+MyAvatar.orientation.x + "," + MyAvatar.orientation.y + ","
|
||||
|
|
|
@ -17,7 +17,7 @@ var yawMin = 20;
|
|||
var isLocal = false;
|
||||
|
||||
// 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() {
|
||||
if (isLocal) {
|
||||
|
@ -40,7 +40,7 @@ function keepLooking(deltaTime) {
|
|||
count++;
|
||||
if (count % 10 == 0) {
|
||||
yaw += yawDirection;
|
||||
orientation = Quat.fromPitchYawRoll(0, yaw, 0);
|
||||
orientation = Quat.fromPitchYawRollDegrees(0, yaw, 0);
|
||||
if (yaw > yawMax || yaw < yawMin) {
|
||||
yawDirection = yawDirection * -1;
|
||||
}
|
||||
|
|
|
@ -4,22 +4,22 @@
|
|||
<context>
|
||||
<name>Application</name>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1362"/>
|
||||
<location filename="src/Application.cpp" line="1354"/>
|
||||
<source>Export Voxels</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="1363"/>
|
||||
<location filename="src/Application.cpp" line="1355"/>
|
||||
<source>Sparse Voxel Octree Files (*.svo)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3569"/>
|
||||
<location filename="src/Application.cpp" line="3565"/>
|
||||
<source>Open Script</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Application.cpp" line="3570"/>
|
||||
<location filename="src/Application.cpp" line="3566"/>
|
||||
<source>JavaScript Files (*.js)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -45,7 +45,7 @@
|
|||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="src/ui/ChatWindow.cpp" line="124"/>
|
||||
<location filename="src/ui/ChatWindow.cpp" line="128"/>
|
||||
<source>day</source>
|
||||
<translation>
|
||||
<numerusform>%n day</numerusform>
|
||||
|
@ -53,7 +53,7 @@
|
|||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="src/ui/ChatWindow.cpp" line="124"/>
|
||||
<location filename="src/ui/ChatWindow.cpp" line="128"/>
|
||||
<source>hour</source>
|
||||
<translation>
|
||||
<numerusform>%n hour</numerusform>
|
||||
|
@ -61,7 +61,7 @@
|
|||
</translation>
|
||||
</message>
|
||||
<message numerus="yes">
|
||||
<location filename="src/ui/ChatWindow.cpp" line="124"/>
|
||||
<location filename="src/ui/ChatWindow.cpp" line="128"/>
|
||||
<source>minute</source>
|
||||
<translation>
|
||||
<numerusform>%n minute</numerusform>
|
||||
|
@ -76,7 +76,7 @@
|
|||
</translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/ui/ChatWindow.cpp" line="179"/>
|
||||
<location filename="src/ui/ChatWindow.cpp" line="183"/>
|
||||
<source>%1 online now:</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
@ -113,18 +113,18 @@
|
|||
<context>
|
||||
<name>Menu</name>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="424"/>
|
||||
<location filename="src/Menu.cpp" line="429"/>
|
||||
<source>Open .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="426"/>
|
||||
<location filename="src/Menu.cpp" line="438"/>
|
||||
<location filename="src/Menu.cpp" line="431"/>
|
||||
<location filename="src/Menu.cpp" line="443"/>
|
||||
<source>Text files (*.ini)</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
<message>
|
||||
<location filename="src/Menu.cpp" line="436"/>
|
||||
<location filename="src/Menu.cpp" line="441"/>
|
||||
<source>Save .ini config file</source>
|
||||
<translation type="unfinished"></translation>
|
||||
</message>
|
||||
|
|
|
@ -27,7 +27,6 @@
|
|||
#include <QColorDialog>
|
||||
#include <QDesktopWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
#include <QImage>
|
||||
#include <QKeyEvent>
|
||||
#include <QMainWindow>
|
||||
|
@ -292,14 +291,7 @@ Application::Application(int& argc, char** argv, timeval &startup_time) :
|
|||
ResourceCache::setNetworkAccessManager(_networkAccessManager);
|
||||
ResourceCache::setRequestLimit(3);
|
||||
|
||||
QWidget* centralWidget = new QWidget();
|
||||
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);
|
||||
_window->setCentralWidget(_glWidget);
|
||||
|
||||
restoreSizeAndPosition();
|
||||
|
||||
|
@ -1534,6 +1526,10 @@ void Application::init() {
|
|||
}
|
||||
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
|
||||
NodeList::getInstance()->sendDomainServerCheckIn();
|
||||
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "Util.h"
|
||||
#include "world.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
|
||||
const int NUM_ELEMENTS = 3;
|
||||
const float RANGE_BBALLS = 0.5f;
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include <HandData.h>
|
||||
#include "Application.h"
|
||||
#include "devices/SixenseManager.h"
|
||||
#include "ControllerScriptingInterface.h"
|
||||
|
||||
ControllerScriptingInterface::ControllerScriptingInterface() :
|
||||
|
|
|
@ -272,11 +272,16 @@ Menu::Menu() :
|
|||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::LookAtVectors, 0, false);
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu,
|
||||
MenuOption::FaceshiftTCP,
|
||||
MenuOption::Faceshift,
|
||||
0,
|
||||
false,
|
||||
true,
|
||||
appInstance->getFaceshift(),
|
||||
SLOT(setTCPEnabled(bool)));
|
||||
#ifdef HAVE_VISAGE
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::Visage, 0, true,
|
||||
appInstance->getVisage(), SLOT(updateEnabled()));
|
||||
#endif
|
||||
|
||||
addCheckableActionToQMenuAndActionHash(avatarOptionsMenu, MenuOption::ChatCircling, 0, false);
|
||||
|
||||
QMenu* handOptionsMenu = developerMenu->addMenu("Hand Options");
|
||||
|
@ -1039,13 +1044,11 @@ void Menu::showMetavoxelEditor() {
|
|||
|
||||
void Menu::showChat() {
|
||||
if (!_chatWindow) {
|
||||
_chatWindow = new ChatWindow();
|
||||
QMainWindow* mainWindow = Application::getInstance()->getWindow();
|
||||
QBoxLayout* boxLayout = static_cast<QBoxLayout*>(mainWindow->centralWidget()->layout());
|
||||
boxLayout->addWidget(_chatWindow, 0, Qt::AlignRight);
|
||||
Application::getInstance()->getWindow()->addDockWidget(Qt::RightDockWidgetArea, _chatWindow = new ChatWindow());
|
||||
|
||||
} else {
|
||||
if (!_chatWindow->isVisible()) {
|
||||
_chatWindow->show();
|
||||
if (!_chatWindow->toggleViewAction()->isChecked()) {
|
||||
_chatWindow->toggleViewAction()->trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1053,8 +1056,8 @@ void Menu::showChat() {
|
|||
void Menu::toggleChat() {
|
||||
#ifdef HAVE_QXMPP
|
||||
_chatAction->setEnabled(XmppClient::getInstance().getXMPPClient().isConnected());
|
||||
if (!_chatAction->isEnabled() && _chatWindow) {
|
||||
_chatWindow->hide();
|
||||
if (!_chatAction->isEnabled() && _chatWindow && _chatWindow->toggleViewAction()->isChecked()) {
|
||||
_chatWindow->toggleViewAction()->trigger();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ namespace MenuOption {
|
|||
const QString DontFadeOnVoxelServerChanges = "Don't Fade In/Out on Voxel Server Changes";
|
||||
const QString HeadMouse = "Head Mouse";
|
||||
const QString HandsCollideWithSelf = "Collide With Self";
|
||||
const QString FaceshiftTCP = "Faceshift (TCP)";
|
||||
const QString Faceshift = "Faceshift";
|
||||
const QString FirstPerson = "First Person";
|
||||
const QString FrameTimer = "Show Timer";
|
||||
const QString FrustumRenderMode = "Render Mode";
|
||||
|
@ -293,6 +293,7 @@ namespace MenuOption {
|
|||
const QString TransmitterDrive = "Transmitter Drive";
|
||||
const QString UploaderAvatarHead = "Upload Avatar Head";
|
||||
const QString UploaderAvatarSkeleton = "Upload Avatar Skeleton";
|
||||
const QString Visage = "Visage";
|
||||
const QString Quit = "Quit";
|
||||
const QString Voxels = "Voxels";
|
||||
const QString VoxelMode = "Cycle Voxel Mode";
|
||||
|
|
|
@ -56,7 +56,8 @@ Avatar::Avatar() :
|
|||
_owningAvatarMixer(),
|
||||
_collisionFlags(0),
|
||||
_initialized(false),
|
||||
_shouldRenderBillboard(true)
|
||||
_shouldRenderBillboard(true),
|
||||
_modelsDirty(true)
|
||||
{
|
||||
// we may have been created in the network thread, but we live in the main thread
|
||||
moveToThread(Application::getInstance()->thread());
|
||||
|
@ -109,6 +110,11 @@ void Avatar::simulate(float deltaTime) {
|
|||
_shouldRenderBillboard = true;
|
||||
}
|
||||
|
||||
// simple frustum check
|
||||
float boundingRadius = getBillboardSize();
|
||||
bool inViewFrustum = Application::getInstance()->getViewFrustum()->sphereInFrustum(_position, boundingRadius) !=
|
||||
ViewFrustum::OUTSIDE;
|
||||
|
||||
getHand()->simulate(deltaTime, false);
|
||||
_skeletonModel.setLODDistance(getLODDistance());
|
||||
|
||||
|
@ -118,8 +124,9 @@ void Avatar::simulate(float deltaTime) {
|
|||
_skeletonModel.setJointState(i, data.valid, data.rotation);
|
||||
}
|
||||
glm::vec3 headPosition = _position;
|
||||
if (!_shouldRenderBillboard) {
|
||||
_skeletonModel.simulate(deltaTime);
|
||||
if (!_shouldRenderBillboard && inViewFrustum) {
|
||||
_skeletonModel.simulate(deltaTime, _modelsDirty);
|
||||
_modelsDirty = false;
|
||||
_skeletonModel.getHeadPosition(headPosition);
|
||||
}
|
||||
Head* head = getHead();
|
||||
|
@ -183,6 +190,12 @@ static TextRenderer* textRenderer(TextRendererType type) {
|
|||
}
|
||||
|
||||
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();
|
||||
float lengthToTarget = glm::length(toTarget);
|
||||
|
||||
|
@ -336,7 +349,7 @@ void Avatar::renderBillboard() {
|
|||
glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z);
|
||||
|
||||
// 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);
|
||||
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
|
@ -361,6 +374,10 @@ void Avatar::renderBillboard() {
|
|||
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() {
|
||||
|
||||
if (_displayName.isEmpty() || _displayNameAlpha == 0.0f) {
|
||||
|
@ -618,6 +635,9 @@ int Avatar::parseData(const QByteArray& packet) {
|
|||
const float MOVE_DISTANCE_THRESHOLD = 0.001f;
|
||||
_moving = glm::distance(oldPosition, _position) > MOVE_DISTANCE_THRESHOLD;
|
||||
|
||||
// note that we need to update our models
|
||||
_modelsDirty = true;
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
|
|
|
@ -187,9 +187,12 @@ private:
|
|||
bool _initialized;
|
||||
QScopedPointer<Texture> _billboardTexture;
|
||||
bool _shouldRenderBillboard;
|
||||
bool _modelsDirty;
|
||||
|
||||
void renderBody();
|
||||
void renderBillboard();
|
||||
|
||||
float getBillboardSize() const;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -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()) {
|
||||
Model::simulate(deltaTime, delayLoad);
|
||||
return;
|
||||
}
|
||||
Avatar* owningAvatar = static_cast<Avatar*>(_owningHead->_owningAvatar);
|
||||
|
@ -36,12 +36,13 @@ void FaceModel::simulate(float deltaTime, bool delayLoad) {
|
|||
setRotation(neckRotation);
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
setScale(glm::vec3(1.0f, 1.0f, 1.0f) * _owningHead->getScale() * MODEL_SCALE);
|
||||
|
||||
setOffset(-_geometry->getFBXGeometry().neckPivot);
|
||||
|
||||
setPupilDilation(_owningHead->getPupilDilation());
|
||||
setBlendshapeCoefficients(_owningHead->getBlendshapeCoefficients());
|
||||
|
||||
Model::simulate(deltaTime, delayLoad);
|
||||
Model::simulate(deltaTime, true, newJointStates);
|
||||
}
|
||||
|
||||
bool FaceModel::render(float alpha) {
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
|
||||
FaceModel(Head* owningHead);
|
||||
|
||||
void simulate(float deltaTime, bool delayLoad = false);
|
||||
void simulate(float deltaTime);
|
||||
bool render(float alpha);
|
||||
|
||||
protected:
|
||||
|
|
|
@ -206,8 +206,8 @@ void Hand::collideAgainstOurself() {
|
|||
}
|
||||
// ignoring everything below the parent of the parent of the last free joint
|
||||
int skipIndex = skeletonModel.getParentJointIndex(skeletonModel.getParentJointIndex(
|
||||
skeletonModel.getLastFreeJointIndex((i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
||||
(i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
||||
skeletonModel.getLastFreeJointIndex(((int)i == leftPalmIndex) ? skeletonModel.getLeftHandJointIndex() :
|
||||
((int)i == rightPalmIndex) ? skeletonModel.getRightHandJointIndex() : -1)));
|
||||
|
||||
handCollisions.clear();
|
||||
if (_owningAvatar->findSphereCollisions(palm.getPosition(), scaledPalmRadius, handCollisions, skipIndex)) {
|
||||
|
|
|
@ -36,9 +36,9 @@ Head::Head(Avatar* owningAvatar) :
|
|||
_leftEyeBlinkVelocity(0.0f),
|
||||
_rightEyeBlinkVelocity(0.0f),
|
||||
_timeWithoutTalking(0.0f),
|
||||
_tweakedPitch(0.f),
|
||||
_tweakedYaw(0.f),
|
||||
_tweakedRoll(0.f),
|
||||
_pitchTweak(0.f),
|
||||
_yawTweak(0.f),
|
||||
_rollTweak(0.f),
|
||||
_isCameraMoving(false),
|
||||
_faceModel(this)
|
||||
{
|
||||
|
@ -202,15 +202,15 @@ glm::vec3 Head::getScalePivot() 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 {
|
||||
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 {
|
||||
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) {
|
||||
|
|
|
@ -70,10 +70,15 @@ public:
|
|||
/// Returns the point about which scaling occurs.
|
||||
glm::vec3 getScalePivot() const;
|
||||
|
||||
void tweakPitch(float pitch) { _tweakedPitch = pitch; }
|
||||
void tweakYaw(float yaw) { _tweakedYaw = yaw; }
|
||||
void tweakRoll(float roll) { _tweakedRoll = roll; }
|
||||
void setPitchTweak(float pitch) { _pitchTweak = pitch; }
|
||||
float getPitchTweak() const { return _pitchTweak; }
|
||||
|
||||
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 getTweakedYaw() const;
|
||||
virtual float getTweakedRoll() const;
|
||||
|
@ -104,9 +109,9 @@ private:
|
|||
float _timeWithoutTalking;
|
||||
|
||||
// tweaked angles affect the rendered head, but not the camera
|
||||
float _tweakedPitch;
|
||||
float _tweakedYaw;
|
||||
float _tweakedRoll;
|
||||
float _pitchTweak;
|
||||
float _yawTweak;
|
||||
float _rollTweak;
|
||||
|
||||
bool _isCameraMoving;
|
||||
FaceModel _faceModel;
|
||||
|
|
|
@ -363,8 +363,9 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
|||
// restore rotation, lean to neutral positions
|
||||
const float RESTORE_PERIOD = 1.f; // seconds
|
||||
float restorePercentage = glm::clamp(deltaTime/RESTORE_PERIOD, 0.f, 1.f);
|
||||
head->setYaw(glm::mix(head->getYaw(), 0.0f, restorePercentage));
|
||||
head->setRoll(glm::mix(head->getRoll(), 0.0f, restorePercentage));
|
||||
head->setPitchTweak(glm::mix(head->getPitchTweak(), 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->setLeanForward(glm::mix(head->getLeanForward(), 0.0f, restorePercentage));
|
||||
return;
|
||||
|
@ -375,9 +376,9 @@ void MyAvatar::updateFromGyros(float deltaTime) {
|
|||
const float AVATAR_HEAD_PITCH_MAGNIFY = 1.0f;
|
||||
const float AVATAR_HEAD_YAW_MAGNIFY = 1.0f;
|
||||
const float AVATAR_HEAD_ROLL_MAGNIFY = 1.0f;
|
||||
head->tweakPitch(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||
head->tweakYaw(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||
head->tweakRoll(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||
head->setPitchTweak(estimatedRotation.x * AVATAR_HEAD_PITCH_MAGNIFY);
|
||||
head->setYawTweak(estimatedRotation.y * AVATAR_HEAD_YAW_MAGNIFY);
|
||||
head->setRollTweak(estimatedRotation.z * AVATAR_HEAD_ROLL_MAGNIFY);
|
||||
|
||||
// Update torso lean distance based on accelerometer data
|
||||
const float TORSO_LENGTH = 0.5f;
|
||||
|
|
|
@ -18,13 +18,13 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
|
|||
_owningAvatar(owningAvatar) {
|
||||
}
|
||||
|
||||
void SkeletonModel::simulate(float deltaTime, bool delayLoad) {
|
||||
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
|
||||
setTranslation(_owningAvatar->getPosition());
|
||||
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)));
|
||||
const float MODEL_SCALE = 0.0006f;
|
||||
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())) {
|
||||
return; // only simulate for own avatar
|
||||
|
@ -123,6 +123,7 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
|
|||
return;
|
||||
}
|
||||
const FBXGeometry& geometry = _geometry->getFBXGeometry();
|
||||
setJointPosition(jointIndex, palm.getPosition());
|
||||
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
|
||||
glm::quat palmRotation;
|
||||
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
|
||||
if (palm.getNumFingers() == 0 || fingerJointIndices.isEmpty()) {
|
||||
stretchArm(jointIndex, palm.getPosition());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -172,8 +172,6 @@ void SkeletonModel::applyPalmData(int jointIndex, const QVector<int>& fingerJoin
|
|||
|
||||
setJointRotation(fingerJointIndex, rotationBetween(palmRotation * jointVector, fingerVector) * palmRotation, true);
|
||||
}
|
||||
|
||||
stretchArm(jointIndex, palm.getPosition());
|
||||
}
|
||||
|
||||
void SkeletonModel::updateJointState(int index) {
|
||||
|
@ -200,41 +198,3 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const
|
|||
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);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ public:
|
|||
|
||||
SkeletonModel(Avatar* owningAvatar);
|
||||
|
||||
void simulate(float deltaTime, bool delayLoad = false);
|
||||
void simulate(float deltaTime, bool fullUpdate = true);
|
||||
bool render(float alpha);
|
||||
|
||||
/// \param jointIndex index of hand joint
|
||||
|
@ -43,10 +43,6 @@ protected:
|
|||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ using namespace std;
|
|||
const quint16 FACESHIFT_PORT = 33433;
|
||||
|
||||
Faceshift::Faceshift() :
|
||||
_tcpEnabled(false),
|
||||
_tcpEnabled(true),
|
||||
_tcpRetryCount(0),
|
||||
_lastTrackingStateReceived(0),
|
||||
_eyeGazeLeftPitch(0.0f),
|
||||
|
@ -49,12 +49,22 @@ Faceshift::Faceshift() :
|
|||
connect(&_tcpSocket, SIGNAL(connected()), SLOT(noteConnected()));
|
||||
connect(&_tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), SLOT(noteError(QAbstractSocket::SocketError)));
|
||||
connect(&_tcpSocket, SIGNAL(readyRead()), SLOT(readFromSocket()));
|
||||
connect(&_tcpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)), SIGNAL(connectionStateChanged()));
|
||||
|
||||
connect(&_udpSocket, SIGNAL(readyRead()), SLOT(readPendingDatagrams()));
|
||||
|
||||
_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 {
|
||||
const quint64 ACTIVE_TIMEOUT_USECS = 1000000;
|
||||
return (usecTimestampNow() - _lastTrackingStateReceived) < ACTIVE_TIMEOUT_USECS;
|
||||
|
|
|
@ -27,6 +27,10 @@ public:
|
|||
|
||||
Faceshift();
|
||||
|
||||
void init();
|
||||
|
||||
bool isConnectedOrConnecting() const;
|
||||
|
||||
bool isActive() const;
|
||||
|
||||
const glm::quat& getHeadRotation() const { return _headRotation; }
|
||||
|
@ -66,6 +70,10 @@ public:
|
|||
void updateFakeCoefficients(float leftBlink, float rightBlink, float browUp,
|
||||
float jawOpen, std::vector<float>& coefficients) const;
|
||||
|
||||
signals:
|
||||
|
||||
void connectionStateChanged();
|
||||
|
||||
public slots:
|
||||
|
||||
void setTCPEnabled(bool enabled);
|
||||
|
|
|
@ -6,17 +6,33 @@
|
|||
// Copyright (c) 2013 High Fidelity, Inc. All rights reserved.
|
||||
//
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
#include "sixense.h"
|
||||
#endif
|
||||
#include <vector>
|
||||
|
||||
#include "Application.h"
|
||||
#include "SixenseManager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
SixenseManager::SixenseManager() : _lastMovement(0) {
|
||||
#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();
|
||||
#endif
|
||||
}
|
||||
|
@ -48,12 +64,18 @@ void SixenseManager::update(float deltaTime) {
|
|||
Hand* hand = avatar->getHand();
|
||||
|
||||
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)) {
|
||||
continue;
|
||||
}
|
||||
sixenseControllerData data;
|
||||
sixenseGetNewestData(i, &data);
|
||||
sixenseControllerData* data = controllers + numActiveControllers;
|
||||
++numActiveControllers;
|
||||
sixenseGetNewestData(i, data);
|
||||
|
||||
// Set palm position and normal based on Hydra position/orientation
|
||||
|
||||
|
@ -61,7 +83,7 @@ void SixenseManager::update(float deltaTime) {
|
|||
PalmData* palm;
|
||||
bool foundHand = false;
|
||||
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]);
|
||||
foundHand = true;
|
||||
}
|
||||
|
@ -70,26 +92,27 @@ void SixenseManager::update(float deltaTime) {
|
|||
PalmData newPalm(hand);
|
||||
hand->getPalms().push_back(newPalm);
|
||||
palm = &(hand->getPalms()[hand->getNumPalms() - 1]);
|
||||
palm->setSixenseID(data.controller_index);
|
||||
printf("Found new Sixense controller, ID %i\n", data.controller_index);
|
||||
palm->setSixenseID(data->controller_index);
|
||||
printf("Found new Sixense controller, ID %i\n", 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);
|
||||
|
||||
glm::vec3 position(data.pos[0], data.pos[1], data.pos[2]);
|
||||
// Adjust for distance between acquisition 'orb' and the user's torso
|
||||
// (distance to the right of body center, distance below torso, distance behind torso)
|
||||
const glm::vec3 SPHERE_TO_TORSO(-250.f, -300.f, -300.f);
|
||||
position = SPHERE_TO_TORSO + position;
|
||||
|
||||
palm->setControllerButtons(data->buttons);
|
||||
palm->setTrigger(data->trigger);
|
||||
palm->setJoystick(data->joystick_x, data->joystick_y);
|
||||
|
||||
glm::vec3 position(data->pos[0], data->pos[1], data->pos[2]);
|
||||
// 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)) * rotation;
|
||||
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;
|
||||
const glm::vec3 PALM_VECTOR(0.0f, -1.0f, 0.0f);
|
||||
glm::vec3 newNormal = rotation * PALM_VECTOR;
|
||||
palm->setRawNormal(newNormal);
|
||||
|
@ -126,14 +149,159 @@ 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
|
||||
const unsigned int MOVEMENT_DISABLE_DURATION = 30 * 1000 * 1000;
|
||||
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);
|
||||
}
|
||||
}
|
||||
#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
|
||||
|
||||
|
|
|
@ -9,6 +9,21 @@
|
|||
#ifndef __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).
|
||||
class SixenseManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
@ -24,7 +39,27 @@ public slots:
|
|||
void setFilter(bool filter);
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@ using namespace VisageSDK;
|
|||
const glm::vec3 DEFAULT_HEAD_ORIGIN(0.0f, 0.0f, 0.7f);
|
||||
|
||||
Visage::Visage() :
|
||||
_enabled(false),
|
||||
_active(false),
|
||||
_headOrigin(DEFAULT_HEAD_ORIGIN),
|
||||
_estimatedEyePitch(0.0f),
|
||||
|
@ -41,23 +42,15 @@ Visage::Visage() :
|
|||
QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc";
|
||||
initializeLicenseManager(licensePath.data());
|
||||
_tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg");
|
||||
if (_tracker->trackFromCam()) {
|
||||
_data = new FaceData();
|
||||
|
||||
} else {
|
||||
delete _tracker;
|
||||
_tracker = NULL;
|
||||
}
|
||||
_data = new FaceData();
|
||||
#endif
|
||||
}
|
||||
|
||||
Visage::~Visage() {
|
||||
#ifdef HAVE_VISAGE
|
||||
if (_tracker) {
|
||||
_tracker->stop();
|
||||
delete _tracker;
|
||||
delete _data;
|
||||
}
|
||||
_tracker->stop();
|
||||
delete _tracker;
|
||||
delete _data;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -117,9 +110,14 @@ static const QMultiHash<QByteArray, QPair<int, float> >& getActionUnitNameMap()
|
|||
|
||||
const float TRANSLATION_SCALE = 20.0f;
|
||||
|
||||
void Visage::init() {
|
||||
connect(Application::getInstance()->getFaceshift(), SIGNAL(connectionStateChanged()), SLOT(updateEnabled()));
|
||||
updateEnabled();
|
||||
}
|
||||
|
||||
void Visage::update() {
|
||||
#ifdef HAVE_VISAGE
|
||||
_active = (_tracker && _tracker->getTrackingData(_data) == TRACK_STAT_OK);
|
||||
_active = (_tracker->getTrackingData(_data) == TRACK_STAT_OK);
|
||||
if (!_active) {
|
||||
return;
|
||||
}
|
||||
|
@ -160,3 +158,22 @@ void Visage::update() {
|
|||
void Visage::reset() {
|
||||
_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
|
||||
}
|
||||
|
|
|
@ -24,11 +24,15 @@ namespace VisageSDK {
|
|||
}
|
||||
|
||||
/// Handles input from the Visage webcam feature tracking software.
|
||||
class Visage {
|
||||
class Visage : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
Visage();
|
||||
~Visage();
|
||||
virtual ~Visage();
|
||||
|
||||
void init();
|
||||
|
||||
bool isActive() const { return _active; }
|
||||
|
||||
|
@ -42,6 +46,10 @@ public:
|
|||
|
||||
void update();
|
||||
void reset();
|
||||
|
||||
public slots:
|
||||
|
||||
void updateEnabled();
|
||||
|
||||
private:
|
||||
|
||||
|
@ -51,6 +59,9 @@ private:
|
|||
QMultiHash<int, QPair<int, float> > _actionUnitIndexMap;
|
||||
#endif
|
||||
|
||||
void setEnabled(bool enabled);
|
||||
|
||||
bool _enabled;
|
||||
bool _active;
|
||||
glm::quat _headRotation;
|
||||
glm::vec3 _headTranslation;
|
||||
|
|
|
@ -156,142 +156,9 @@ void Model::updateShapePositions() {
|
|||
}
|
||||
}
|
||||
|
||||
void Model::simulate(float deltaTime, bool delayLoad) {
|
||||
// update our LOD
|
||||
QVector<JointState> newJointStates = updateGeometry(delayLoad);
|
||||
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;
|
||||
void Model::simulate(float deltaTime, bool fullUpdate) {
|
||||
// update our LOD, then simulate
|
||||
simulate(deltaTime, fullUpdate, updateGeometry());
|
||||
}
|
||||
|
||||
bool Model::render(float alpha) {
|
||||
|
@ -572,6 +439,186 @@ bool Model::findSphereCollisions(const glm::vec3& sphereCenter, float sphereRadi
|
|||
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) {
|
||||
_shapesAreDirty = true;
|
||||
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() {
|
||||
// delete our local geometry and custom textures
|
||||
deleteGeometry();
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
void clearShapes();
|
||||
void createCollisionShapes();
|
||||
void updateShapePositions();
|
||||
void simulate(float deltaTime, bool delayLoad = false);
|
||||
void simulate(float deltaTime, bool fullUpdate = true);
|
||||
bool render(float alpha);
|
||||
|
||||
/// Sets the URL of the model to render.
|
||||
|
@ -226,6 +226,9 @@ protected:
|
|||
|
||||
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.
|
||||
virtual void updateJointState(int index);
|
||||
|
||||
|
@ -256,7 +259,6 @@ protected:
|
|||
|
||||
private:
|
||||
|
||||
QVector<JointState> updateGeometry(bool delayLoad);
|
||||
void applyNextGeometry();
|
||||
void deleteGeometry();
|
||||
void renderMeshes(float alpha, bool translucent);
|
||||
|
|
|
@ -29,7 +29,7 @@ BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthMeter* model) :
|
|||
this->QDialog::setLayout(form);
|
||||
|
||||
// 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;
|
||||
BandwidthMeter::ChannelInfo& ch = _model->channelInfo(BandwidthMeter::ChannelIndex(i / 2));
|
||||
QLabel* label = _labels[i] = new QLabel();
|
||||
|
@ -48,7 +48,7 @@ BandwidthDialog::BandwidthDialog(QWidget* parent, BandwidthMeter* model) :
|
|||
}
|
||||
|
||||
BandwidthDialog::~BandwidthDialog() {
|
||||
for (int i = 0; i < BandwidthMeter::N_STREAMS; ++i) {
|
||||
for (size_t i = 0; i < BandwidthMeter::N_STREAMS; ++i) {
|
||||
delete _labels[i];
|
||||
}
|
||||
}
|
||||
|
@ -57,7 +57,7 @@ void BandwidthDialog::paintEvent(QPaintEvent* event) {
|
|||
|
||||
// Update labels
|
||||
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);
|
||||
bool input = i % 2 == 0;
|
||||
BandwidthMeter::ChannelInfo& ch = _model->channelInfo(chIdx);
|
||||
|
|
|
@ -28,17 +28,21 @@ const int NUM_MESSAGES_TO_TIME_STAMP = 20;
|
|||
const QRegularExpression regexLinks("((?:(?:ftp)|(?:https?))://\\S+)");
|
||||
|
||||
ChatWindow::ChatWindow() :
|
||||
QWidget(),
|
||||
ui(new Ui::ChatWindow),
|
||||
numMessagesAfterLastTimeStamp(0)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
QWidget* widget = new QWidget();
|
||||
setWidget(widget);
|
||||
|
||||
ui->setupUi(widget);
|
||||
|
||||
FlowLayout* flowLayout = new FlowLayout(0, 4, 4);
|
||||
ui->usersWidget->setLayout(flowLayout);
|
||||
|
||||
ui->messagePlainTextEdit->installEventFilter(this);
|
||||
|
||||
ui->closeButton->hide();
|
||||
|
||||
#ifdef HAVE_QXMPP
|
||||
const QXmppClient& xmppClient = XmppClient::getInstance().getXMPPClient();
|
||||
if (xmppClient.isConnected()) {
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
#define __interface__ChatWindow__
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QDockWidget>
|
||||
#include <QTimer>
|
||||
#include <QWidget>
|
||||
|
||||
#include <Application.h>
|
||||
|
||||
|
@ -26,7 +26,7 @@ namespace Ui {
|
|||
class ChatWindow;
|
||||
}
|
||||
|
||||
class ChatWindow : public QWidget {
|
||||
class ChatWindow : public QDockWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
|
|
@ -41,7 +41,7 @@ void LocalVoxelsOverlay::update(float deltatime) {
|
|||
}
|
||||
|
||||
_tree->lockForRead();
|
||||
if (_visible && _voxelCount != _tree->getOctreeElementsCount()) {
|
||||
if (_visible && _voxelCount != (int)_tree->getOctreeElementsCount()) {
|
||||
_voxelCount = _tree->getOctreeElementsCount();
|
||||
_voxelSystem->forceRedrawEntireTree();
|
||||
}
|
||||
|
|
|
@ -25,10 +25,6 @@ HandData::HandData(AvatarData* owningAvatar) :
|
|||
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 {
|
||||
return glm::inverse(getBaseOrientation()) * worldVector / LEAP_UNIT_SCALE;
|
||||
}
|
||||
|
@ -272,9 +268,7 @@ glm::quat HandData::getBaseOrientation() const {
|
|||
}
|
||||
|
||||
glm::vec3 HandData::getBasePosition() const {
|
||||
const glm::vec3 LEAP_HANDS_OFFSET_FROM_TORSO(0.0, 0.3, -0.3);
|
||||
return _owningAvatarData->getPosition() + getBaseOrientation() * LEAP_HANDS_OFFSET_FROM_TORSO *
|
||||
_owningAvatarData->getTargetScale();
|
||||
return _owningAvatarData->getPosition();
|
||||
}
|
||||
|
||||
void FingerData::setTrailLength(unsigned int length) {
|
||||
|
|
|
@ -28,13 +28,6 @@ const int NUM_FINGERS = NUM_HANDS * NUM_FINGERS_PER_HAND;
|
|||
const int LEAPID_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 int SIXENSE_CONTROLLER_ID_LEFT_HAND = 0;
|
||||
|
@ -55,7 +48,6 @@ public:
|
|||
glm::vec3 leapDirectionToWorldDirection(const glm::vec3& leapDirection) {
|
||||
return getBaseOrientation() * leapDirection;
|
||||
}
|
||||
glm::vec3 worldPositionToLeapPosition(const glm::vec3& worldPosition) const;
|
||||
glm::vec3 worldVectorToLeapVector(const glm::vec3& worldVector) const;
|
||||
|
||||
std::vector<PalmData>& getPalms() { return _palms; }
|
||||
|
@ -182,11 +174,11 @@ public:
|
|||
int getFramesWithoutData() const { return _numFramesWithoutData; }
|
||||
|
||||
// Controller buttons
|
||||
void setControllerButtons(int controllerButtons) { _controllerButtons = controllerButtons; }
|
||||
void setLastControllerButtons(int controllerButtons) { _lastControllerButtons = controllerButtons; }
|
||||
void setControllerButtons(unsigned int controllerButtons) { _controllerButtons = controllerButtons; }
|
||||
void setLastControllerButtons(unsigned int controllerButtons) { _lastControllerButtons = controllerButtons; }
|
||||
|
||||
int getControllerButtons() const { return _controllerButtons; }
|
||||
int getLastControllerButtons() const { return _lastControllerButtons; }
|
||||
unsigned int getControllerButtons() const { return _controllerButtons; }
|
||||
unsigned int getLastControllerButtons() const { return _lastControllerButtons; }
|
||||
|
||||
void setTrigger(float trigger) { _trigger = trigger; }
|
||||
float getTrigger() const { return _trigger; }
|
||||
|
@ -217,8 +209,8 @@ private:
|
|||
|
||||
glm::vec3 _tipPosition;
|
||||
glm::vec3 _tipVelocity;
|
||||
int _controllerButtons;
|
||||
int _lastControllerButtons;
|
||||
unsigned int _controllerButtons;
|
||||
unsigned int _lastControllerButtons;
|
||||
float _trigger;
|
||||
float _joystickX, _joystickY;
|
||||
|
||||
|
|
|
@ -925,7 +925,7 @@ void OctreeSceneStats::trackIncomingOctreePacket(const QByteArray& packet,
|
|||
qDebug() << "(_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) {
|
||||
qDebug() << "pruning really old missing sequence:" << missingItem;
|
||||
}
|
||||
|
|
|
@ -22,14 +22,22 @@ glm::quat Quat::multiply(const glm::quat& q1, const glm::quat& 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));
|
||||
}
|
||||
|
||||
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)));
|
||||
}
|
||||
|
||||
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) {
|
||||
return glm::inverse(q);
|
||||
}
|
||||
|
|
|
@ -23,8 +23,10 @@ class Quat : public QObject {
|
|||
|
||||
public slots:
|
||||
glm::quat multiply(const glm::quat& q1, const glm::quat& q2);
|
||||
glm::quat fromVec3(const glm::vec3& vec3);
|
||||
glm::quat fromPitchYawRoll(float pitch, float yaw, float roll); // degrees
|
||||
glm::quat fromVec3Degrees(const glm::vec3& vec3); // 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::vec3 getFront(const glm::quat& orientation);
|
||||
glm::vec3 getRight(const glm::quat& orientation);
|
||||
|
|
|
@ -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 SQUARE_ROOT_OF_2 = (float)sqrt(2.f);
|
||||
static const float SQUARE_ROOT_OF_3 = (float)sqrt(3.f);
|
||||
static const float METER = 1.0f;
|
||||
static const float DECIMETER = 0.1f;
|
||||
static const float CENTIMETER = 0.01f;
|
||||
static const float MILLIIMETER = 0.001f;
|
||||
static const float METERS_PER_DECIMETER = 0.1f;
|
||||
static const float METERS_PER_CENTIMETER = 0.01f;
|
||||
static const float METERS_PER_MILLIMETER = 0.001f;
|
||||
static const quint64 USECS_PER_MSEC = 1000;
|
||||
static const quint64 MSECS_PER_SECOND = 1000;
|
||||
static const quint64 USECS_PER_SECOND = USECS_PER_MSEC * MSECS_PER_SECOND;
|
||||
|
|
|
@ -174,7 +174,7 @@ void ShapeColliderTests::sphereMissesCapsule() {
|
|||
CapsuleShape capsuleB(radiusB, halfHeightB);
|
||||
|
||||
// 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::quat rotation = glm::angleAxis(angle, axis);
|
||||
glm::vec3 translation(15.1f, -27.1f, -38.6f);
|
||||
|
|
Loading…
Reference in a new issue