Merge branch 'master' of https://github.com/highfidelity/hifi into point_and_glow

This commit is contained in:
Atlante45 2014-05-23 11:32:52 -07:00
commit 7b9f6f8505
21 changed files with 464 additions and 108 deletions

View file

@ -158,10 +158,11 @@ bool OctreeQueryNode::shouldSuppressDuplicatePacket() {
void OctreeQueryNode::init() { void OctreeQueryNode::init() {
_myPacketType = getMyPacketType(); _myPacketType = getMyPacketType();
resetOctreePacket(true); // don't bump sequence resetOctreePacket(); // don't bump sequence
} }
void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
void OctreeQueryNode::resetOctreePacket() {
// if shutting down, return immediately // if shutting down, return immediately
if (_isShuttingDown) { if (_isShuttingDown) {
return; return;
@ -202,9 +203,6 @@ void OctreeQueryNode::resetOctreePacket(bool lastWasSurpressed) {
*sequenceAt = _sequenceNumber; *sequenceAt = _sequenceNumber;
_octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE); _octreePacketAt += sizeof(OCTREE_PACKET_SEQUENCE);
_octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE); _octreePacketAvailableBytes -= sizeof(OCTREE_PACKET_SEQUENCE);
if (!(lastWasSurpressed || _lastOctreePacketLength == (numBytesPacketHeader + OCTREE_PACKET_EXTRA_HEADERS_SIZE))) {
_sequenceNumber++;
}
// pack in timestamp // pack in timestamp
OCTREE_PACKET_SENT_TIME now = usecTimestampNow(); OCTREE_PACKET_SENT_TIME now = usecTimestampNow();
@ -365,3 +363,6 @@ void OctreeQueryNode::dumpOutOfView() {
} }
} }
void OctreeQueryNode::incrementSequenceNumber() {
_sequenceNumber++;
}

View file

@ -35,7 +35,7 @@ public:
void init(); // called after creation to set up some virtual items void init(); // called after creation to set up some virtual items
virtual PacketType getMyPacketType() const = 0; virtual PacketType getMyPacketType() const = 0;
void resetOctreePacket(bool lastWasSurpressed = false); // resets octree packet to after "V" header void resetOctreePacket(); // resets octree packet to after "V" header
void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet void writeToPacket(const unsigned char* buffer, unsigned int bytes); // writes to end of packet
@ -100,6 +100,8 @@ public:
void forceNodeShutdown(); void forceNodeShutdown();
bool isShuttingDown() const { return _isShuttingDown; } bool isShuttingDown() const { return _isShuttingDown; }
void incrementSequenceNumber();
private slots: private slots:
void sendThreadFinished(); void sendThreadFinished();
@ -137,6 +139,7 @@ private:
bool _lodInitialized; bool _lodInitialized;
OCTREE_PACKET_SEQUENCE _sequenceNumber; OCTREE_PACKET_SEQUENCE _sequenceNumber;
quint64 _lastRootTimestamp; quint64 _lastRootTimestamp;
PacketType _myPacketType; PacketType _myPacketType;

View file

@ -9,8 +9,6 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QMutexLocker>
#include <NodeList.h> #include <NodeList.h>
#include <PacketHeaders.h> #include <PacketHeaders.h>
#include <PerfStat.h> #include <PerfStat.h>
@ -139,7 +137,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
// obscure the packet and not send it. This allows the callers and upper level logic to not need to know about // obscure the packet and not send it. This allows the callers and upper level logic to not need to know about
// this rate control savings. // this rate control savings.
if (nodeData->shouldSuppressDuplicatePacket()) { if (nodeData->shouldSuppressDuplicatePacket()) {
nodeData->resetOctreePacket(true); // we still need to reset it though! nodeData->resetOctreePacket(); // we still need to reset it though!
return packetsSent; // without sending... return packetsSent; // without sending...
} }
@ -244,6 +242,7 @@ int OctreeSendThread::handlePacketSend(OctreeQueryNode* nodeData, int& trueBytes
trueBytesSent += nodeData->getPacketLength(); trueBytesSent += nodeData->getPacketLength();
truePacketsSent++; truePacketsSent++;
packetsSent++; packetsSent++;
nodeData->incrementSequenceNumber();
nodeData->resetOctreePacket(); nodeData->resetOctreePacket();
} }

View file

@ -218,6 +218,8 @@ bool DomainServer::optionallySetupAssignmentPayment() {
} }
} }
qDebug() << "Assignments will be paid for via" << qPrintable(_oauthProviderURL.toString());
// assume that the fact we are authing against HF data server means we will pay for assignments // assume that the fact we are authing against HF data server means we will pay for assignments
// setup a timer to send transactions to pay assigned nodes every 30 seconds // setup a timer to send transactions to pay assigned nodes every 30 seconds
QTimer* creditSetupTimer = new QTimer(this); QTimer* creditSetupTimer = new QTimer(this);
@ -731,10 +733,11 @@ void DomainServer::setupPendingAssignmentCredits() {
qint64 elapsedMsecsSinceLastPayment = nodeData->getPaymentIntervalTimer().elapsed(); qint64 elapsedMsecsSinceLastPayment = nodeData->getPaymentIntervalTimer().elapsed();
nodeData->getPaymentIntervalTimer().restart(); nodeData->getPaymentIntervalTimer().restart();
const float CREDITS_PER_HOUR = 3; const float CREDITS_PER_HOUR = 0.10f;
const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000); const float CREDITS_PER_MSEC = CREDITS_PER_HOUR / (60 * 60 * 1000);
const int SATOSHIS_PER_MSEC = CREDITS_PER_MSEC * powf(10.0f, 8.0f);
float pendingCredits = elapsedMsecsSinceLastPayment * CREDITS_PER_MSEC; float pendingCredits = elapsedMsecsSinceLastPayment * SATOSHIS_PER_MSEC;
if (existingTransaction) { if (existingTransaction) {
existingTransaction->incrementAmount(pendingCredits); existingTransaction->incrementAmount(pendingCredits);

View file

@ -26,6 +26,7 @@ var THRUST_INCREASE_RATE = 1.05;
var MAX_THRUST_MULTIPLIER = 75.0; var MAX_THRUST_MULTIPLIER = 75.0;
var thrustMultiplier = INITIAL_THRUST_MULTPLIER; var thrustMultiplier = INITIAL_THRUST_MULTPLIER;
var grabDelta = { x: 0, y: 0, z: 0}; var grabDelta = { x: 0, y: 0, z: 0};
var grabStartPosition = { x: 0, y: 0, z: 0};
var grabDeltaVelocity = { x: 0, y: 0, z: 0}; var grabDeltaVelocity = { x: 0, y: 0, z: 0};
var grabStartRotation = { x: 0, y: 0, z: 0, w: 1}; var grabStartRotation = { x: 0, y: 0, z: 0, w: 1};
var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1}; var grabCurrentRotation = { x: 0, y: 0, z: 0, w: 1};
@ -50,7 +51,7 @@ var JOYSTICK_PITCH_MAG = PITCH_MAG * 0.5;
var LEFT_PALM = 0; var LEFT_PALM = 0;
var LEFT_BUTTON_4 = 5; var LEFT_BUTTON_4 = 4;
var LEFT_BUTTON_FWD = 5; var LEFT_BUTTON_FWD = 5;
var RIGHT_PALM = 2; var RIGHT_PALM = 2;
var RIGHT_BUTTON_4 = 10; var RIGHT_BUTTON_4 = 10;
@ -63,6 +64,63 @@ function printVector(text, v, decimals) {
} }
var debug = false; var debug = false;
var RED_COLOR = { red: 255, green: 0, blue: 0 };
var GRAY_COLOR = { red: 25, green: 25, blue: 25 };
var defaultPosition = { x: 0, y: 0, z: 0};
var RADIUS = 0.05;
var greenSphere = -1;
var redSphere = -1;
function createDebugOverlay() {
if (greenSphere == -1) {
greenSphere = Overlays.addOverlay("sphere", {
position: defaultPosition,
size: RADIUS,
color: GRAY_COLOR,
alpha: 1,
visible: true,
solid: true,
anchor: "MyAvatar"
});
redSphere = Overlays.addOverlay("sphere", {
position: defaultPosition,
size: RADIUS,
color: RED_COLOR,
alpha: 1,
visible: true,
solid: true,
anchor: "MyAvatar"
});
}
}
function destroyDebugOverlay() {
if (greenSphere != -1) {
Overlays.deleteOverlay(greenSphere);
Overlays.deleteOverlay(redSphere);
greenSphere = -1;
redSphere = -1;
}
}
function displayDebug() {
if (!(grabbingWithRightHand || grabbingWithLeftHand)) {
if (greenSphere != -1) {
destroyDebugOverlay();
}
} else {
// update debug indicator
if (greenSphere == -1) {
createDebugOverlay();
}
var displayOffset = { x:0, y:0.5, z:-0.5 };
Overlays.editOverlay(greenSphere, { position: Vec3.sum(grabStartPosition, displayOffset) } );
Overlays.editOverlay(redSphere, { position: Vec3.sum(Vec3.sum(grabStartPosition, grabDelta), displayOffset), size: RADIUS + (0.25 * Vec3.length(grabDelta)) } );
}
}
function getJoystickPosition(palm) { function getJoystickPosition(palm) {
// returns CONTROLLER_ID position in avatar local frame // returns CONTROLLER_ID position in avatar local frame
@ -220,6 +278,8 @@ function flyWithHydra(deltaTime) {
MyAvatar.headPitch = newPitch; MyAvatar.headPitch = newPitch;
} }
handleGrabBehavior(deltaTime); handleGrabBehavior(deltaTime);
displayDebug();
} }
Script.update.connect(flyWithHydra); Script.update.connect(flyWithHydra);

View file

@ -11,7 +11,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
var alwaysLook = true; // if you want the mouse look to happen only when you click, change this to false var alwaysLook = false; // if you want the mouse look to happen only when you click, change this to false
var isMouseDown = false; var isMouseDown = false;
var lastX = 0; var lastX = 0;
var lastY = 0; var lastY = 0;

View file

@ -237,7 +237,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&))); connect(&nodeList->getDomainHandler(), SIGNAL(connectedToDomain(const QString&)), SLOT(connectedToDomain(const QString&)));
// update our location every 5 seconds in the data-server, assuming that we are authenticated with one // update our location every 5 seconds in the data-server, assuming that we are authenticated with one
const float DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5.0f * 1000.0f; const qint64 DATA_SERVER_LOCATION_CHANGE_UPDATE_MSECS = 5 * 1000;
QTimer* locationUpdateTimer = new QTimer(this); QTimer* locationUpdateTimer = new QTimer(this);
connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer); connect(locationUpdateTimer, &QTimer::timeout, this, &Application::updateLocationInServer);
@ -254,6 +254,15 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
// connect to appropriate slots on AccountManager // connect to appropriate slots on AccountManager
AccountManager& accountManager = AccountManager::getInstance(); AccountManager& accountManager = AccountManager::getInstance();
const qint64 BALANCE_UPDATE_INTERVAL_MSECS = 5 * 1000;
QTimer* balanceUpdateTimer = new QTimer(this);
connect(balanceUpdateTimer, &QTimer::timeout, &accountManager, &AccountManager::updateBalance);
balanceUpdateTimer->start(BALANCE_UPDATE_INTERVAL_MSECS);
connect(&accountManager, &AccountManager::balanceChanged, this, &Application::updateWindowTitle);
connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain); connect(&accountManager, &AccountManager::authRequired, Menu::getInstance(), &Menu::loginForCurrentDomain);
connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle); connect(&accountManager, &AccountManager::usernameChanged, this, &Application::updateWindowTitle);
@ -3094,6 +3103,17 @@ void Application::updateWindowTitle(){
QString username = AccountManager::getInstance().getAccountInfo().getUsername(); QString username = AccountManager::getInstance().getAccountInfo().getUsername();
QString title = QString() + (!username.isEmpty() ? username + " @ " : QString()) QString title = QString() + (!username.isEmpty() ? username + " @ " : QString())
+ nodeList->getDomainHandler().getHostname() + buildVersion; + nodeList->getDomainHandler().getHostname() + buildVersion;
AccountManager& accountManager = AccountManager::getInstance();
if (accountManager.getAccountInfo().hasBalance()) {
float creditBalance = accountManager.getAccountInfo().getBalance() * pow(10.0f, -8.0f);
QString creditBalanceString;
creditBalanceString.sprintf("%.8f", creditBalance);
title += " - ₵" + creditBalanceString;
}
qDebug("Application title set to: %s", title.toStdString().c_str()); qDebug("Application title set to: %s", title.toStdString().c_str());
_window->setWindowTitle(title); _window->setWindowTitle(title);
} }

View file

@ -17,7 +17,7 @@ const float MINIMUM_ATTENUATION_TO_REFLECT = 1.0f / 256.0f;
const float DEFAULT_DISTANCE_SCALING_FACTOR = 2.0f; const float DEFAULT_DISTANCE_SCALING_FACTOR = 2.0f;
const float MAXIMUM_DELAY_MS = 1000.0 * 20.0f; // stop reflecting after path is this long const float MAXIMUM_DELAY_MS = 1000.0 * 20.0f; // stop reflecting after path is this long
const int DEFAULT_DIFFUSION_FANOUT = 5; const int DEFAULT_DIFFUSION_FANOUT = 5;
const int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10; const unsigned int ABSOLUTE_MAXIMUM_BOUNCE_COUNT = 10;
const float DEFAULT_LOCAL_ATTENUATION_FACTOR = 0.125; const float DEFAULT_LOCAL_ATTENUATION_FACTOR = 0.125;
const float DEFAULT_COMB_FILTER_WINDOW = 0.05f; //ms delay differential to avoid const float DEFAULT_COMB_FILTER_WINDOW = 0.05f; //ms delay differential to avoid

View file

@ -430,8 +430,6 @@ void MyAvatar::setGravity(const glm::vec3& gravity) {
AnimationHandlePointer MyAvatar::addAnimationHandle() { AnimationHandlePointer MyAvatar::addAnimationHandle() {
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
handle->setLoop(true);
handle->start();
_animationHandles.append(handle); _animationHandles.append(handle);
return handle; return handle;
} }
@ -441,10 +439,12 @@ void MyAvatar::removeAnimationHandle(const AnimationHandlePointer& handle) {
_animationHandles.removeOne(handle); _animationHandles.removeOne(handle);
} }
void MyAvatar::startAnimation(const QString& url, float fps, float priority, bool loop, const QStringList& maskedJoints) { void MyAvatar::startAnimation(const QString& url, float fps, float priority,
bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), QMetaObject::invokeMethod(this, "startAnimation", Q_ARG(const QString&, url), Q_ARG(float, fps),
Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(const QStringList&, maskedJoints)); Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(int, firstFrame),
Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return; return;
} }
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle(); AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
@ -452,10 +452,54 @@ void MyAvatar::startAnimation(const QString& url, float fps, float priority, boo
handle->setFPS(fps); handle->setFPS(fps);
handle->setPriority(priority); handle->setPriority(priority);
handle->setLoop(loop); handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints); handle->setMaskedJoints(maskedJoints);
handle->start(); handle->start();
} }
void MyAvatar::startAnimationByRole(const QString& role, const QString& url, float fps, float priority,
bool loop, bool hold, int firstFrame, int lastFrame, const QStringList& maskedJoints) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "startAnimationByRole", Q_ARG(const QString&, role), Q_ARG(const QString&, url),
Q_ARG(float, fps), Q_ARG(float, priority), Q_ARG(bool, loop), Q_ARG(bool, hold), Q_ARG(int, firstFrame),
Q_ARG(int, lastFrame), Q_ARG(const QStringList&, maskedJoints));
return;
}
// check for a configured animation for the role
foreach (const AnimationHandlePointer& handle, _animationHandles) {
if (handle->getRole() == role) {
handle->start();
return;
}
}
// no joy; use the parameters provided
AnimationHandlePointer handle = _skeletonModel.createAnimationHandle();
handle->setRole(role);
handle->setURL(url);
handle->setFPS(fps);
handle->setPriority(priority);
handle->setLoop(loop);
handle->setHold(hold);
handle->setFirstFrame(firstFrame);
handle->setLastFrame(lastFrame);
handle->setMaskedJoints(maskedJoints);
handle->start();
}
void MyAvatar::stopAnimationByRole(const QString& role) {
if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopAnimationByRole", Q_ARG(const QString&, role));
return;
}
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
if (handle->getRole() == role) {
handle->stop();
}
}
}
void MyAvatar::stopAnimation(const QString& url) { void MyAvatar::stopAnimation(const QString& url) {
if (QThread::currentThread() != thread()) { if (QThread::currentThread() != thread()) {
QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url)); QMetaObject::invokeMethod(this, "stopAnimation", Q_ARG(const QString&, url));
@ -464,7 +508,6 @@ void MyAvatar::stopAnimation(const QString& url) {
foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) { foreach (const AnimationHandlePointer& handle, _skeletonModel.getRunningAnimations()) {
if (handle->getURL() == url) { if (handle->getURL() == url) {
handle->stop(); handle->stop();
return;
} }
} }
} }
@ -511,9 +554,15 @@ void MyAvatar::saveData(QSettings* settings) {
for (int i = 0; i < _animationHandles.size(); i++) { for (int i = 0; i < _animationHandles.size(); i++) {
settings->setArrayIndex(i); settings->setArrayIndex(i);
const AnimationHandlePointer& pointer = _animationHandles.at(i); const AnimationHandlePointer& pointer = _animationHandles.at(i);
settings->setValue("role", pointer->getRole());
settings->setValue("url", pointer->getURL()); settings->setValue("url", pointer->getURL());
settings->setValue("fps", pointer->getFPS()); settings->setValue("fps", pointer->getFPS());
settings->setValue("priority", pointer->getPriority()); settings->setValue("priority", pointer->getPriority());
settings->setValue("loop", pointer->getLoop());
settings->setValue("hold", pointer->getHold());
settings->setValue("startAutomatically", pointer->getStartAutomatically());
settings->setValue("firstFrame", pointer->getFirstFrame());
settings->setValue("lastFrame", pointer->getLastFrame());
settings->setValue("maskedJoints", pointer->getMaskedJoints()); settings->setValue("maskedJoints", pointer->getMaskedJoints());
} }
settings->endArray(); settings->endArray();
@ -578,9 +627,15 @@ void MyAvatar::loadData(QSettings* settings) {
for (int i = 0; i < animationCount; i++) { for (int i = 0; i < animationCount; i++) {
settings->setArrayIndex(i); settings->setArrayIndex(i);
const AnimationHandlePointer& handle = _animationHandles.at(i); const AnimationHandlePointer& handle = _animationHandles.at(i);
handle->setRole(settings->value("role", "idle").toString());
handle->setURL(settings->value("url").toUrl()); handle->setURL(settings->value("url").toUrl());
handle->setFPS(loadSetting(settings, "fps", 30.0f)); handle->setFPS(loadSetting(settings, "fps", 30.0f));
handle->setPriority(loadSetting(settings, "priority", 1.0f)); handle->setPriority(loadSetting(settings, "priority", 1.0f));
handle->setLoop(settings->value("loop", true).toBool());
handle->setHold(settings->value("hold", false).toBool());
handle->setStartAutomatically(settings->value("startAutomatically", true).toBool());
handle->setFirstFrame(settings->value("firstFrame", 0).toInt());
handle->setLastFrame(settings->value("lastFrame", INT_MAX).toInt());
handle->setMaskedJoints(settings->value("maskedJoints").toStringList()); handle->setMaskedJoints(settings->value("maskedJoints").toStringList());
} }
settings->endArray(); settings->endArray();
@ -698,17 +753,19 @@ glm::vec3 MyAvatar::getUprightHeadPosition() const {
return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f); return _position + getWorldAlignedOrientation() * glm::vec3(0.0f, getPelvisToHeadLength(), 0.0f);
} }
const float JOINT_PRIORITY = 2.0f;
void MyAvatar::setJointData(int index, const glm::quat& rotation) { void MyAvatar::setJointData(int index, const glm::quat& rotation) {
Avatar::setJointData(index, rotation); Avatar::setJointData(index, rotation);
if (QThread::currentThread() == thread()) { if (QThread::currentThread() == thread()) {
_skeletonModel.setJointState(index, true, rotation); _skeletonModel.setJointState(index, true, rotation, JOINT_PRIORITY);
} }
} }
void MyAvatar::clearJointData(int index) { void MyAvatar::clearJointData(int index) {
Avatar::clearJointData(index); Avatar::clearJointData(index);
if (QThread::currentThread() == thread()) { if (QThread::currentThread() == thread()) {
_skeletonModel.setJointState(index, false); _skeletonModel.setJointState(index, false, glm::quat(), JOINT_PRIORITY);
} }
} }

View file

@ -67,12 +67,21 @@ public:
void removeAnimationHandle(const AnimationHandlePointer& handle); void removeAnimationHandle(const AnimationHandlePointer& handle);
/// Allows scripts to run animations. /// Allows scripts to run animations.
Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, Q_INVOKABLE void startAnimation(const QString& url, float fps = 30.0f, float priority = 1.0f, bool loop = false,
float priority = 1.0f, bool loop = false, const QStringList& maskedJoints = QStringList()); bool hold = false, int firstFrame = 0, int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList());
/// Stops an animation as identified by a URL. /// Stops an animation as identified by a URL.
Q_INVOKABLE void stopAnimation(const QString& url); Q_INVOKABLE void stopAnimation(const QString& url);
/// Starts an animation by its role, using the provided URL and parameters if the avatar doesn't have a custom
/// animation for the role.
Q_INVOKABLE void startAnimationByRole(const QString& role, const QString& url = QString(), float fps = 30.0f,
float priority = 1.0f, bool loop = false, bool hold = false, int firstFrame = 0,
int lastFrame = INT_MAX, const QStringList& maskedJoints = QStringList());
/// Stops an animation identified by its role.
Q_INVOKABLE void stopAnimationByRole(const QString& role);
// get/set avatar data // get/set avatar data
void saveData(QSettings* settings); void saveData(QSettings* settings);
void loadData(QSettings* settings); void loadData(QSettings* settings);

View file

@ -21,6 +21,8 @@ SkeletonModel::SkeletonModel(Avatar* owningAvatar) :
_owningAvatar(owningAvatar) { _owningAvatar(owningAvatar) {
} }
const float PALM_PRIORITY = 3.0f;
void SkeletonModel::simulate(float deltaTime, bool fullUpdate) { void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
setTranslation(_owningAvatar->getPosition()); setTranslation(_owningAvatar->getPosition());
setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f))); setRotation(_owningAvatar->getOrientation() * glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)));
@ -43,7 +45,7 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
} }
int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex); int jointIndex = geometry.humanIKJointIndices.at(humanIKJointIndex);
if (jointIndex != -1) { if (jointIndex != -1) {
setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true); setJointRotation(jointIndex, _rotation * prioVR->getJointRotations().at(i), true, PALM_PRIORITY);
} }
} }
return; return;
@ -58,16 +60,16 @@ void SkeletonModel::simulate(float deltaTime, bool fullUpdate) {
if (leftPalmIndex == -1) { if (leftPalmIndex == -1) {
// palms are not yet set, use mouse // palms are not yet set, use mouse
if (_owningAvatar->getHandState() == HAND_STATE_NULL) { if (_owningAvatar->getHandState() == HAND_STATE_NULL) {
restoreRightHandPosition(HAND_RESTORATION_RATE); restoreRightHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else { } else {
applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition()); applyHandPosition(geometry.rightHandJointIndex, _owningAvatar->getHandPosition());
} }
restoreLeftHandPosition(HAND_RESTORATION_RATE); restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else if (leftPalmIndex == rightPalmIndex) { } else if (leftPalmIndex == rightPalmIndex) {
// right hand only // right hand only
applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]); applyPalmData(geometry.rightHandJointIndex, hand->getPalms()[leftPalmIndex]);
restoreLeftHandPosition(HAND_RESTORATION_RATE); restoreLeftHandPosition(HAND_RESTORATION_RATE, PALM_PRIORITY);
} else { } else {
applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]); applyPalmData(geometry.leftHandJointIndex, hand->getPalms()[leftPalmIndex]);
@ -132,7 +134,7 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
if (jointIndex == -1) { if (jointIndex == -1) {
return; return;
} }
setJointPosition(jointIndex, position); setJointPosition(jointIndex, position, glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
const FBXGeometry& geometry = _geometry->getFBXGeometry(); const FBXGeometry& geometry = _geometry->getFBXGeometry();
glm::vec3 handPosition, elbowPosition; glm::vec3 handPosition, elbowPosition;
@ -148,7 +150,8 @@ void SkeletonModel::applyHandPosition(int jointIndex, const glm::vec3& position)
// align hand with forearm // align hand with forearm
float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f; float sign = (jointIndex == geometry.rightHandJointIndex) ? 1.0f : -1.0f;
applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f), forearmVector)); applyRotationDelta(jointIndex, rotationBetween(handRotation * glm::vec3(-sign, 0.0f, 0.0f),
forearmVector), true, PALM_PRIORITY);
} }
void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
@ -183,12 +186,14 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) {
} else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) { } else if (Menu::getInstance()->isOptionChecked(MenuOption::AlignForearmsWithWrists)) {
glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f); glm::vec3 forearmVector = palmRotation * glm::vec3(sign, 0.0f, 0.0f);
setJointPosition(parentJointIndex, palm.getPosition() + forearmVector * setJointPosition(parentJointIndex, palm.getPosition() + forearmVector *
geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale)); geometry.joints.at(jointIndex).distanceToParent * extractUniformScale(_scale),
setJointRotation(parentJointIndex, palmRotation, true); glm::quat(), false, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
setJointRotation(parentJointIndex, palmRotation, true, PALM_PRIORITY);
_jointStates[jointIndex].rotation = glm::quat(); _jointStates[jointIndex].rotation = glm::quat();
} else { } else {
setJointPosition(jointIndex, palm.getPosition(), palmRotation, true); setJointPosition(jointIndex, palm.getPosition(), palmRotation,
true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY);
} }
} }
@ -335,11 +340,11 @@ void SkeletonModel::setHandPosition(int jointIndex, const glm::vec3& position, c
glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f); glm::vec3 forwardVector(rightHand ? -1.0f : 1.0f, 0.0f, 0.0f);
glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition); glm::quat shoulderRotation = rotationBetween(forwardVector, elbowPosition - shoulderPosition);
setJointRotation(shoulderJointIndex, shoulderRotation, true); setJointRotation(shoulderJointIndex, shoulderRotation, true, PALM_PRIORITY);
setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector, setJointRotation(elbowJointIndex, rotationBetween(shoulderRotation * forwardVector,
wristPosition - elbowPosition) * shoulderRotation, true); wristPosition - elbowPosition) * shoulderRotation, true, PALM_PRIORITY);
setJointRotation(jointIndex, rotation, true); setJointRotation(jointIndex, rotation, true, PALM_PRIORITY);
} }

View file

@ -120,7 +120,6 @@ void SixenseManager::update(float deltaTime) {
// Rotation of Palm // Rotation of Palm
glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]); glm::quat rotation(data->rot_quat[3], -data->rot_quat[0], data->rot_quat[1], -data->rot_quat[2]);
rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation; rotation = glm::angleAxis(PI, glm::vec3(0.f, 1.f, 0.f)) * _orbRotation * rotation;
palm->setRawRotation(rotation);
// Compute current velocity from position change // Compute current velocity from position change
glm::vec3 rawVelocity; glm::vec3 rawVelocity;
@ -130,7 +129,12 @@ void SixenseManager::update(float deltaTime) {
rawVelocity = glm::vec3(0.0f); rawVelocity = glm::vec3(0.0f);
} }
palm->setRawVelocity(rawVelocity); // meters/sec palm->setRawVelocity(rawVelocity); // meters/sec
palm->setRawPosition(position);
// Use a velocity sensitive filter to damp small motions and preserve large ones with
// no latency.
float velocityFilter = glm::clamp(1.0f - glm::length(rawVelocity), 0.0f, 1.0f);
palm->setRawPosition(palm->getRawPosition() * velocityFilter + position * (1.0f - velocityFilter));
palm->setRawRotation(safeMix(palm->getRawRotation(), rotation, 1.0f - velocityFilter));
// use the velocity to determine whether there's any movement (if the hand isn't new) // use the velocity to determine whether there's any movement (if the hand isn't new)
const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f; const float MOVEMENT_DISTANCE_THRESHOLD = 0.003f;

View file

@ -41,8 +41,14 @@ Visage::Visage() :
_headOrigin(DEFAULT_HEAD_ORIGIN) { _headOrigin(DEFAULT_HEAD_ORIGIN) {
#ifdef HAVE_VISAGE #ifdef HAVE_VISAGE
_tracker = NULL; #ifdef WIN32
_data = NULL; QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage";
#else
QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc";
#endif
initializeLicenseManager(licensePath.data());
_tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg");
_data = new FaceData();
#endif #endif
} }
@ -173,19 +179,6 @@ void Visage::setEnabled(bool enabled) {
return; return;
} }
if ((_enabled = enabled)) { if ((_enabled = enabled)) {
if(_tracker == NULL && _data == NULL){
#ifdef WIN32
QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage";
#else
QByteArray licensePath = Application::resourcesPath().toLatin1() + "visage/license.vlc";
#endif
initializeLicenseManager(licensePath.data());
_tracker = new VisageTracker2(Application::resourcesPath().toLatin1() + "visage/tracker.cfg");
_data = new FaceData();
}
_tracker->trackFromCam(); _tracker->trackFromCam();
} else { } else {
_tracker->stop(); _tracker->stop();

View file

@ -118,7 +118,7 @@ QVector<Model::JointState> Model::createJointStates(const FBXGeometry& geometry)
JointState state; JointState state;
state.translation = joint.translation; state.translation = joint.translation;
state.rotation = joint.rotation; state.rotation = joint.rotation;
state.animationDisabled = false; state.animationPriority = 0.0f;
jointStates.append(state); jointStates.append(state);
} }
@ -473,9 +473,18 @@ bool Model::getJointState(int index, glm::quat& rotation) const {
glm::abs(rotation.w - defaultRotation.w) >= EPSILON; glm::abs(rotation.w - defaultRotation.w) >= EPSILON;
} }
void Model::setJointState(int index, bool valid, const glm::quat& rotation) { void Model::setJointState(int index, bool valid, const glm::quat& rotation, float priority) {
if (index != -1 && index < _jointStates.size()) { if (index != -1 && index < _jointStates.size()) {
_jointStates[index].rotation = valid ? rotation : _geometry->getFBXGeometry().joints.at(index).rotation; JointState& state = _jointStates[index];
if (priority >= state.animationPriority) {
if (valid) {
state.rotation = rotation;
state.animationPriority = priority;
} else if (priority == state.animationPriority) {
state.rotation = _geometry->getFBXGeometry().joints.at(index).rotation;
state.animationPriority = 0.0f;
}
}
} }
} }
@ -535,8 +544,8 @@ bool Model::getRightHandRotation(glm::quat& rotation) const {
return getJointRotation(getRightHandJointIndex(), rotation); return getJointRotation(getRightHandJointIndex(), rotation);
} }
bool Model::restoreLeftHandPosition(float percent) { bool Model::restoreLeftHandPosition(float percent, float priority) {
return restoreJointPosition(getLeftHandJointIndex(), percent); return restoreJointPosition(getLeftHandJointIndex(), percent, priority);
} }
bool Model::getLeftShoulderPosition(glm::vec3& position) const { bool Model::getLeftShoulderPosition(glm::vec3& position) const {
@ -547,8 +556,8 @@ float Model::getLeftArmLength() const {
return getLimbLength(getLeftHandJointIndex()); return getLimbLength(getLeftHandJointIndex());
} }
bool Model::restoreRightHandPosition(float percent) { bool Model::restoreRightHandPosition(float percent, float priority) {
return restoreJointPosition(getRightHandJointIndex(), percent); return restoreJointPosition(getRightHandJointIndex(), percent, priority);
} }
bool Model::getRightShoulderPosition(glm::vec3& position) const { bool Model::getRightShoulderPosition(glm::vec3& position) const {
@ -1114,7 +1123,7 @@ void Model::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint
} }
bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation, bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation, bool useRotation,
int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment) { int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) { if (jointIndex == -1 || _jointStates.isEmpty()) {
return false; return false;
} }
@ -1137,7 +1146,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
glm::quat endRotation; glm::quat endRotation;
if (useRotation) { if (useRotation) {
getJointRotation(jointIndex, endRotation, true); getJointRotation(jointIndex, endRotation, true);
applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation)); applyRotationDelta(jointIndex, rotation * glm::inverse(endRotation), priority);
getJointRotation(jointIndex, endRotation, true); getJointRotation(jointIndex, endRotation, true);
} }
@ -1181,7 +1190,7 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
1.0f / (combinedWeight + 1.0f)); 1.0f / (combinedWeight + 1.0f));
} }
} }
applyRotationDelta(index, combinedDelta); applyRotationDelta(index, combinedDelta, priority);
glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation); glm::quat actualDelta = state.combinedRotation * glm::inverse(oldCombinedRotation);
endPosition = actualDelta * jointVector + jointPosition; endPosition = actualDelta * jointVector + jointPosition;
if (useRotation) { if (useRotation) {
@ -1199,15 +1208,17 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& translation, const
return true; return true;
} }
bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind) { bool Model::setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) { if (jointIndex == -1 || _jointStates.isEmpty()) {
return false; return false;
} }
JointState& state = _jointStates[jointIndex]; JointState& state = _jointStates[jointIndex];
if (priority >= state.animationPriority) {
state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation * state.rotation = state.rotation * glm::inverse(state.combinedRotation) * rotation *
glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation : glm::inverse(fromBind ? _geometry->getFBXGeometry().joints.at(jointIndex).inverseBindRotation :
_geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation); _geometry->getFBXGeometry().joints.at(jointIndex).inverseDefaultRotation);
state.animationDisabled = true; state.animationPriority = priority;
}
return true; return true;
} }
@ -1228,7 +1239,7 @@ void Model::setJointTranslation(int jointIndex, const glm::vec3& translation) {
state.translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation; state.translation = glm::vec3(glm::inverse(parentTransform) * glm::vec4(translation, 1.0f)) - preTranslation;
} }
bool Model::restoreJointPosition(int jointIndex, float percent) { bool Model::restoreJointPosition(int jointIndex, float percent, float priority) {
if (jointIndex == -1 || _jointStates.isEmpty()) { if (jointIndex == -1 || _jointStates.isEmpty()) {
return false; return false;
} }
@ -1237,10 +1248,12 @@ bool Model::restoreJointPosition(int jointIndex, float percent) {
foreach (int index, freeLineage) { foreach (int index, freeLineage) {
JointState& state = _jointStates[index]; JointState& state = _jointStates[index];
if (priority == state.animationPriority) {
const FBXJoint& joint = geometry.joints.at(index); const FBXJoint& joint = geometry.joints.at(index);
state.rotation = safeMix(state.rotation, joint.rotation, percent); state.rotation = safeMix(state.rotation, joint.rotation, percent);
state.translation = glm::mix(state.translation, joint.translation, percent); state.translation = glm::mix(state.translation, joint.translation, percent);
state.animationDisabled = false; state.animationPriority = 0.0f;
}
} }
return true; return true;
} }
@ -1259,8 +1272,12 @@ float Model::getLimbLength(int jointIndex) const {
return length; return length;
} }
void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain) { void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain, float priority) {
JointState& state = _jointStates[jointIndex]; JointState& state = _jointStates[jointIndex];
if (priority < state.animationPriority) {
return;
}
state.animationPriority = priority;
const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex]; const FBXJoint& joint = _geometry->getFBXGeometry().joints[jointIndex];
if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) && if (!constrain || (joint.rotationMin == glm::vec3(-PI, -PI, -PI) &&
joint.rotationMax == glm::vec3(PI, PI, PI))) { joint.rotationMax == glm::vec3(PI, PI, PI))) {
@ -1274,7 +1291,6 @@ void Model::applyRotationDelta(int jointIndex, const glm::quat& delta, bool cons
glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax)); glm::quat newRotation = glm::quat(glm::clamp(eulers, joint.rotationMin, joint.rotationMax));
state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation; state.combinedRotation = state.combinedRotation * glm::inverse(state.rotation) * newRotation;
state.rotation = newRotation; state.rotation = newRotation;
state.animationDisabled = true;
} }
const int BALL_SUBDIVISIONS = 10; const int BALL_SUBDIVISIONS = 10;
@ -1665,7 +1681,7 @@ void AnimationHandle::setURL(const QUrl& url) {
static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) { static void insertSorted(QList<AnimationHandlePointer>& handles, const AnimationHandlePointer& handle) {
for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) { for (QList<AnimationHandlePointer>::iterator it = handles.begin(); it != handles.end(); it++) {
if (handle->getPriority() < (*it)->getPriority()) { if (handle->getPriority() > (*it)->getPriority()) {
handles.insert(it, handle); handles.insert(it, handle);
return; return;
} }
@ -1674,12 +1690,25 @@ static void insertSorted(QList<AnimationHandlePointer>& handles, const Animation
} }
void AnimationHandle::setPriority(float priority) { void AnimationHandle::setPriority(float priority) {
if (_priority != priority) { if (_priority == priority) {
_priority = priority; return;
}
if (_running) { if (_running) {
_model->_runningAnimations.removeOne(_self); _model->_runningAnimations.removeOne(_self);
insertSorted(_model->_runningAnimations, _self); if (priority < _priority) {
replaceMatchingPriorities(priority);
} }
_priority = priority;
insertSorted(_model->_runningAnimations, _self);
} else {
_priority = priority;
}
}
void AnimationHandle::setStartAutomatically(bool startAutomatically) {
if ((_startAutomatically = startAutomatically) && !_running) {
start();
} }
} }
@ -1689,15 +1718,24 @@ void AnimationHandle::setMaskedJoints(const QStringList& maskedJoints) {
} }
void AnimationHandle::setRunning(bool running) { void AnimationHandle::setRunning(bool running) {
if (_running == running) {
if (running) {
// move back to the beginning
_frameIndex = _firstFrame;
}
return;
}
if ((_running = running)) { if ((_running = running)) {
if (!_model->_runningAnimations.contains(_self)) { if (!_model->_runningAnimations.contains(_self)) {
insertSorted(_model->_runningAnimations, _self); insertSorted(_model->_runningAnimations, _self);
} }
_frameIndex = 0.0f; _frameIndex = _firstFrame;
} else { } else {
_model->_runningAnimations.removeOne(_self); _model->_runningAnimations.removeOne(_self);
replaceMatchingPriorities(0.0f);
} }
emit runningChanged(_running);
} }
AnimationHandle::AnimationHandle(Model* model) : AnimationHandle::AnimationHandle(Model* model) :
@ -1706,6 +1744,10 @@ AnimationHandle::AnimationHandle(Model* model) :
_fps(30.0f), _fps(30.0f),
_priority(1.0f), _priority(1.0f),
_loop(false), _loop(false),
_hold(false),
_startAutomatically(false),
_firstFrame(0),
_lastFrame(INT_MAX),
_running(false) { _running(false) {
} }
@ -1736,36 +1778,55 @@ void AnimationHandle::simulate(float deltaTime) {
stop(); stop();
return; return;
} }
int ceilFrameIndex = (int)glm::ceil(_frameIndex); int lastFrameIndex = qMin(_lastFrame, animationGeometry.animationFrames.size() - 1);
if (!_loop && ceilFrameIndex >= animationGeometry.animationFrames.size()) { int firstFrameIndex = qMin(_firstFrame, lastFrameIndex);
if ((!_loop && _frameIndex >= lastFrameIndex) || firstFrameIndex == lastFrameIndex) {
// passed the end; apply the last frame // passed the end; apply the last frame
const FBXAnimationFrame& frame = animationGeometry.animationFrames.last(); const FBXAnimationFrame& frame = animationGeometry.animationFrames.at(lastFrameIndex);
for (int i = 0; i < _jointMappings.size(); i++) { for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i); int mapping = _jointMappings.at(i);
if (mapping != -1) { if (mapping != -1) {
Model::JointState& state = _model->_jointStates[mapping]; Model::JointState& state = _model->_jointStates[mapping];
if (!state.animationDisabled) { if (_priority >= state.animationPriority) {
state.rotation = frame.rotations.at(i); state.rotation = frame.rotations.at(i);
state.animationPriority = _priority;
} }
} }
} }
if (!_hold) {
stop(); stop();
}
return; return;
} }
int frameCount = lastFrameIndex - firstFrameIndex + 1;
_frameIndex = firstFrameIndex + glm::mod(qMax(_frameIndex - firstFrameIndex, 0.0f), (float)frameCount);
// blend between the closest two frames // blend between the closest two frames
const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at( const FBXAnimationFrame& ceilFrame = animationGeometry.animationFrames.at(
ceilFrameIndex % animationGeometry.animationFrames.size()); firstFrameIndex + ((int)glm::ceil(_frameIndex) - firstFrameIndex) % frameCount);
const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at( const FBXAnimationFrame& floorFrame = animationGeometry.animationFrames.at(
(int)glm::floor(_frameIndex) % animationGeometry.animationFrames.size()); firstFrameIndex + ((int)glm::floor(_frameIndex) - firstFrameIndex) % frameCount);
float frameFraction = glm::fract(_frameIndex); float frameFraction = glm::fract(_frameIndex);
for (int i = 0; i < _jointMappings.size(); i++) { for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i); int mapping = _jointMappings.at(i);
if (mapping != -1) { if (mapping != -1) {
Model::JointState& state = _model->_jointStates[mapping]; Model::JointState& state = _model->_jointStates[mapping];
if (!state.animationDisabled) { if (_priority >= state.animationPriority) {
state.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); state.rotation = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction);
state.animationPriority = _priority;
} }
} }
} }
} }
void AnimationHandle::replaceMatchingPriorities(float newPriority) {
for (int i = 0; i < _jointMappings.size(); i++) {
int mapping = _jointMappings.at(i);
if (mapping != -1) {
Model::JointState& state = _model->_jointStates[mapping];
if (_priority == state.animationPriority) {
state.animationPriority = newPriority;
}
}
}
}

View file

@ -113,7 +113,7 @@ public:
bool getJointState(int index, glm::quat& rotation) const; bool getJointState(int index, glm::quat& rotation) const;
/// Sets the joint state at the specified index. /// Sets the joint state at the specified index.
void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat()); void setJointState(int index, bool valid, const glm::quat& rotation = glm::quat(), float priority = 1.0f);
/// Returns the index of the left hand joint, or -1 if not found. /// Returns the index of the left hand joint, or -1 if not found.
int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; } int getLeftHandJointIndex() const { return isActive() ? _geometry->getFBXGeometry().leftHandJointIndex : -1; }
@ -166,7 +166,7 @@ public:
/// Restores some percentage of the default position of the left hand. /// Restores some percentage of the default position of the left hand.
/// \param percent the percentage of the default position to restore /// \param percent the percentage of the default position to restore
/// \return whether or not the left hand joint was found /// \return whether or not the left hand joint was found
bool restoreLeftHandPosition(float percent = 1.0f); bool restoreLeftHandPosition(float percent = 1.0f, float priority = 1.0f);
/// Gets the position of the left shoulder. /// Gets the position of the left shoulder.
/// \return whether or not the left shoulder joint was found /// \return whether or not the left shoulder joint was found
@ -178,7 +178,7 @@ public:
/// Restores some percentage of the default position of the right hand. /// Restores some percentage of the default position of the right hand.
/// \param percent the percentage of the default position to restore /// \param percent the percentage of the default position to restore
/// \return whether or not the right hand joint was found /// \return whether or not the right hand joint was found
bool restoreRightHandPosition(float percent = 1.0f); bool restoreRightHandPosition(float percent = 1.0f, float priority = 1.0f);
/// Gets the position of the right shoulder. /// Gets the position of the right shoulder.
/// \return whether or not the right shoulder joint was found /// \return whether or not the right shoulder joint was found
@ -254,7 +254,7 @@ protected:
glm::quat rotation; // rotation relative to parent glm::quat rotation; // rotation relative to parent
glm::mat4 transform; // rotation to world frame + translation in model frame glm::mat4 transform; // rotation to world frame + translation in model frame
glm::quat combinedRotation; // rotation from joint local to world frame glm::quat combinedRotation; // rotation from joint local to world frame
bool animationDisabled; // if true, animations do not affect this joint float animationPriority; // the priority of the animation affecting this joint
}; };
bool _shapesAreDirty; bool _shapesAreDirty;
@ -290,8 +290,8 @@ protected:
bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(), bool setJointPosition(int jointIndex, const glm::vec3& translation, const glm::quat& rotation = glm::quat(),
bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false,
const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f)); const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f);
bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false); bool setJointRotation(int jointIndex, const glm::quat& rotation, bool fromBind = false, float priority = 1.0f);
void setJointTranslation(int jointIndex, const glm::vec3& translation); void setJointTranslation(int jointIndex, const glm::vec3& translation);
@ -299,13 +299,13 @@ protected:
/// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to /// \param percent the percentage of the default position to apply (i.e., 0.25f to slerp one fourth of the way to
/// the original position /// the original position
/// \return true if the joint was found /// \return true if the joint was found
bool restoreJointPosition(int jointIndex, float percent = 1.0f); bool restoreJointPosition(int jointIndex, float percent = 1.0f, float priority = 0.0f);
/// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's /// Computes and returns the extended length of the limb terminating at the specified joint and starting at the joint's
/// first free ancestor. /// first free ancestor.
float getLimbLength(int jointIndex) const; float getLimbLength(int jointIndex) const;
void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true); void applyRotationDelta(int jointIndex, const glm::quat& delta, bool constrain = true, float priority = 1.0f);
void computeBoundingShape(const FBXGeometry& geometry); void computeBoundingShape(const FBXGeometry& geometry);
@ -381,6 +381,9 @@ class AnimationHandle : public QObject {
public: public:
void setRole(const QString& role) { _role = role; }
const QString& getRole() const { return _role; }
void setURL(const QUrl& url); void setURL(const QUrl& url);
const QUrl& getURL() const { return _url; } const QUrl& getURL() const { return _url; }
@ -393,12 +396,30 @@ public:
void setLoop(bool loop) { _loop = loop; } void setLoop(bool loop) { _loop = loop; }
bool getLoop() const { return _loop; } bool getLoop() const { return _loop; }
void setHold(bool hold) { _hold = hold; }
bool getHold() const { return _hold; }
void setStartAutomatically(bool startAutomatically);
bool getStartAutomatically() const { return _startAutomatically; }
void setFirstFrame(int firstFrame) { _firstFrame = firstFrame; }
int getFirstFrame() const { return _firstFrame; }
void setLastFrame(int lastFrame) { _lastFrame = lastFrame; }
int getLastFrame() const { return _lastFrame; }
void setMaskedJoints(const QStringList& maskedJoints); void setMaskedJoints(const QStringList& maskedJoints);
const QStringList& getMaskedJoints() const { return _maskedJoints; } const QStringList& getMaskedJoints() const { return _maskedJoints; }
void setRunning(bool running); void setRunning(bool running);
bool isRunning() const { return _running; } bool isRunning() const { return _running; }
signals:
void runningChanged(bool running);
public slots:
void start() { setRunning(true); } void start() { setRunning(true); }
void stop() { setRunning(false); } void stop() { setRunning(false); }
@ -409,14 +430,20 @@ private:
AnimationHandle(Model* model); AnimationHandle(Model* model);
void simulate(float deltaTime); void simulate(float deltaTime);
void replaceMatchingPriorities(float newPriority);
Model* _model; Model* _model;
WeakAnimationHandlePointer _self; WeakAnimationHandlePointer _self;
AnimationPointer _animation; AnimationPointer _animation;
QString _role;
QUrl _url; QUrl _url;
float _fps; float _fps;
float _priority; float _priority;
bool _loop; bool _loop;
bool _hold;
bool _startAutomatically;
int _firstFrame;
int _lastFrame;
QStringList _maskedJoints; QStringList _maskedJoints;
bool _running; bool _running;
QVector<int> _jointMappings; QVector<int> _jointMappings;

View file

@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
// //
#include <QCheckBox>
#include <QComboBox>
#include <QDialogButtonBox> #include <QDialogButtonBox>
#include <QDoubleSpinBox> #include <QDoubleSpinBox>
#include <QFileDialog> #include <QFileDialog>
@ -79,6 +81,13 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow); layout->setFieldGrowthPolicy(QFormLayout::AllNonFixedFieldsGrow);
setLayout(layout); setLayout(layout);
layout->addRow("Role:", _role = new QComboBox());
_role->addItem("idle");
_role->addItem("sit");
_role->setEditable(true);
_role->setCurrentText(handle->getRole());
connect(_role, SIGNAL(currentTextChanged(const QString&)), SLOT(updateHandle()));
QHBoxLayout* urlBox = new QHBoxLayout(); QHBoxLayout* urlBox = new QHBoxLayout();
layout->addRow("URL:", urlBox); layout->addRow("URL:", urlBox);
urlBox->addWidget(_url = new QLineEdit(handle->getURL().toString()), 1); urlBox->addWidget(_url = new QLineEdit(handle->getURL().toString()), 1);
@ -107,9 +116,40 @@ AnimationPanel::AnimationPanel(AnimationsDialog* dialog, const AnimationHandlePo
maskedJointBox->addWidget(_chooseMaskedJoints = new QPushButton("Choose")); maskedJointBox->addWidget(_chooseMaskedJoints = new QPushButton("Choose"));
connect(_chooseMaskedJoints, SIGNAL(clicked(bool)), SLOT(chooseMaskedJoints())); connect(_chooseMaskedJoints, SIGNAL(clicked(bool)), SLOT(chooseMaskedJoints()));
layout->addRow("Loop:", _loop = new QCheckBox());
_loop->setChecked(handle->getLoop());
connect(_loop, SIGNAL(toggled(bool)), SLOT(updateHandle()));
layout->addRow("Hold:", _hold = new QCheckBox());
_hold->setChecked(handle->getHold());
connect(_hold, SIGNAL(toggled(bool)), SLOT(updateHandle()));
layout->addRow("Start Automatically:", _startAutomatically = new QCheckBox());
_startAutomatically->setChecked(handle->getStartAutomatically());
connect(_startAutomatically, SIGNAL(toggled(bool)), SLOT(updateHandle()));
layout->addRow("First Frame:", _firstFrame = new QSpinBox());
_firstFrame->setMaximum(INT_MAX);
_firstFrame->setValue(handle->getFirstFrame());
connect(_firstFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle()));
layout->addRow("Last Frame:", _lastFrame = new QSpinBox());
_lastFrame->setMaximum(INT_MAX);
_lastFrame->setValue(handle->getLastFrame());
connect(_lastFrame, SIGNAL(valueChanged(int)), SLOT(updateHandle()));
QHBoxLayout* buttons = new QHBoxLayout();
layout->addRow(buttons);
buttons->addWidget(_start = new QPushButton("Start"));
_handle->connect(_start, SIGNAL(clicked(bool)), SLOT(start()));
buttons->addWidget(_stop = new QPushButton("Stop"));
_handle->connect(_stop, SIGNAL(clicked(bool)), SLOT(stop()));
QPushButton* remove = new QPushButton("Delete"); QPushButton* remove = new QPushButton("Delete");
layout->addRow(remove); buttons->addWidget(remove);
connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle())); connect(remove, SIGNAL(clicked(bool)), SLOT(removeHandle()));
_stop->connect(_handle.data(), SIGNAL(runningChanged(bool)), SLOT(setEnabled(bool)));
_stop->setEnabled(_handle->isRunning());
} }
void AnimationPanel::chooseURL() { void AnimationPanel::chooseURL() {
@ -146,9 +186,15 @@ void AnimationPanel::chooseMaskedJoints() {
} }
void AnimationPanel::updateHandle() { void AnimationPanel::updateHandle() {
_handle->setRole(_role->currentText());
_handle->setURL(_url->text()); _handle->setURL(_url->text());
_handle->setFPS(_fps->value()); _handle->setFPS(_fps->value());
_handle->setPriority(_priority->value()); _handle->setPriority(_priority->value());
_handle->setLoop(_loop->isChecked());
_handle->setHold(_hold->isChecked());
_handle->setStartAutomatically(_startAutomatically->isChecked());
_handle->setFirstFrame(_firstFrame->value());
_handle->setLastFrame(_lastFrame->value());
_handle->setMaskedJoints(_maskedJoints->text().split(QRegExp("\\s*,\\s*"))); _handle->setMaskedJoints(_maskedJoints->text().split(QRegExp("\\s*,\\s*")));
} }

View file

@ -17,9 +17,12 @@
#include "avatar/MyAvatar.h" #include "avatar/MyAvatar.h"
class QCheckBox;
class QComboBox;
class QDoubleSpinner; class QDoubleSpinner;
class QLineEdit; class QLineEdit;
class QPushButton; class QPushButton;
class QSpinBox;
class QVBoxLayout; class QVBoxLayout;
/// Allows users to edit the avatar animations. /// Allows users to edit the avatar animations.
@ -61,11 +64,19 @@ private:
AnimationsDialog* _dialog; AnimationsDialog* _dialog;
AnimationHandlePointer _handle; AnimationHandlePointer _handle;
QComboBox* _role;
QLineEdit* _url; QLineEdit* _url;
QDoubleSpinBox* _fps; QDoubleSpinBox* _fps;
QDoubleSpinBox* _priority; QDoubleSpinBox* _priority;
QCheckBox* _loop;
QCheckBox* _hold;
QCheckBox* _startAutomatically;
QSpinBox* _firstFrame;
QSpinBox* _lastFrame;
QLineEdit* _maskedJoints; QLineEdit* _maskedJoints;
QPushButton* _chooseMaskedJoints; QPushButton* _chooseMaskedJoints;
QPushButton* _start;
QPushButton* _stop;
bool _applying; bool _applying;
}; };

View file

@ -61,6 +61,8 @@ AccountManager::AccountManager() :
qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation"); qRegisterMetaType<QNetworkAccessManager::Operation>("QNetworkAccessManager::Operation");
qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters"); qRegisterMetaType<JSONCallbackParameters>("JSONCallbackParameters");
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
} }
const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash"; const QString DOUBLE_SLASH_SUBSTITUTE = "slashslash";
@ -69,6 +71,9 @@ void AccountManager::logout() {
// a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file // a logout means we want to delete the DataServerAccountInfo we currently have for this URL, in-memory and in file
_accountInfo = DataServerAccountInfo(); _accountInfo = DataServerAccountInfo();
emit balanceChanged(0);
connect(&_accountInfo, &DataServerAccountInfo::balanceChanged, this, &AccountManager::accountInfoBalanceChanged);
QSettings settings; QSettings settings;
settings.beginGroup(ACCOUNTS_GROUP); settings.beginGroup(ACCOUNTS_GROUP);
@ -82,6 +87,21 @@ void AccountManager::logout() {
emit usernameChanged(QString()); emit usernameChanged(QString());
} }
void AccountManager::updateBalance() {
if (hasValidAccessToken()) {
// ask our auth endpoint for our balance
JSONCallbackParameters callbackParameters;
callbackParameters.jsonCallbackReceiver = &_accountInfo;
callbackParameters.jsonCallbackMethod = "setBalanceFromJSON";
authenticatedRequest("/api/v1/wallets/mine", QNetworkAccessManager::GetOperation, callbackParameters);
}
}
void AccountManager::accountInfoBalanceChanged(qint64 newBalance) {
emit balanceChanged(newBalance);
}
void AccountManager::setAuthURL(const QUrl& authURL) { void AccountManager::setAuthURL(const QUrl& authURL) {
if (_authURL != authURL) { if (_authURL != authURL) {
_authURL = authURL; _authURL = authURL;

View file

@ -63,6 +63,8 @@ public slots:
void requestFinished(); void requestFinished();
void requestError(QNetworkReply::NetworkError error); void requestError(QNetworkReply::NetworkError error);
void logout(); void logout();
void updateBalance();
void accountInfoBalanceChanged(qint64 newBalance);
signals: signals:
void authRequired(); void authRequired();
void authEndpointChanged(); void authEndpointChanged();
@ -71,6 +73,7 @@ signals:
void loginComplete(const QUrl& authURL); void loginComplete(const QUrl& authURL);
void loginFailed(); void loginFailed();
void logoutComplete(); void logoutComplete();
void balanceChanged(qint64 newBalance);
private slots: private slots:
void processReply(); void processReply();
private: private:

View file

@ -17,7 +17,9 @@ DataServerAccountInfo::DataServerAccountInfo() :
_accessToken(), _accessToken(),
_username(), _username(),
_xmppPassword(), _xmppPassword(),
_discourseApiKey() _discourseApiKey(),
_balance(0),
_hasBalance(false)
{ {
} }
@ -25,7 +27,9 @@ DataServerAccountInfo::DataServerAccountInfo() :
DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) : DataServerAccountInfo::DataServerAccountInfo(const QJsonObject& jsonObject) :
_accessToken(jsonObject), _accessToken(jsonObject),
_username(), _username(),
_xmppPassword() _xmppPassword(),
_balance(0),
_hasBalance(false)
{ {
QJsonObject userJSONObject = jsonObject["user"].toObject(); QJsonObject userJSONObject = jsonObject["user"].toObject();
setUsername(userJSONObject["username"].toString()); setUsername(userJSONObject["username"].toString());
@ -38,6 +42,8 @@ DataServerAccountInfo::DataServerAccountInfo(const DataServerAccountInfo& otherI
_username = otherInfo._username; _username = otherInfo._username;
_xmppPassword = otherInfo._xmppPassword; _xmppPassword = otherInfo._xmppPassword;
_discourseApiKey = otherInfo._discourseApiKey; _discourseApiKey = otherInfo._discourseApiKey;
_balance = otherInfo._balance;
_hasBalance = otherInfo._hasBalance;
} }
DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) { DataServerAccountInfo& DataServerAccountInfo::operator=(const DataServerAccountInfo& otherInfo) {
@ -53,6 +59,8 @@ void DataServerAccountInfo::swap(DataServerAccountInfo& otherInfo) {
swap(_username, otherInfo._username); swap(_username, otherInfo._username);
swap(_xmppPassword, otherInfo._xmppPassword); swap(_xmppPassword, otherInfo._xmppPassword);
swap(_discourseApiKey, otherInfo._discourseApiKey); swap(_discourseApiKey, otherInfo._discourseApiKey);
swap(_balance, otherInfo._balance);
swap(_hasBalance, otherInfo._hasBalance);
} }
void DataServerAccountInfo::setUsername(const QString& username) { void DataServerAccountInfo::setUsername(const QString& username) {
@ -75,6 +83,22 @@ void DataServerAccountInfo::setDiscourseApiKey(const QString& discourseApiKey) {
} }
} }
void DataServerAccountInfo::setBalance(quint64 balance) {
if (!_hasBalance || _balance != balance) {
_balance = balance;
_hasBalance = true;
emit balanceChanged(_balance);
}
}
void DataServerAccountInfo::setBalanceFromJSON(const QJsonObject& jsonObject) {
if (jsonObject["status"].toString() == "success") {
qint64 balanceInSatoshis = jsonObject["data"].toObject()["wallet"].toObject()["balance"].toInt();
setBalance(balanceInSatoshis);
}
}
QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) { QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info) {
out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey; out << info._accessToken << info._username << info._xmppPassword << info._discourseApiKey;
return out; return out;

View file

@ -35,8 +35,16 @@ public:
const QString& getDiscourseApiKey() const { return _discourseApiKey; } const QString& getDiscourseApiKey() const { return _discourseApiKey; }
void setDiscourseApiKey(const QString& discourseApiKey); void setDiscourseApiKey(const QString& discourseApiKey);
quint64 getBalance() const { return _balance; }
void setBalance(quint64 balance);
bool hasBalance() const { return _hasBalance; }
void setHasBalance(bool hasBalance) { _hasBalance = hasBalance; }
Q_INVOKABLE void setBalanceFromJSON(const QJsonObject& jsonObject);
friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info); friend QDataStream& operator<<(QDataStream &out, const DataServerAccountInfo& info);
friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info); friend QDataStream& operator>>(QDataStream &in, DataServerAccountInfo& info);
signals:
qint64 balanceChanged(qint64 newBalance);
private: private:
void swap(DataServerAccountInfo& otherInfo); void swap(DataServerAccountInfo& otherInfo);
@ -44,6 +52,8 @@ private:
QString _username; QString _username;
QString _xmppPassword; QString _xmppPassword;
QString _discourseApiKey; QString _discourseApiKey;
quint64 _balance;
bool _hasBalance;
}; };
#endif // hifi_DataServerAccountInfo_h #endif // hifi_DataServerAccountInfo_h