3
0
Fork 0
mirror of https://github.com/lubosz/overte.git synced 2025-04-27 04:55:32 +02:00
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) {
yaw += 360;
}
var orientation = Quat.fromPitchYawRoll(pitch, yaw, roll);
var orientation = Quat.fromPitchYawRollDegrees(pitch, yaw, roll);
Camera.setOrientation(orientation);
}
}

View file

@ -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))));
});

View file

@ -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;

View file

@ -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

View file

@ -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 + ","

View file

@ -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 + ","

View file

@ -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 + ","

View file

@ -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;
}

View file

@ -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>

View file

@ -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();

View file

@ -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;

View file

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

View file

@ -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
}

View file

@ -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";

View file

@ -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;
}

View file

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

View file

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

View file

@ -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)) {

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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;

View file

@ -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);

View file

@ -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

View file

@ -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;
};

View file

@ -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
}

View file

@ -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;

View file

@ -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();

View file

@ -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);

View file

@ -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);

View file

@ -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()) {

View file

@ -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:

View file

@ -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();
}

View file

@ -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) {

View file

@ -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;

View file

@ -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;
}

View file

@ -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);
}

View file

@ -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);

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 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;

View file

@ -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);