From 7926f8e692489dd90fafaef14edb68d7927becfe Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Wed, 21 May 2014 18:25:31 -0700 Subject: [PATCH 01/28] First pass at user activity logger on client side --- interface/src/UserActivityLogger.cpp | 62 ++++++++++++++++++++++++++++ interface/src/UserActivityLogger.h | 29 +++++++++++++ 2 files changed, 91 insertions(+) create mode 100644 interface/src/UserActivityLogger.cpp create mode 100644 interface/src/UserActivityLogger.h diff --git a/interface/src/UserActivityLogger.cpp b/interface/src/UserActivityLogger.cpp new file mode 100644 index 0000000000..61e85bcf03 --- /dev/null +++ b/interface/src/UserActivityLogger.cpp @@ -0,0 +1,62 @@ +// +// UserActivityLogger.cpp +// +// +// Created by Clement on 5/21/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "UserActivityLogger.h" + +#include + +#include + +static const QString USER_ACTIVITY_URL = "/api/v1/user_activities"; + +UserActivityLogger& UserActivityLogger::getInstance() { + static UserActivityLogger sharedInstance; + return sharedInstance; +} + +UserActivityLogger::UserActivityLogger() { + +} + +void UserActivityLogger::logAction(QString action, QString details) { + AccountManager& accountManager = AccountManager::getInstance(); + QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + QHttpPart actionPart; + actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action\""); + actionPart.setBody(QByteArray().append(action)); + multipart->append(actionPart); + + + if (!details.isEmpty()) { + QHttpPart detailsPart; + detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" + " name=\"details\""); + detailsPart.setBody(QByteArray().append(details)); + multipart->append(detailsPart); + } + + accountManager.authenticatedRequest(USER_ACTIVITY_URL, + QNetworkAccessManager::PostOperation, + JSONCallbackParameters(), + NULL, + multipart); +} + +void UserActivityLogger::login() { + const QString ACTION_NAME = "login"; + logAction(ACTION_NAME); +} + +void UserActivityLogger::logout() { + const QString ACTION_NAME = "logout"; + logAction(ACTION_NAME); +} \ No newline at end of file diff --git a/interface/src/UserActivityLogger.h b/interface/src/UserActivityLogger.h new file mode 100644 index 0000000000..5d469df16d --- /dev/null +++ b/interface/src/UserActivityLogger.h @@ -0,0 +1,29 @@ +// +// UserActivityLogger.h +// +// +// Created by Clement on 5/21/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_UserActivityLogger_h +#define hifi_UserActivityLogger_h + +#include + +class UserActivityLogger { +public: + static UserActivityLogger& getInstance(); + + void login(); + void logout(); + +private: + UserActivityLogger(); + void logAction(QString action, QString details = QString()); +}; + +#endif // hifi_UserActivityLogger_h \ No newline at end of file From f7be9bb73079bfd84cef7f67515f6d4827e58001 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 22 May 2014 14:41:02 -0700 Subject: [PATCH 02/28] First working prototype for activity logger --- interface/src/UserActivityLogger.cpp | 17 ++++++++++++----- interface/src/UserActivityLogger.h | 3 ++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/interface/src/UserActivityLogger.cpp b/interface/src/UserActivityLogger.cpp index 61e85bcf03..10ff2c16bb 100644 --- a/interface/src/UserActivityLogger.cpp +++ b/interface/src/UserActivityLogger.cpp @@ -14,6 +14,7 @@ #include #include +#include static const QString USER_ACTIVITY_URL = "/api/v1/user_activities"; @@ -26,12 +27,12 @@ UserActivityLogger::UserActivityLogger() { } -void UserActivityLogger::logAction(QString action, QString details) { +void UserActivityLogger::logAction(QString action, QJsonObject details) { AccountManager& accountManager = AccountManager::getInstance(); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpPart actionPart; - actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action\""); + actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"user_action\""); actionPart.setBody(QByteArray().append(action)); multipart->append(actionPart); @@ -39,11 +40,13 @@ void UserActivityLogger::logAction(QString action, QString details) { if (!details.isEmpty()) { QHttpPart detailsPart; detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" - " name=\"details\""); - detailsPart.setBody(QByteArray().append(details)); + " name=\"action_details\""); + detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); multipart->append(detailsPart); } + + qDebug() << "Loging activity " << action; accountManager.authenticatedRequest(USER_ACTIVITY_URL, QNetworkAccessManager::PostOperation, JSONCallbackParameters(), @@ -53,7 +56,11 @@ void UserActivityLogger::logAction(QString action, QString details) { void UserActivityLogger::login() { const QString ACTION_NAME = "login"; - logAction(ACTION_NAME); + QJsonObject details; + details.insert("OS", QJsonValue(10.9)); + + + logAction(ACTION_NAME, details); } void UserActivityLogger::logout() { diff --git a/interface/src/UserActivityLogger.h b/interface/src/UserActivityLogger.h index 5d469df16d..816d26b9b8 100644 --- a/interface/src/UserActivityLogger.h +++ b/interface/src/UserActivityLogger.h @@ -13,6 +13,7 @@ #define hifi_UserActivityLogger_h #include +#include class UserActivityLogger { public: @@ -23,7 +24,7 @@ public: private: UserActivityLogger(); - void logAction(QString action, QString details = QString()); + void logAction(QString action, QJsonObject details = QJsonObject()); }; #endif // hifi_UserActivityLogger_h \ No newline at end of file From 552efa3e91e068baee70ce46fdef494110adb551 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Jun 2014 09:01:49 -0700 Subject: [PATCH 03/28] remove warning: unused variables --- libraries/shared/src/PhysicsSimulation.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/libraries/shared/src/PhysicsSimulation.cpp b/libraries/shared/src/PhysicsSimulation.cpp index 637a5e955c..e2f95a213c 100644 --- a/libraries/shared/src/PhysicsSimulation.cpp +++ b/libraries/shared/src/PhysicsSimulation.cpp @@ -24,9 +24,6 @@ int MAX_ENTITIES_PER_SIMULATION = 64; int MAX_COLLISIONS_PER_SIMULATION = 256; -const int NUM_SHAPE_BITS = 6; -const int SHAPE_INDEX_MASK = (1 << (NUM_SHAPE_BITS + 1)) - 1; - PhysicsSimulation::PhysicsSimulation() : _collisionList(MAX_COLLISIONS_PER_SIMULATION), _numIterations(0), _numCollisions(0), _constraintError(0.0f), _stepTime(0) { } From d3d8e4dc2d07866905b53c786298337b359c3f79 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Jun 2014 09:07:10 -0700 Subject: [PATCH 04/28] fix warnings about signed/unsigned comparisons --- interface/src/Audio.cpp | 2 +- libraries/audio/src/AudioRingBuffer.cpp | 18 +++++++++--------- libraries/audio/src/AudioRingBuffer.h | 14 +++++++------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/interface/src/Audio.cpp b/interface/src/Audio.cpp index 65912f83e8..07f38eee20 100644 --- a/interface/src/Audio.cpp +++ b/interface/src/Audio.cpp @@ -427,7 +427,7 @@ void Audio::handleAudioInput() { float inputToNetworkInputRatio = calculateDeviceToNetworkInputRatio(_numInputCallbackBytes); - unsigned int inputSamplesRequired = NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio; + int inputSamplesRequired = (int)((float)NETWORK_BUFFER_LENGTH_SAMPLES_PER_CHANNEL * inputToNetworkInputRatio); QByteArray inputByteArray = _inputDevice->readAll(); diff --git a/libraries/audio/src/AudioRingBuffer.cpp b/libraries/audio/src/AudioRingBuffer.cpp index ee4027841b..c213ae7454 100644 --- a/libraries/audio/src/AudioRingBuffer.cpp +++ b/libraries/audio/src/AudioRingBuffer.cpp @@ -52,7 +52,7 @@ void AudioRingBuffer::reset() { _isStarved = true; } -void AudioRingBuffer::resizeForFrameSize(qint64 numFrameSamples) { +void AudioRingBuffer::resizeForFrameSize(int numFrameSamples) { delete[] _buffer; _sampleCapacity = numFrameSamples * RING_BUFFER_LENGTH_FRAMES; _buffer = new int16_t[_sampleCapacity]; @@ -68,14 +68,14 @@ int AudioRingBuffer::parseData(const QByteArray& packet) { return writeData(packet.data() + numBytesPacketHeader, packet.size() - numBytesPacketHeader); } -qint64 AudioRingBuffer::readSamples(int16_t* destination, qint64 maxSamples) { +int AudioRingBuffer::readSamples(int16_t* destination, int maxSamples) { return readData((char*) destination, maxSamples * sizeof(int16_t)); } -qint64 AudioRingBuffer::readData(char *data, qint64 maxSize) { +int AudioRingBuffer::readData(char *data, int maxSize) { // only copy up to the number of samples we have available - int numReadSamples = std::min((unsigned) (maxSize / sizeof(int16_t)), samplesAvailable()); + int numReadSamples = std::min((int) (maxSize / sizeof(int16_t)), samplesAvailable()); // If we're in random access mode, then we consider our number of available read samples slightly // differently. Namely, if anything has been written, we say we have as many samples as they ask for @@ -113,15 +113,15 @@ qint64 AudioRingBuffer::readData(char *data, qint64 maxSize) { return numReadSamples * sizeof(int16_t); } -qint64 AudioRingBuffer::writeSamples(const int16_t* source, qint64 maxSamples) { +int AudioRingBuffer::writeSamples(const int16_t* source, int maxSamples) { return writeData((const char*) source, maxSamples * sizeof(int16_t)); } -qint64 AudioRingBuffer::writeData(const char* data, qint64 maxSize) { +int AudioRingBuffer::writeData(const char* data, int maxSize) { // make sure we have enough bytes left for this to be the right amount of audio // otherwise we should not copy that data, and leave the buffer pointers where they are - int samplesToCopy = std::min((quint64)(maxSize / sizeof(int16_t)), (quint64)_sampleCapacity); + int samplesToCopy = std::min((int)(maxSize / sizeof(int16_t)), _sampleCapacity); if (_hasStarted && samplesToCopy > _sampleCapacity - samplesAvailable()) { // this read will cross the next output, so call us starved and reset the buffer @@ -157,7 +157,7 @@ void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) { _nextOutput = shiftedPositionAccomodatingWrap(_nextOutput, numSamples); } -unsigned int AudioRingBuffer::samplesAvailable() const { +int AudioRingBuffer::samplesAvailable() const { if (!_endOfLastWrite) { return 0; } else { @@ -186,7 +186,7 @@ void AudioRingBuffer::addSilentFrame(int numSilentSamples) { } } -bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const { +bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const { if (!_isStarved) { return true; } else { diff --git a/libraries/audio/src/AudioRingBuffer.h b/libraries/audio/src/AudioRingBuffer.h index 1ddcadeceb..6a77f5a830 100644 --- a/libraries/audio/src/AudioRingBuffer.h +++ b/libraries/audio/src/AudioRingBuffer.h @@ -43,7 +43,7 @@ public: ~AudioRingBuffer(); void reset(); - void resizeForFrameSize(qint64 numFrameSamples); + void resizeForFrameSize(int numFrameSamples); int getSampleCapacity() const { return _sampleCapacity; } @@ -53,20 +53,20 @@ public: const int16_t* getNextOutput() const { return _nextOutput; } const int16_t* getBuffer() const { return _buffer; } - qint64 readSamples(int16_t* destination, qint64 maxSamples); - qint64 writeSamples(const int16_t* source, qint64 maxSamples); + int readSamples(int16_t* destination, int maxSamples); + int writeSamples(const int16_t* source, int maxSamples); - qint64 readData(char* data, qint64 maxSize); - qint64 writeData(const char* data, qint64 maxSize); + int readData(char* data, int maxSize); + int writeData(const char* data, int maxSize); int16_t& operator[](const int index); const int16_t& operator[] (const int index) const; void shiftReadPosition(unsigned int numSamples); - unsigned int samplesAvailable() const; + int samplesAvailable() const; - bool isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const; + bool isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const; bool isStarved() const { return _isStarved; } void setIsStarved(bool isStarved) { _isStarved = isStarved; } From 81209c38cae10d34c9c05e368da5c7d1d902049c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Jun 2014 09:09:44 -0700 Subject: [PATCH 05/28] fix warning about out of order initialization --- interface/src/ui/ApplicationOverlay.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/ui/ApplicationOverlay.cpp b/interface/src/ui/ApplicationOverlay.cpp index 9be556cf62..cef1047f35 100644 --- a/interface/src/ui/ApplicationOverlay.cpp +++ b/interface/src/ui/ApplicationOverlay.cpp @@ -39,9 +39,9 @@ inline float min(float a, float b) { ApplicationOverlay::ApplicationOverlay() : _framebufferObject(NULL), _textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE), - _crosshairTexture(0), _alpha(1.0f), - _active(true) { + _active(true), + _crosshairTexture(0) { memset(_reticleActive, 0, sizeof(_reticleActive)); memset(_magActive, 0, sizeof(_reticleActive)); From d78580adc4c49dfcdd24c8977d9651c9d667ee01 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Jun 2014 09:38:12 -0700 Subject: [PATCH 06/28] remove warning about uninitialized variables --- interface/src/ui/overlays/Overlays.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 95f4f2b2fe..a2353e504f 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -82,13 +82,13 @@ void Overlays::render3D() { return; } bool myAvatarComputed = false; - MyAvatar* avatar; + MyAvatar* avatar = NULL; glm::quat myAvatarRotation; - glm::vec3 myAvatarPosition; - float angle; - glm::vec3 axis; - float myAvatarScale; - + glm::vec3 myAvatarPosition(0.0f); + float angle = 0.0f; + glm::vec3 axis(0.0f, 1.0f, 0.0f); + float myAvatarScale = 1.0f; + foreach(Overlay* thisOverlay, _overlays3D) { glPushMatrix(); switch (thisOverlay->getAnchor()) { From 1b24a111fe28869376fd3683daa8d32db5d38cf3 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Jun 2014 09:56:18 -0700 Subject: [PATCH 07/28] JointState::_rotationInParentFrame is now private --- interface/src/avatar/FaceModel.cpp | 8 ++++---- interface/src/avatar/SkeletonModel.cpp | 6 +++--- interface/src/renderer/JointState.cpp | 16 ++++++++-------- interface/src/renderer/JointState.h | 16 +++++++++++----- interface/src/renderer/Model.cpp | 8 ++++---- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/interface/src/avatar/FaceModel.cpp b/interface/src/avatar/FaceModel.cpp index c3f31ff6e7..59e5b08cc0 100644 --- a/interface/src/avatar/FaceModel.cpp +++ b/interface/src/avatar/FaceModel.cpp @@ -51,10 +51,10 @@ void FaceModel::maybeUpdateNeckRotation(const JointState& parentState, const FBX glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) * joint.preTransform * glm::mat4_cast(joint.preRotation))); - state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2])) + state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalRoll(), glm::normalize(inverse * axes[2])) * glm::angleAxis(RADIANS_PER_DEGREE * _owningHead->getFinalYaw(), glm::normalize(inverse * axes[1])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningHead->getFinalPitch(), glm::normalize(inverse * axes[0])) - * joint.rotation; + * joint.rotation); } void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { @@ -68,8 +68,8 @@ void FaceModel::maybeUpdateEyeRotation(const JointState& parentState, const FBXJ _owningHead->getSaccade() - _translation, 1.0f)); glm::quat between = rotationBetween(front, lookAt); const float MAX_ANGLE = 30.0f * RADIANS_PER_DEGREE; - state._rotationInParentFrame = glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * - joint.rotation; + state.setRotationInParentFrame(glm::angleAxis(glm::clamp(glm::angle(between), -MAX_ANGLE, MAX_ANGLE), glm::axis(between)) * + joint.rotation); } void FaceModel::updateJointState(int index) { diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index fdb3ce03d8..bdda30c9a3 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -220,7 +220,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { JointState& parentState = _jointStates[parentJointIndex]; parentState.setRotationFromBindFrame(palmRotation, PALM_PRIORITY); // lock hand to forearm by slamming its rotation (in parent-frame) to identity - _jointStates[jointIndex]._rotationInParentFrame = glm::quat(); + _jointStates[jointIndex].setRotationInParentFrame(glm::quat()); } else { setJointPosition(jointIndex, palmPosition, palmRotation, true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); @@ -259,9 +259,9 @@ void SkeletonModel::maybeUpdateLeanRotation(const JointState& parentState, const glm::mat3 axes = glm::mat3_cast(glm::quat()); glm::mat3 inverse = glm::mat3(glm::inverse(parentState.getTransform() * glm::translate(state.getDefaultTranslationInParentFrame()) * joint.preTransform * glm::mat4_cast(joint.preRotation * joint.rotation))); - state._rotationInParentFrame = glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), + state.setRotationInParentFrame(glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanSideways(), glm::normalize(inverse * axes[2])) * glm::angleAxis(- RADIANS_PER_DEGREE * _owningAvatar->getHead()->getFinalLeanForward(), - glm::normalize(inverse * axes[0])) * joint.rotation; + glm::normalize(inverse * axes[0])) * joint.rotation); } void SkeletonModel::maybeUpdateNeckRotation(const JointState& parentState, const FBXJoint& joint, JointState& state) { diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index e66a2f44e9..9219db8840 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -21,6 +21,14 @@ JointState::JointState() : _fbxJoint(NULL) { } +void JointState::copyState(const JointState& state) { + _animationPriority = state._animationPriority; + _transform = state._transform; + _rotation = extractRotation(_transform); + _rotationInParentFrame = state._rotationInParentFrame; + // DO NOT copy _fbxJoint +} + void JointState::setFBXJoint(const FBXJoint* joint) { assert(joint != NULL); _rotationInParentFrame = joint->rotation; @@ -28,14 +36,6 @@ void JointState::setFBXJoint(const FBXJoint* joint) { _fbxJoint = joint; } -void JointState::copyState(const JointState& state) { - _rotationInParentFrame = state._rotationInParentFrame; - _transform = state._transform; - _rotation = extractRotation(_transform); - _animationPriority = state._animationPriority; - // DO NOT copy _fbxJoint -} - void JointState::computeTransform(const glm::mat4& parentTransform) { glm::quat modifiedRotation = _fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation; glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(modifiedRotation) * _fbxJoint->postTransform; diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index b1a584d4ec..81023f89ca 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -22,12 +22,14 @@ class JointState { public: JointState(); + void copyState(const JointState& state); + void setFBXJoint(const FBXJoint* joint); const FBXJoint& getFBXJoint() const { return *_fbxJoint; } - void copyState(const JointState& state); - void computeTransform(const glm::mat4& parentTransform); + + const glm::mat4& getTransform() const { return _transform; } glm::quat getRotation() const { return _rotation; } @@ -42,8 +44,6 @@ public: /// \param delta is in the jointParent-frame void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f); - const glm::vec3& getDefaultTranslationInParentFrame() const; - void restoreRotation(float fraction, float priority); /// \param rotation is from bind- to model-frame @@ -51,14 +51,20 @@ public: /// NOTE: the JointState's model-frame transform/rotation are NOT updated! void setRotationFromBindFrame(const glm::quat& rotation, float priority); + void setRotationInParentFrame(const glm::quat& rotation) { _rotationInParentFrame = rotation; } + const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; } + + const glm::vec3& getDefaultTranslationInParentFrame() const; + + void clearTransformTranslation(); - glm::quat _rotationInParentFrame; // joint- to parentJoint-frame float _animationPriority; // the priority of the animation affecting this joint private: glm::mat4 _transform; // joint- to model-frame glm::quat _rotation; // joint- to model-frame + glm::quat _rotationInParentFrame; // joint- to parentJoint-frame const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 3b5cda4fd2..7afcb2a736 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -460,7 +460,7 @@ void Model::reset() { } const FBXGeometry& geometry = _geometry->getFBXGeometry(); for (int i = 0; i < _jointStates.size(); i++) { - _jointStates[i]._rotationInParentFrame = geometry.joints.at(i).rotation; + _jointStates[i].setRotationInParentFrame(geometry.joints.at(i).rotation); } } @@ -686,7 +686,7 @@ bool Model::getJointState(int index, glm::quat& rotation) const { if (index == -1 || index >= _jointStates.size()) { return false; } - rotation = _jointStates.at(index)._rotationInParentFrame; + rotation = _jointStates.at(index).getRotationInParentFrame(); const glm::quat& defaultRotation = _geometry->getFBXGeometry().joints.at(index).rotation; return glm::abs(rotation.x - defaultRotation.x) >= EPSILON || glm::abs(rotation.y - defaultRotation.y) >= EPSILON || @@ -699,7 +699,7 @@ void Model::setJointState(int index, bool valid, const glm::quat& rotation, floa JointState& state = _jointStates[index]; if (priority >= state._animationPriority) { if (valid) { - state._rotationInParentFrame = rotation; + state.setRotationInParentFrame(rotation); state._animationPriority = priority; } else { state.restoreRotation(1.0f, priority); @@ -1605,7 +1605,7 @@ void AnimationHandle::applyFrame(float frameIndex) { if (mapping != -1) { JointState& state = _model->_jointStates[mapping]; if (_priority >= state._animationPriority) { - state._rotationInParentFrame = safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction); + state.setRotationInParentFrame(safeMix(floorFrame.rotations.at(i), ceilFrame.rotations.at(i), frameFraction)); state._animationPriority = _priority; } } From c12c869cdfb981bc22ffd662959b5e0b5b99892d Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 25 Jun 2014 18:06:56 -0700 Subject: [PATCH 08/28] add "visible" transforms to JointState also stubbery for using them in Model and SkeletonModel --- interface/src/avatar/SkeletonModel.cpp | 7 +++- interface/src/avatar/SkeletonModel.h | 4 +- interface/src/renderer/JointState.cpp | 21 +++++++++- interface/src/renderer/JointState.h | 11 ++++++ interface/src/renderer/Model.cpp | 54 +++++++++++++++++++++++--- interface/src/renderer/Model.h | 6 +++ 6 files changed, 94 insertions(+), 9 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index bdda30c9a3..0229d686d4 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -539,6 +539,11 @@ void SkeletonModel::buildRagdollConstraints() { } } +void SkeletonModel::updateVisibleJointStates() { + Model::updateVisibleJointStates(); + // TODO: implement this to move visible joints to agree with joint shape positions +} + // virtual void SkeletonModel::stepRagdollForward(float deltaTime) { const float RAGDOLL_FOLLOWS_JOINTS_TIMESCALE = 0.03f; @@ -699,7 +704,7 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { _boundingRadius = 0.5f * glm::length(diagonal); } -void SkeletonModel::resetShapePositions() { +void SkeletonModel::resetShapePositionsToDefaultPose() { // DEBUG method. // Moves shapes to the joint default locations for debug visibility into // how the bounding shape is computed. diff --git a/interface/src/avatar/SkeletonModel.h b/interface/src/avatar/SkeletonModel.h index b91c112b6a..76d0d45efa 100644 --- a/interface/src/avatar/SkeletonModel.h +++ b/interface/src/avatar/SkeletonModel.h @@ -93,6 +93,8 @@ public: /// Retrieve the positions of up to two eye meshes. /// \return whether or not both eye meshes were found bool getEyePositions(glm::vec3& firstEyePosition, glm::vec3& secondEyePosition) const; + + virtual void updateVisibleJointStates(); // virtual overrride from Ragdoll virtual void stepRagdollForward(float deltaTime); @@ -104,7 +106,7 @@ public: float getBoundingShapeRadius() const { return _boundingShape.getRadius(); } const CapsuleShape& getBoundingShape() const { return _boundingShape; } - void resetShapePositions(); // DEBUG method + void resetShapePositionsToDefaultPose(); // DEBUG method void renderRagdoll(); protected: diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index 9219db8840..f75bbe8bec 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -11,7 +11,6 @@ #include -//#include #include #include "JointState.h" @@ -26,6 +25,10 @@ void JointState::copyState(const JointState& state) { _transform = state._transform; _rotation = extractRotation(_transform); _rotationInParentFrame = state._rotationInParentFrame; + + _visibleTransform = state._visibleTransform; + _visibleRotation = extractRotation(_visibleTransform); + _visibleRotationInParentFrame = state._visibleRotationInParentFrame; // DO NOT copy _fbxJoint } @@ -43,6 +46,13 @@ void JointState::computeTransform(const glm::mat4& parentTransform) { _rotation = extractRotation(_transform); } +void JointState::computeVisibleTransform(const glm::mat4& parentTransform) { + glm::quat modifiedRotation = _fbxJoint->preRotation * _visibleRotationInParentFrame * _fbxJoint->postRotation; + glm::mat4 modifiedTransform = _fbxJoint->preTransform * glm::mat4_cast(modifiedRotation) * _fbxJoint->postTransform; + _visibleTransform = parentTransform * glm::translate(_fbxJoint->translation) * modifiedTransform; + _visibleRotation = extractRotation(_visibleTransform); +} + glm::quat JointState::getRotationFromBindToModelFrame() const { return _rotation * _fbxJoint->inverseBindRotation; } @@ -68,6 +78,9 @@ void JointState::clearTransformTranslation() { _transform[3][0] = 0.0f; _transform[3][1] = 0.0f; _transform[3][2] = 0.0f; + _visibleTransform[3][0] = 0.0f; + _visibleTransform[3][1] = 0.0f; + _visibleTransform[3][2] = 0.0f; } void JointState::setRotation(const glm::quat& rotation, bool constrain, float priority) { @@ -99,3 +112,9 @@ const glm::vec3& JointState::getDefaultTranslationInParentFrame() const { assert(_fbxJoint != NULL); return _fbxJoint->translation; } + +void JointState::slaveVisibleTransform() { + _visibleTransform = _transform; + _visibleRotation = _rotation; + _visibleRotationInParentFrame = _rotationInParentFrame; +} diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index 81023f89ca..b42132d0a0 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -27,8 +27,13 @@ public: void setFBXJoint(const FBXJoint* joint); const FBXJoint& getFBXJoint() const { return *_fbxJoint; } + void computeTransform(const glm::mat4& parentTransform); + void computeVisibleTransform(const glm::mat4& parentTransform); + const glm::mat4& getVisibleTransform() const { return _visibleTransform; } + glm::quat getVisibleRotation() const { return _visibleRotation; } + glm::vec3 getVisiblePosition() const { return extractTranslation(_visibleTransform); } const glm::mat4& getTransform() const { return _transform; } @@ -59,6 +64,8 @@ public: void clearTransformTranslation(); + void slaveVisibleTransform(); + float _animationPriority; // the priority of the animation affecting this joint private: @@ -66,6 +73,10 @@ private: glm::quat _rotation; // joint- to model-frame glm::quat _rotationInParentFrame; // joint- to parentJoint-frame + glm::mat4 _visibleTransform; + glm::quat _visibleRotation; + glm::quat _visibleRotationInParentFrame; + const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 7afcb2a736..5d82b111b7 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -39,8 +39,8 @@ Model::Model(QObject* parent) : _scaledToFit(false), _snapModelToCenter(false), _snappedToCenter(false), + _showTrueJointTransforms(false), _rootIndex(-1), - //_enableCollisionShapes(false), _lodDistance(0.0f), _pupilDilation(0.0f), _url("http://invalid.com") { @@ -571,6 +571,9 @@ void Model::setJointStates(QVector states) { radius = distance; } } + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } _boundingRadius = radius; } @@ -765,6 +768,23 @@ bool Model::getJointCombinedRotation(int jointIndex, glm::quat& rotation) const return true; } +bool Model::getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + // position is in world-frame + position = _translation + _rotation * _jointStates[jointIndex].getVisiblePosition(); + return true; +} + +bool Model::getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const { + if (jointIndex == -1 || jointIndex >= _jointStates.size()) { + return false; + } + rotation = _rotation * _jointStates[jointIndex].getVisibleRotation(); + return true; +} + QStringList Model::getJointNames() const { if (QThread::currentThread() != thread()) { QStringList result; @@ -918,6 +938,8 @@ void Model::simulateInternal(float deltaTime) { for (int i = 0; i < _jointStates.size(); i++) { updateJointState(i); } + updateVisibleJointStates(); + _shapesAreDirty = ! _shapes.isEmpty(); // update the attachment transforms and simulate them @@ -928,8 +950,13 @@ void Model::simulateInternal(float deltaTime) { glm::vec3 jointTranslation = _translation; glm::quat jointRotation = _rotation; - getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); - getJointRotationInWorldFrame(attachment.jointIndex, jointRotation); + if (_showTrueJointTransforms) { + getJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); + getJointRotationInWorldFrame(attachment.jointIndex, jointRotation); + } else { + getVisibleJointPositionInWorldFrame(attachment.jointIndex, jointTranslation); + getVisibleJointRotationInWorldFrame(attachment.jointIndex, jointRotation); + } model->setTranslation(jointTranslation + jointRotation * attachment.translation * _scale); model->setRotation(jointRotation * attachment.rotation); @@ -944,9 +971,16 @@ void Model::simulateInternal(float 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] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + if (_showTrueJointTransforms) { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getTransform() * cluster.inverseBindMatrix; + } + } else { + for (int j = 0; j < mesh.clusters.size(); j++) { + const FBXCluster& cluster = mesh.clusters.at(j); + state.clusterMatrices[j] = modelToWorld * _jointStates[cluster.jointIndex].getVisibleTransform() * cluster.inverseBindMatrix; + } } } @@ -972,6 +1006,14 @@ void Model::updateJointState(int index) { } } +void Model::updateVisibleJointStates() { + if (!_showTrueJointTransforms) { + for (int i = 0; i < _jointStates.size(); i++) { + _jointStates[i].slaveVisibleTransform(); + } + } +} + bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation, bool useRotation, int lastFreeIndex, bool allIntermediatesFree, const glm::vec3& alignment, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 2045a0c9b5..8572831d8b 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -120,6 +120,9 @@ public: bool getJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; bool getJointCombinedRotation(int jointIndex, glm::quat& rotation) const; + bool getVisibleJointPositionInWorldFrame(int jointIndex, glm::vec3& position) const; + bool getVisibleJointRotationInWorldFrame(int jointIndex, glm::quat& rotation) const; + /// \param jointIndex index of joint in model structure /// \param position[out] position of joint in model-frame /// \return true if joint exists @@ -152,6 +155,7 @@ protected: bool _snapModelToCenter; /// is the model's offset automatically adjusted to center around 0,0,0 in model space bool _snappedToCenter; /// are we currently snapped to center + bool _showTrueJointTransforms; int _rootIndex; QVector _jointStates; @@ -176,6 +180,8 @@ protected: /// Updates the state of the joint at the specified index. virtual void updateJointState(int index); + + virtual void updateVisibleJointStates(); /// \param jointIndex index of joint in model structure /// \param position position of joint in model-frame From 9d393bbe8c62f863f6a96ddad699db01e33f4b96 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Thu, 26 Jun 2014 14:55:11 -0700 Subject: [PATCH 09/28] Added some functions to UserActivityLogger + made logAction public --- interface/src/Application.cpp | 6 ++ interface/src/UserActivityLogger.cpp | 87 +++++++++++++++----- interface/src/UserActivityLogger.h | 18 +++- libraries/networking/src/LimitedNodeList.cpp | 2 +- 4 files changed, 89 insertions(+), 24 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 013b1755ab..809e690b6d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -68,6 +68,7 @@ #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" +#include "UserActivityLogger.h" #include "Util.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" @@ -844,10 +845,15 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Space: + + UserActivityLogger::getInstance().login(); + break; resetSensors(); break; case Qt::Key_G: + UserActivityLogger::getInstance().logout(); + break; if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity); } diff --git a/interface/src/UserActivityLogger.cpp b/interface/src/UserActivityLogger.cpp index 10ff2c16bb..33dfcde576 100644 --- a/interface/src/UserActivityLogger.cpp +++ b/interface/src/UserActivityLogger.cpp @@ -24,46 +24,91 @@ UserActivityLogger& UserActivityLogger::getInstance() { } UserActivityLogger::UserActivityLogger() { - } void UserActivityLogger::logAction(QString action, QJsonObject details) { AccountManager& accountManager = AccountManager::getInstance(); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - QHttpPart actionPart; - actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"user_action\""); - actionPart.setBody(QByteArray().append(action)); - multipart->append(actionPart); - - - if (!details.isEmpty()) { - QHttpPart detailsPart; - detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" - " name=\"action_details\""); - detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); - multipart->append(detailsPart); + if (action != "login") { + QHttpPart actionPart; + actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); + actionPart.setBody(QByteArray().append(action)); + multipart->append(actionPart); + + + if (!details.isEmpty()) { + QHttpPart detailsPart; + detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" + " name=\"action_details\""); + detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); + multipart->append(detailsPart); + } } - - qDebug() << "Loging activity " << action; + + JSONCallbackParameters params; + params.jsonCallbackReceiver = this; + params.jsonCallbackMethod = "requestFinished"; + params.errorCallbackReceiver = this; + params.errorCallbackMethod = "requestError"; + accountManager.authenticatedRequest(USER_ACTIVITY_URL, QNetworkAccessManager::PostOperation, - JSONCallbackParameters(), + params, NULL, multipart); } +void UserActivityLogger::requestFinished(const QJsonObject& object) { + qDebug() << object; +} + +void UserActivityLogger::requestError(QNetworkReply::NetworkError error,const QString& string) { + qDebug() << error << ": " << string; +} + void UserActivityLogger::login() { const QString ACTION_NAME = "login"; - QJsonObject details; - details.insert("OS", QJsonValue(10.9)); + QJsonObject actionDetails; + QString OS_KEY = "OS"; + QString VERSION_KEY = "Version"; +#ifdef Q_OS_MAC + actionDetails.insert(OS_KEY, QJsonValue(QString("Mac"))); + actionDetails.insert(VERSION_KEY, QJsonValue(QSysInfo::macVersion())); +#elif Q_OS_LINUX + actionDetails.insert(OS_KEY, QJsonValue(QString("Linux"))); +#elif Q_OS_WIN + actionDetails.insert(OS_KEY, QJsonValue(QString("Windows"))); + actionDetails.insert(VERSION_KEY, QJsonValue(QSysInfo::windowsVersion())); +#elif Q_OS_UNIX + actionDetails.insert(OS_KEY, QJsonValue(QString("Unknown UNIX"))); +#else + actionDetails.insert(OS_KEY, QJsonValue(QString("Unknown system"))); +#endif - logAction(ACTION_NAME, details); + logAction(ACTION_NAME, actionDetails); } void UserActivityLogger::logout() { - const QString ACTION_NAME = "logout"; + const QString ACTION_NAME = ""; logAction(ACTION_NAME); -} \ No newline at end of file +} + +void UserActivityLogger::changedModel() { + const QString ACTION_NAME = "changed_model"; + QJsonObject actionDetails; + + + logAction(ACTION_NAME, actionDetails); +} + +void UserActivityLogger::changedDomain() { + const QString ACTION_NAME = "changed_domain"; + QJsonObject actionDetails; + + + logAction(ACTION_NAME, actionDetails); +} + diff --git a/interface/src/UserActivityLogger.h b/interface/src/UserActivityLogger.h index 816d26b9b8..168d448089 100644 --- a/interface/src/UserActivityLogger.h +++ b/interface/src/UserActivityLogger.h @@ -12,19 +12,33 @@ #ifndef hifi_UserActivityLogger_h #define hifi_UserActivityLogger_h +#include #include #include +#include -class UserActivityLogger { +class UserActivityLogger : public QObject { + Q_OBJECT + public: static UserActivityLogger& getInstance(); +public slots: + void logAction(QString action, QJsonObject details = QJsonObject()); + void login(); void logout(); +// void changedDisplayName(); + void changedModel(); + void changedDomain(); +// void connectedDevice(); + +private slots: + void requestFinished(const QJsonObject& object); + void requestError(QNetworkReply::NetworkError error,const QString& string); private: UserActivityLogger(); - void logAction(QString action, QJsonObject details = QJsonObject()); }; #endif // hifi_UserActivityLogger_h \ No newline at end of file diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp index b5a23f6b99..2264d24c42 100644 --- a/libraries/networking/src/LimitedNodeList.cpp +++ b/libraries/networking/src/LimitedNodeList.cpp @@ -33,7 +33,7 @@ const char SOLO_NODE_TYPES[2] = { NodeType::AudioMixer }; -const QUrl DEFAULT_NODE_AUTH_URL = QUrl("https://data-web.highfidelity.io"); +const QUrl DEFAULT_NODE_AUTH_URL = QUrl("http://localhost:3000"); LimitedNodeList* LimitedNodeList::_sharedInstance = NULL; From e8e9d6464b7993d521393b0d3cb4d3bac59e649e Mon Sep 17 00:00:00 2001 From: David Rowe Date: Mon, 30 Jun 2014 12:41:01 -0700 Subject: [PATCH 10/28] Fix running scripts scrollbar and add scrollbar to list of all scripts --- interface/ui/runningScriptsWidget.ui | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/interface/ui/runningScriptsWidget.ui b/interface/ui/runningScriptsWidget.ui index 71efe6970a..b58c8436d0 100644 --- a/interface/ui/runningScriptsWidget.ui +++ b/interface/ui/runningScriptsWidget.ui @@ -137,7 +137,7 @@ background: transparent; Helvetica,Arial,sans-serif - 16 + -1 75 false true @@ -326,9 +326,6 @@ padding-top: 3px; Qt::LeftToRight - - margin: 0; - QFrame::NoFrame @@ -336,7 +333,7 @@ padding-top: 3px; 0 - Qt::ScrollBarAlwaysOn + Qt::ScrollBarAsNeeded Qt::ScrollBarAlwaysOff @@ -352,7 +349,7 @@ padding-top: 3px; 0 0 - 269 + 284 16 @@ -460,7 +457,6 @@ font: bold 16px; background: transparent; font-size: 14px; - runningScriptsList @@ -627,10 +623,10 @@ QListView::item { QFrame::Plain - Qt::ScrollBarAlwaysOff + Qt::ScrollBarAlwaysOn - Qt::ScrollBarAsNeeded + Qt::ScrollBarAlwaysOff From 0d18debd1b8b9982ece73384a8dfc29d665dbbe6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 14:27:07 -0700 Subject: [PATCH 11/28] Moved UserActivityLogger to networking --- {interface => libraries/networking}/src/UserActivityLogger.cpp | 0 {interface => libraries/networking}/src/UserActivityLogger.h | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename {interface => libraries/networking}/src/UserActivityLogger.cpp (100%) rename {interface => libraries/networking}/src/UserActivityLogger.h (100%) diff --git a/interface/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp similarity index 100% rename from interface/src/UserActivityLogger.cpp rename to libraries/networking/src/UserActivityLogger.cpp diff --git a/interface/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h similarity index 100% rename from interface/src/UserActivityLogger.h rename to libraries/networking/src/UserActivityLogger.h From 5e2ad7173c327492456284e7ea3794eec6ebd60e Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 14:27:53 -0700 Subject: [PATCH 12/28] Added functions to logger --- .../networking/src/UserActivityLogger.cpp | 45 +++++++++++++++++-- libraries/networking/src/UserActivityLogger.h | 9 ++-- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 33dfcde576..9356e1bbe6 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -11,7 +11,7 @@ #include "UserActivityLogger.h" -#include +#include "AccountManager.h" #include #include @@ -96,19 +96,58 @@ void UserActivityLogger::logout() { logAction(ACTION_NAME); } -void UserActivityLogger::changedModel() { +void UserActivityLogger::changedDisplayName(QString displayName) { + const QString ACTION_NAME = "changed_display_name"; + QJsonObject actionDetails; + const QString DISPLAY_NAME = "display_name"; + + actionDetails.insert(DISPLAY_NAME, displayName); + + logAction(ACTION_NAME, actionDetails); +} + +void UserActivityLogger::changedModel(QString typeOfModel, QString modelURL) { const QString ACTION_NAME = "changed_model"; QJsonObject actionDetails; + const QString TYPE_OF_MODEL = "type_of_model"; + const QString MODEL_URL = "model_url"; + actionDetails.insert(TYPE_OF_MODEL, typeOfModel); + actionDetails.insert(MODEL_URL, modelURL); logAction(ACTION_NAME, actionDetails); } -void UserActivityLogger::changedDomain() { +void UserActivityLogger::changedDomain(QString domainURL) { const QString ACTION_NAME = "changed_domain"; QJsonObject actionDetails; + const QString DOMAIN_URL = "domain_url"; + actionDetails.insert(DOMAIN_URL, domainURL); logAction(ACTION_NAME, actionDetails); } +void UserActivityLogger::connectedDevice(QString typeOfDevice, QString deviceName) { + const QString ACTION_NAME = "connected_device"; + QJsonObject actionDetails; + const QString TYPE_OF_DEVICE = "type_of_device"; + const QString DEVICE_NAME = "device_name"; + + actionDetails.insert(TYPE_OF_DEVICE, typeOfDevice); + actionDetails.insert(DEVICE_NAME, deviceName); + + logAction(ACTION_NAME, actionDetails); + +} + +void UserActivityLogger::loadedScript(QString scriptName) { + const QString ACTION_NAME = "loaded_script"; + QJsonObject actionDetails; + const QString SCRIPT_NAME = "script_name"; + + actionDetails.insert(SCRIPT_NAME, scriptName); + + logAction(ACTION_NAME, actionDetails); + +} diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 168d448089..8bb5c0aaa6 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -28,10 +28,11 @@ public slots: void login(); void logout(); -// void changedDisplayName(); - void changedModel(); - void changedDomain(); -// void connectedDevice(); + void changedDisplayName(QString displayName); + void changedModel(QString typeOfModel, QString modelURL); + void changedDomain(QString domainURL); + void connectedDevice(QString typeOfDevice, QString deviceName); + void loadedScript(QString scriptName); private slots: void requestFinished(const QJsonObject& object); From e9dffc0681b437b2ba3f89763116aed510751e08 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 14:31:39 -0700 Subject: [PATCH 13/28] Removed testing code --- interface/src/Application.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 6712d1c5f3..3a5db6666d 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -68,7 +68,6 @@ #include "InterfaceVersion.h" #include "Menu.h" #include "ModelUploader.h" -#include "UserActivityLogger.h" #include "Util.h" #include "devices/OculusManager.h" #include "devices/TV3DManager.h" @@ -848,15 +847,10 @@ void Application::keyPressEvent(QKeyEvent* event) { break; case Qt::Key_Space: - - UserActivityLogger::getInstance().login(); - break; resetSensors(); break; case Qt::Key_G: - UserActivityLogger::getInstance().logout(); - break; if (isShifted) { Menu::getInstance()->triggerOption(MenuOption::ObeyEnvironmentalGravity); } From f33ad8a100aa35f92f1237a0b85fd09e3fdd30fa Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 17:10:17 -0700 Subject: [PATCH 14/28] Adding logger calls --- interface/src/Application.cpp | 17 +++++- interface/src/devices/SixenseManager.cpp | 1 + interface/src/ui/PreferencesDialog.cpp | 6 +- libraries/networking/src/DomainHandler.cpp | 2 + .../networking/src/UserActivityLogger.cpp | 56 +++++++++---------- libraries/networking/src/UserActivityLogger.h | 8 ++- 6 files changed, 56 insertions(+), 34 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 3a5db6666d..26f4d25eb7 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include #include @@ -268,6 +269,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // set the account manager's root URL and trigger a login request if we don't have the access token accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); + UserActivityLogger::getInstance().launch(); // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); @@ -396,7 +398,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : } Application::~Application() { - + int DELAI_TIME = 1000; + QEventLoop loop; + QTimer timer; + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + JSONCallbackParameters params; + params.jsonCallbackReceiver = &loop; + params.errorCallbackReceiver = &loop; + params.jsonCallbackMethod = "quit"; + params.errorCallbackMethod = "quit"; + UserActivityLogger::getInstance().close(params); + timer.start(DELAI_TIME); + loop.exec(); + qInstallMessageHandler(NULL); // make sure we don't call the idle timer any more @@ -3558,6 +3572,7 @@ ScriptEngine* Application::loadScript(const QString& scriptName, bool loadScript _scriptEnginesHash.insertMulti(scriptURLString, scriptEngine); _runningScriptsWidget->setRunningScripts(getRunningScripts()); + UserActivityLogger::getInstance().loadedScript(scriptURLString); } // setup the packet senders and jurisdiction listeners of the script engine's scripting interfaces so diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 07536d0af8..558a82cd3d 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -13,6 +13,7 @@ #include "Application.h" #include "SixenseManager.h" +#include "UserActivityLogger.h" #ifdef HAVE_SIXENSE const int CALIBRATION_STATE_IDLE = 0; diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index 55d3360884..9c89826cb9 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -12,8 +12,9 @@ #include "Application.h" #include "Menu.h" -#include "PreferencesDialog.h" #include "ModelsBrowser.h" +#include "PreferencesDialog.h" +#include "UserActivityLogger.h" const int SCROLL_PANEL_BOTTOM_MARGIN = 30; const int OK_BUTTON_RIGHT_MARGIN = 30; @@ -176,6 +177,7 @@ void PreferencesDialog::savePreferences() { QString displayNameStr(ui.displayNameEdit->text()); if (displayNameStr != _displayNameString) { myAvatar->setDisplayName(displayNameStr); + UserActivityLogger::getInstance().changedDisplayName(displayNameStr); shouldDispatchIdentityPacket = true; } @@ -183,6 +185,7 @@ void PreferencesDialog::savePreferences() { if (faceModelURL.toString() != _faceURLString) { // change the faceModelURL in the profile, it will also update this user's BlendFace myAvatar->setFaceModelURL(faceModelURL); + UserActivityLogger::getInstance().changedModel("head", faceModelURL.toString()); shouldDispatchIdentityPacket = true; } @@ -190,6 +193,7 @@ void PreferencesDialog::savePreferences() { if (skeletonModelURL.toString() != _skeletonURLString) { // change the skeletonModelURL in the profile, it will also update this user's Body myAvatar->setSkeletonModelURL(skeletonModelURL); + UserActivityLogger::getInstance().changedModel("skeleton", skeletonModelURL.toString()); shouldDispatchIdentityPacket = true; } diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp index 95689f8e82..f603d21240 100644 --- a/libraries/networking/src/DomainHandler.cpp +++ b/libraries/networking/src/DomainHandler.cpp @@ -11,6 +11,7 @@ #include "NodeList.h" #include "PacketHeaders.h" +#include "UserActivityLogger.h" #include "DomainHandler.h" @@ -83,6 +84,7 @@ void DomainHandler::setHostname(const QString& hostname) { qDebug("Looking up DS hostname %s.", _hostname.toLocal8Bit().constData()); QHostInfo::lookupHost(_hostname, this, SLOT(completedHostnameLookup(const QHostInfo&))); + UserActivityLogger::getInstance().changedDomain(_hostname); emit hostnameChanged(_hostname); } } diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 9356e1bbe6..b797a1b7eb 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -11,8 +11,6 @@ #include "UserActivityLogger.h" -#include "AccountManager.h" - #include #include @@ -26,32 +24,32 @@ UserActivityLogger& UserActivityLogger::getInstance() { UserActivityLogger::UserActivityLogger() { } -void UserActivityLogger::logAction(QString action, QJsonObject details) { +void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCallbackParameters params) { AccountManager& accountManager = AccountManager::getInstance(); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); - if (action != "login") { - QHttpPart actionPart; - actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); - actionPart.setBody(QByteArray().append(action)); - multipart->append(actionPart); - - - if (!details.isEmpty()) { - QHttpPart detailsPart; - detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" - " name=\"action_details\""); - detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); - multipart->append(detailsPart); - } - } - qDebug() << "Loging activity " << action; + QHttpPart actionPart; + actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); + actionPart.setBody(QByteArray().append(action)); + multipart->append(actionPart); - JSONCallbackParameters params; - params.jsonCallbackReceiver = this; - params.jsonCallbackMethod = "requestFinished"; - params.errorCallbackReceiver = this; - params.errorCallbackMethod = "requestError"; + + if (!details.isEmpty()) { + QHttpPart detailsPart; + detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" + " name=\"action_details\""); + detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); + multipart->append(detailsPart); + } + qDebug() << "Loging activity" << action; + qDebug() << AccountManager::getInstance().getAuthURL() << ": " << AccountManager::getInstance().isLoggedIn(); + + if (params.isEmpty()) { + params.jsonCallbackReceiver = this; + params.jsonCallbackMethod = "requestFinished"; + params.errorCallbackReceiver = this; + params.errorCallbackMethod = "requestError"; + } accountManager.authenticatedRequest(USER_ACTIVITY_URL, QNetworkAccessManager::PostOperation, @@ -68,8 +66,8 @@ void UserActivityLogger::requestError(QNetworkReply::NetworkError error,const QS qDebug() << error << ": " << string; } -void UserActivityLogger::login() { - const QString ACTION_NAME = "login"; +void UserActivityLogger::launch() { + const QString ACTION_NAME = "launch"; QJsonObject actionDetails; QString OS_KEY = "OS"; @@ -91,9 +89,9 @@ void UserActivityLogger::login() { logAction(ACTION_NAME, actionDetails); } -void UserActivityLogger::logout() { - const QString ACTION_NAME = ""; - logAction(ACTION_NAME); +void UserActivityLogger::close(JSONCallbackParameters params) { + const QString ACTION_NAME = "close"; + logAction(ACTION_NAME, QJsonObject(), params); } void UserActivityLogger::changedDisplayName(QString displayName) { diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 8bb5c0aaa6..3a36974128 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -12,6 +12,8 @@ #ifndef hifi_UserActivityLogger_h #define hifi_UserActivityLogger_h +#include "AccountManager.h" + #include #include #include @@ -24,10 +26,10 @@ public: static UserActivityLogger& getInstance(); public slots: - void logAction(QString action, QJsonObject details = QJsonObject()); + void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); - void login(); - void logout(); + void launch(); + void close(JSONCallbackParameters params = JSONCallbackParameters()); void changedDisplayName(QString displayName); void changedModel(QString typeOfModel, QString modelURL); void changedDomain(QString domainURL); From d102795314d3bb780e1ca031d62a3c12b0b9f64c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 17:18:49 -0700 Subject: [PATCH 15/28] Added application version to logger --- interface/src/Application.cpp | 2 +- .../networking/src/UserActivityLogger.cpp | 19 +++++++++++-------- libraries/networking/src/UserActivityLogger.h | 2 +- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 26f4d25eb7..c6be40d89e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -269,7 +269,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : // set the account manager's root URL and trigger a login request if we don't have the access token accountManager.setAuthURL(DEFAULT_NODE_AUTH_URL); - UserActivityLogger::getInstance().launch(); + UserActivityLogger::getInstance().launch(applicationVersion()); // once the event loop has started, check and signal for an access token QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection); diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index b797a1b7eb..2b5a4ee31a 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -66,26 +66,29 @@ void UserActivityLogger::requestError(QNetworkReply::NetworkError error,const QS qDebug() << error << ": " << string; } -void UserActivityLogger::launch() { +void UserActivityLogger::launch(QString applicationVersion) { const QString ACTION_NAME = "launch"; QJsonObject actionDetails; - QString OS_KEY = "OS"; - QString VERSION_KEY = "Version"; + QString APP_VERION = "application_version"; + QString OS_KEY = "os"; + QString VERSION_KEY = "version"; #ifdef Q_OS_MAC - actionDetails.insert(OS_KEY, QJsonValue(QString("Mac"))); + actionDetails.insert(OS_KEY, QJsonValue(QString("osx"))); actionDetails.insert(VERSION_KEY, QJsonValue(QSysInfo::macVersion())); #elif Q_OS_LINUX - actionDetails.insert(OS_KEY, QJsonValue(QString("Linux"))); + actionDetails.insert(OS_KEY, QJsonValue(QString("linux"))); #elif Q_OS_WIN - actionDetails.insert(OS_KEY, QJsonValue(QString("Windows"))); + actionDetails.insert(OS_KEY, QJsonValue(QString("windows"))); actionDetails.insert(VERSION_KEY, QJsonValue(QSysInfo::windowsVersion())); #elif Q_OS_UNIX - actionDetails.insert(OS_KEY, QJsonValue(QString("Unknown UNIX"))); + actionDetails.insert(OS_KEY, QJsonValue(QString("unknown unix"))); #else - actionDetails.insert(OS_KEY, QJsonValue(QString("Unknown system"))); + actionDetails.insert(OS_KEY, QJsonValue(QString("unknown system"))); #endif + actionDetails.insert(APP_VERION, applicationVersion); + logAction(ACTION_NAME, actionDetails); } diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 3a36974128..1474107ff3 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -28,7 +28,7 @@ public: public slots: void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); - void launch(); + void launch(QString applicationVersion); void close(JSONCallbackParameters params = JSONCallbackParameters()); void changedDisplayName(QString displayName); void changedModel(QString typeOfModel, QString modelURL); From 780e669fbb22d28bcd8a474b327fe8f5da209471 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 17:46:19 -0700 Subject: [PATCH 16/28] fix for jenkins failure --- libraries/networking/src/UserActivityLogger.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 2b5a4ee31a..992c534e71 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -70,24 +70,21 @@ void UserActivityLogger::launch(QString applicationVersion) { const QString ACTION_NAME = "launch"; QJsonObject actionDetails; - QString APP_VERION = "application_version"; QString OS_KEY = "os"; QString VERSION_KEY = "version"; #ifdef Q_OS_MAC actionDetails.insert(OS_KEY, QJsonValue(QString("osx"))); - actionDetails.insert(VERSION_KEY, QJsonValue(QSysInfo::macVersion())); #elif Q_OS_LINUX actionDetails.insert(OS_KEY, QJsonValue(QString("linux"))); #elif Q_OS_WIN actionDetails.insert(OS_KEY, QJsonValue(QString("windows"))); - actionDetails.insert(VERSION_KEY, QJsonValue(QSysInfo::windowsVersion())); #elif Q_OS_UNIX actionDetails.insert(OS_KEY, QJsonValue(QString("unknown unix"))); #else actionDetails.insert(OS_KEY, QJsonValue(QString("unknown system"))); #endif - actionDetails.insert(APP_VERION, applicationVersion); + actionDetails.insert(VERSION_KEY, applicationVersion); logAction(ACTION_NAME, actionDetails); } From d4ed9d4573f572d339d15f06263b4abad9c3f939 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 17:52:05 -0700 Subject: [PATCH 17/28] Removed preprocessor statements --- libraries/networking/src/UserActivityLogger.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 992c534e71..1ad88bbca0 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -69,21 +69,7 @@ void UserActivityLogger::requestError(QNetworkReply::NetworkError error,const QS void UserActivityLogger::launch(QString applicationVersion) { const QString ACTION_NAME = "launch"; QJsonObject actionDetails; - - QString OS_KEY = "os"; QString VERSION_KEY = "version"; -#ifdef Q_OS_MAC - actionDetails.insert(OS_KEY, QJsonValue(QString("osx"))); -#elif Q_OS_LINUX - actionDetails.insert(OS_KEY, QJsonValue(QString("linux"))); -#elif Q_OS_WIN - actionDetails.insert(OS_KEY, QJsonValue(QString("windows"))); -#elif Q_OS_UNIX - actionDetails.insert(OS_KEY, QJsonValue(QString("unknown unix"))); -#else - actionDetails.insert(OS_KEY, QJsonValue(QString("unknown system"))); -#endif - actionDetails.insert(VERSION_KEY, applicationVersion); logAction(ACTION_NAME, actionDetails); From 7c797449ab29f9a938ce9088d4ee65fdb84ff820 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 19:02:43 -0700 Subject: [PATCH 18/28] Comments + connectedDevice --- interface/src/Application.cpp | 10 +++++++--- interface/src/devices/OculusManager.cpp | 4 ++++ interface/src/devices/SixenseManager.cpp | 5 +++++ interface/src/devices/SixenseManager.h | 1 + libraries/networking/src/UserActivityLogger.cpp | 7 ++++--- 5 files changed, 21 insertions(+), 6 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 702afd4dfe..9fcce81567 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -398,15 +398,19 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : } Application::~Application() { - int DELAI_TIME = 1000; + // In order to get the end of the session, we nned to give the account manager enough time to send the packet. QEventLoop loop; - QTimer timer; - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + // Here we connect the callbacks to stop the event loop JSONCallbackParameters params; params.jsonCallbackReceiver = &loop; params.errorCallbackReceiver = &loop; params.jsonCallbackMethod = "quit"; params.errorCallbackMethod = "quit"; + // In case something goes wrong, we also setup a timer so that the delai is not greater than DELAI_TIME + int DELAI_TIME = 1000; + QTimer timer; + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + // Now we can log it UserActivityLogger::getInstance().close(params); timer.start(DELAI_TIME); loop.exec(); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index b2ee4e8c18..6e17468bb5 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -14,6 +14,7 @@ #include #include +#include #include "Application.h" #include "OculusManager.h" @@ -45,6 +46,9 @@ void OculusManager::connect() { _hmdDevice = *_deviceManager->EnumerateDevices().CreateDevice(); if (_hmdDevice) { + if (!_isConnected) { + UserActivityLogger::getInstance().connectedDevice("hmd", "oculus"); + } _isConnected = true; _sensorDevice = *_hmdDevice->GetSensor(); diff --git a/interface/src/devices/SixenseManager.cpp b/interface/src/devices/SixenseManager.cpp index 558a82cd3d..1b7baf2ee1 100644 --- a/interface/src/devices/SixenseManager.cpp +++ b/interface/src/devices/SixenseManager.cpp @@ -40,6 +40,7 @@ SixenseManager::SixenseManager() { sixenseInit(); #endif + _hydrasConnected = false; _triggerPressed[0] = false; _bumperPressed[0] = false; _oldX[0] = -1; @@ -71,7 +72,11 @@ void SixenseManager::setFilter(bool filter) { void SixenseManager::update(float deltaTime) { #ifdef HAVE_SIXENSE if (sixenseGetNumActiveControllers() == 0) { + _hydrasConnected = false; return; + } else if (!_hydrasConnected) { + _hydrasConnected = true; + UserActivityLogger::getInstance().connectedDevice("spatial_controller", "hydra"); } MyAvatar* avatar = Application::getInstance()->getAvatar(); Hand* hand = avatar->getHand(); diff --git a/interface/src/devices/SixenseManager.h b/interface/src/devices/SixenseManager.h index 8803c2c006..8ca27ef77c 100644 --- a/interface/src/devices/SixenseManager.h +++ b/interface/src/devices/SixenseManager.h @@ -71,6 +71,7 @@ private: float _lastDistance; #endif + bool _hydrasConnected; quint64 _lastMovement; glm::vec3 _amountMoved; diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 1ad88bbca0..4a0b065c0a 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -28,12 +28,13 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall AccountManager& accountManager = AccountManager::getInstance(); QHttpMultiPart* multipart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + // Adding the action name QHttpPart actionPart; actionPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data; name=\"action_name\""); actionPart.setBody(QByteArray().append(action)); multipart->append(actionPart); - + // If there are action details, add them to the multipart if (!details.isEmpty()) { QHttpPart detailsPart; detailsPart.setHeader(QNetworkRequest::ContentDispositionHeader, "form-data;" @@ -41,9 +42,9 @@ void UserActivityLogger::logAction(QString action, QJsonObject details, JSONCall detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact)); multipart->append(detailsPart); } - qDebug() << "Loging activity" << action; - qDebug() << AccountManager::getInstance().getAuthURL() << ": " << AccountManager::getInstance().isLoggedIn(); + qDebug() << "Logging activity" << action; + // if no callbacks specified, call our owns if (params.isEmpty()) { params.jsonCallbackReceiver = this; params.jsonCallbackMethod = "requestFinished"; From 2015861e130fa40c17f101f2ae6f0543f0e3cc50 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Mon, 30 Jun 2014 20:10:53 -0700 Subject: [PATCH 19/28] Fixed spelling mistake --- interface/src/Application.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9fcce81567..d79c30cacb 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -406,13 +406,13 @@ Application::~Application() { params.errorCallbackReceiver = &loop; params.jsonCallbackMethod = "quit"; params.errorCallbackMethod = "quit"; - // In case something goes wrong, we also setup a timer so that the delai is not greater than DELAI_TIME - int DELAI_TIME = 1000; + // In case something goes wrong, we also setup a timer so that the delai is not greater than DELAY_TIME + int DELAY_TIME = 1000; QTimer timer; connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); // Now we can log it UserActivityLogger::getInstance().close(params); - timer.start(DELAI_TIME); + timer.start(DELAY_TIME); loop.exec(); qInstallMessageHandler(NULL); From 09cf01e097ea9ecd7158f0925e16cfb5199b7133 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 1 Jul 2014 12:42:56 -0700 Subject: [PATCH 20/28] ModelOverlay functionnal + bounding box --- interface/src/ui/overlays/ModelOverlay.cpp | 115 +++++++++++++++++++++ interface/src/ui/overlays/ModelOverlay.h | 38 +++++++ interface/src/ui/overlays/Overlays.cpp | 7 ++ 3 files changed, 160 insertions(+) create mode 100644 interface/src/ui/overlays/ModelOverlay.cpp create mode 100644 interface/src/ui/overlays/ModelOverlay.h diff --git a/interface/src/ui/overlays/ModelOverlay.cpp b/interface/src/ui/overlays/ModelOverlay.cpp new file mode 100644 index 0000000000..bc0cc720c2 --- /dev/null +++ b/interface/src/ui/overlays/ModelOverlay.cpp @@ -0,0 +1,115 @@ +// +// ModelOverlay.cpp +// +// +// Created by Clement on 6/30/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +#include "../../Menu.h" + +#include "ModelOverlay.h" + +ModelOverlay::ModelOverlay() + : _model(), + _scale(1.0f), + _updateModel(false) { + _model.init(); +} + +void ModelOverlay::update(float deltatime) { + if (_updateModel) { + _updateModel = false; + + _model.setScaleToFit(true, _scale); + _model.setSnapModelToCenter(true); + _model.setRotation(_rotation); + _model.setTranslation(_position); + _model.setURL(_url); + _model.simulate(deltatime, true); + } else { + _model.simulate(deltatime); + } +} + +void ModelOverlay::render() { + if (_model.isActive()) { + + if (_model.isRenderable()) { + _model.render(_alpha); + } + bool displayModelBounds = Menu::getInstance()->isOptionChecked(MenuOption::DisplayModelBounds); + if (displayModelBounds) { + glm::vec3 unRotatedMinimum = _model.getUnscaledMeshExtents().minimum; + glm::vec3 unRotatedMaximum = _model.getUnscaledMeshExtents().maximum; + glm::vec3 unRotatedExtents = unRotatedMaximum - unRotatedMinimum; + + float width = unRotatedExtents.x; + float height = unRotatedExtents.y; + float depth = unRotatedExtents.z; + + Extents rotatedExtents = _model.getUnscaledMeshExtents(); + calculateRotatedExtents(rotatedExtents, _rotation); + + glm::vec3 rotatedSize = rotatedExtents.maximum - rotatedExtents.minimum; + + const glm::vec3& modelScale = _model.getScale(); + + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + + // draw the rotated bounding cube + glColor4f(0.0f, 0.0f, 1.0f, 1.0f); + glPushMatrix(); { + glScalef(rotatedSize.x * modelScale.x, rotatedSize.y * modelScale.y, rotatedSize.z * modelScale.z); + glutWireCube(1.0); + } glPopMatrix(); + + // draw the model relative bounding box + glm::vec3 axis = glm::axis(_rotation); + glRotatef(glm::degrees(glm::angle(_rotation)), axis.x, axis.y, axis.z); + glScalef(width * modelScale.x, height * modelScale.y, depth * modelScale.z); + glColor3f(0.0f, 1.0f, 0.0f); + glutWireCube(1.0); + + } glPopMatrix(); + } + } +} + +void ModelOverlay::setProperties(const QScriptValue &properties) { + Base3DOverlay::setProperties(properties); + + QScriptValue urlValue = properties.property("url"); + if (urlValue.isValid()) { + _url = urlValue.toVariant().toString(); + _updateModel = true; + } + + QScriptValue scaleValue = properties.property("scale"); + if (scaleValue.isValid()) { + _scale = scaleValue.toVariant().toFloat(); + _updateModel = true; + } + + QScriptValue rotationValue = properties.property("rotation"); + if (rotationValue.isValid()) { + QScriptValue x = rotationValue.property("x"); + QScriptValue y = rotationValue.property("y"); + QScriptValue z = rotationValue.property("z"); + QScriptValue w = rotationValue.property("w"); + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + _rotation.x = x.toVariant().toFloat(); + _rotation.y = y.toVariant().toFloat(); + _rotation.z = z.toVariant().toFloat(); + _rotation.w = w.toVariant().toFloat(); + } + _updateModel = true; + } + + if (properties.property("position").isValid()) { + _updateModel = true; + } +} \ No newline at end of file diff --git a/interface/src/ui/overlays/ModelOverlay.h b/interface/src/ui/overlays/ModelOverlay.h new file mode 100644 index 0000000000..e0f979676f --- /dev/null +++ b/interface/src/ui/overlays/ModelOverlay.h @@ -0,0 +1,38 @@ +// +// ModelOverlay.h +// +// +// Created by Clement on 6/30/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ModelOverlay_h +#define hifi_ModelOverlay_h + +#include "Base3DOverlay.h" + +#include "../../renderer/Model.h" + +class ModelOverlay : public Base3DOverlay { + Q_OBJECT +public: + ModelOverlay(); + + virtual void update(float deltatime); + virtual void render(); + virtual void setProperties(const QScriptValue& properties); +private: + + Model _model; + + QUrl _url; + glm::quat _rotation; + float _scale; + + bool _updateModel; +}; + +#endif // hifi_ModelOverlay_h \ No newline at end of file diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 95f4f2b2fe..7fd1b44480 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -17,6 +17,7 @@ #include "Sphere3DOverlay.h" #include "TextOverlay.h" #include "LocalVoxelsOverlay.h" +#include "ModelOverlay.h" Overlays::Overlays() : _nextOverlayID(1) { } @@ -156,6 +157,12 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; + } else if (type == "model") { + thisOverlay = new ModelOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; + is3D = true; } if (created) { From 547431d52ee85e7f976e4552179324080fcd251c Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 1 Jul 2014 12:43:28 -0700 Subject: [PATCH 21/28] Coding Standard -- static_cast --- interface/src/models/ModelTreeRenderer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/models/ModelTreeRenderer.cpp b/interface/src/models/ModelTreeRenderer.cpp index 9c4b08fd99..78107db699 100644 --- a/interface/src/models/ModelTreeRenderer.cpp +++ b/interface/src/models/ModelTreeRenderer.cpp @@ -135,7 +135,7 @@ void ModelTreeRenderer::renderElement(OctreeElement* element, RenderArgs* args) args->_elementsTouched++; // actually render it here... // we need to iterate the actual modelItems of the element - ModelTreeElement* modelTreeElement = (ModelTreeElement*)element; + ModelTreeElement* modelTreeElement = static_cast(element); QList& modelItems = modelTreeElement->getModels(); From e9826250cb9131b5bdf7c8a972fe41cd50c6a40b Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 1 Jul 2014 12:57:39 -0700 Subject: [PATCH 22/28] CR --- interface/src/Application.cpp | 16 +------------- .../networking/src/UserActivityLogger.cpp | 21 +++++++++++++++++-- libraries/networking/src/UserActivityLogger.h | 2 +- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c222b363b8..0f8fbbae56 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -398,22 +398,8 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) : } Application::~Application() { - // In order to get the end of the session, we nned to give the account manager enough time to send the packet. - QEventLoop loop; - // Here we connect the callbacks to stop the event loop - JSONCallbackParameters params; - params.jsonCallbackReceiver = &loop; - params.errorCallbackReceiver = &loop; - params.jsonCallbackMethod = "quit"; - params.errorCallbackMethod = "quit"; - // In case something goes wrong, we also setup a timer so that the delai is not greater than DELAY_TIME int DELAY_TIME = 1000; - QTimer timer; - connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); - // Now we can log it - UserActivityLogger::getInstance().close(params); - timer.start(DELAY_TIME); - loop.exec(); + UserActivityLogger::getInstance().close(DELAY_TIME); qInstallMessageHandler(NULL); diff --git a/libraries/networking/src/UserActivityLogger.cpp b/libraries/networking/src/UserActivityLogger.cpp index 4a0b065c0a..aa18cb43ee 100644 --- a/libraries/networking/src/UserActivityLogger.cpp +++ b/libraries/networking/src/UserActivityLogger.cpp @@ -11,8 +11,10 @@ #include "UserActivityLogger.h" -#include +#include #include +#include +#include static const QString USER_ACTIVITY_URL = "/api/v1/user_activities"; @@ -76,9 +78,24 @@ void UserActivityLogger::launch(QString applicationVersion) { logAction(ACTION_NAME, actionDetails); } -void UserActivityLogger::close(JSONCallbackParameters params) { +void UserActivityLogger::close(int delayTime) { const QString ACTION_NAME = "close"; + + // In order to get the end of the session, we need to give the account manager enough time to send the packet. + QEventLoop loop; + // Here we connect the callbacks to stop the event loop + JSONCallbackParameters params; + params.jsonCallbackReceiver = &loop; + params.errorCallbackReceiver = &loop; + params.jsonCallbackMethod = "quit"; + params.errorCallbackMethod = "quit"; + // In case something goes wrong, we also setup a timer so that the delai is not greater than delayTime + QTimer timer; + connect(&timer, &QTimer::timeout, &loop, &QEventLoop::quit); + // Now we can log it logAction(ACTION_NAME, QJsonObject(), params); + timer.start(delayTime); + loop.exec(); } void UserActivityLogger::changedDisplayName(QString displayName) { diff --git a/libraries/networking/src/UserActivityLogger.h b/libraries/networking/src/UserActivityLogger.h index 1474107ff3..4823143234 100644 --- a/libraries/networking/src/UserActivityLogger.h +++ b/libraries/networking/src/UserActivityLogger.h @@ -29,7 +29,7 @@ public slots: void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters()); void launch(QString applicationVersion); - void close(JSONCallbackParameters params = JSONCallbackParameters()); + void close(int delayTime); void changedDisplayName(QString displayName); void changedModel(QString typeOfModel, QString modelURL); void changedDomain(QString domainURL); From 19be406b1acab5f0074683db9228a6581e50b042 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 1 Jul 2014 15:09:30 -0700 Subject: [PATCH 23/28] Added billboards overlays --- .../src/ui/overlays/BillboardOverlay.cpp | 139 ++++++++++++++++++ interface/src/ui/overlays/BillboardOverlay.h | 47 ++++++ interface/src/ui/overlays/Overlays.cpp | 11 +- 3 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 interface/src/ui/overlays/BillboardOverlay.cpp create mode 100644 interface/src/ui/overlays/BillboardOverlay.h diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp new file mode 100644 index 0000000000..0f0733afc3 --- /dev/null +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -0,0 +1,139 @@ +// +// BillboardOverlay.cpp +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "../../Application.h" + +#include "BillboardOverlay.h" + +BillboardOverlay::BillboardOverlay() +: _manager(NULL), + _scale(1.0f), + _isFacingAvatar(true) { +} + +void BillboardOverlay::update(float deltatime) { +} + +void BillboardOverlay::render() { + if (_billboard.isEmpty()) { + return; + } + if (!_billboardTexture) { + QImage image = QImage::fromData(_billboard); + if (image.format() != QImage::Format_ARGB32) { + image = image.convertToFormat(QImage::Format_ARGB32); + } + _size = image.size(); + _billboardTexture.reset(new Texture()); + glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _size.width(), _size.height(), 0, + GL_BGRA, GL_UNSIGNED_BYTE, image.constBits()); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + } else { + glBindTexture(GL_TEXTURE_2D, _billboardTexture->getID()); + } + + glEnable(GL_ALPHA_TEST); + glAlphaFunc(GL_GREATER, 0.5f); + + glEnable(GL_TEXTURE_2D); + glDisable(GL_LIGHTING); + + glPushMatrix(); { + glTranslatef(_position.x, _position.y, _position.z); + if (_isFacingAvatar) { + // rotate about vertical to face the camera + glm::quat rotation = Application::getInstance()->getCamera()->getRotation(); + rotation *= glm::angleAxis(glm::pi(), glm::vec3(0.0f, 1.0f, 0.0f)); + glm::vec3 axis = glm::axis(rotation); + glRotatef(glm::degrees(glm::angle(rotation)), axis.x, axis.y, axis.z); + } else { + glm::vec3 axis = glm::axis(_rotation); + glRotatef(glm::degrees(glm::angle(_rotation)), axis.x, axis.y, axis.z); + } + glScalef(_scale, _scale, _scale); + + float maxSize = glm::max(_size.width(), _size.height()); + float x = _size.width() / (2.0f * maxSize); + float y = -_size.height() / (2.0f * maxSize); + + glColor3f(1.0f, 1.0f, 1.0f); + glBegin(GL_QUADS); { + glTexCoord2f(0.0f, 0.0f); + glVertex2f(-x, -y); + glTexCoord2f(1.0f, 0.0f); + glVertex2f(x, -y); + glTexCoord2f(1.0f, 1.0f); + glVertex2f(x, y); + glTexCoord2f(0.0f, 1.0f); + glVertex2f(-x, y); + } glEnd(); + + } glPopMatrix(); + + glDisable(GL_TEXTURE_2D); + glEnable(GL_LIGHTING); + glDisable(GL_ALPHA_TEST); + + glBindTexture(GL_TEXTURE_2D, 0); +} + +void BillboardOverlay::setProperties(const QScriptValue &properties) { + Base3DOverlay::setProperties(properties); + + QScriptValue urlValue = properties.property("url"); + if (urlValue.isValid()) { + _url = urlValue.toVariant().toString(); + + setBillboardURL(_url); + } + + QScriptValue scaleValue = properties.property("scale"); + if (scaleValue.isValid()) { + _scale = scaleValue.toVariant().toFloat(); + } + + QScriptValue rotationValue = properties.property("rotation"); + if (rotationValue.isValid()) { + QScriptValue x = rotationValue.property("x"); + QScriptValue y = rotationValue.property("y"); + QScriptValue z = rotationValue.property("z"); + QScriptValue w = rotationValue.property("w"); + if (x.isValid() && y.isValid() && z.isValid() && w.isValid()) { + _rotation.x = x.toVariant().toFloat(); + _rotation.y = y.toVariant().toFloat(); + _rotation.z = z.toVariant().toFloat(); + _rotation.w = w.toVariant().toFloat(); + } + } + + QScriptValue isFacingAvatarValue = properties.property("isFacingAvatar"); + if (isFacingAvatarValue.isValid()) { + _isFacingAvatar = isFacingAvatarValue.toVariant().toBool(); + } +} + +// TODO: handle setting image multiple times, how do we manage releasing the bound texture? +void BillboardOverlay::setBillboardURL(const QUrl url) { + // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? + _manager->deleteLater(); + _manager = new QNetworkAccessManager(); + connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); + _manager->get(QNetworkRequest(url)); +} + +void BillboardOverlay::replyFinished(QNetworkReply* reply) { + // replace our byte array with the downloaded data + _billboard = reply->readAll(); + _manager->deleteLater(); + _manager = NULL; +} diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h new file mode 100644 index 0000000000..2cbe4f7afd --- /dev/null +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -0,0 +1,47 @@ +// +// BillboardOverlay.h +// +// +// Created by Clement on 7/1/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_BillboardOverlay_h +#define hifi_BillboardOverlay_h + +#include +#include + +#include "Base3DOverlay.h" +#include "../../renderer/TextureCache.h" + +class BillboardOverlay : public Base3DOverlay { + Q_OBJECT +public: + BillboardOverlay(); + + virtual void update(float deltatime); + virtual void render(); + virtual void setProperties(const QScriptValue& properties); + +private slots: + void replyFinished(QNetworkReply* reply); + +private: + void setBillboardURL(const QUrl url); + + QNetworkAccessManager* _manager; + QUrl _url; + QByteArray _billboard; + QSize _size; + QScopedPointer _billboardTexture; + + glm::quat _rotation; + float _scale; + bool _isFacingAvatar; +}; + +#endif // hifi_BillboardOverlay_h \ No newline at end of file diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index 7fd1b44480..dd483da27a 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -10,14 +10,15 @@ #include +#include "BillboardOverlay.h" #include "Cube3DOverlay.h" #include "ImageOverlay.h" #include "Line3DOverlay.h" +#include "LocalVoxelsOverlay.h" +#include "ModelOverlay.h" #include "Overlays.h" #include "Sphere3DOverlay.h" #include "TextOverlay.h" -#include "LocalVoxelsOverlay.h" -#include "ModelOverlay.h" Overlays::Overlays() : _nextOverlayID(1) { } @@ -163,6 +164,12 @@ unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& prope thisOverlay->setProperties(properties); created = true; is3D = true; + } else if (type == "billboard") { + thisOverlay = new BillboardOverlay(); + thisOverlay->init(_parent); + thisOverlay->setProperties(properties); + created = true; + is3D = true; } if (created) { From 12ebfd92d1af8bd1e8b3c1acd8d19cbab0d4e8e6 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 1 Jul 2014 15:12:31 -0700 Subject: [PATCH 24/28] Memory leak --- interface/src/ui/overlays/ImageOverlay.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/src/ui/overlays/ImageOverlay.cpp b/interface/src/ui/overlays/ImageOverlay.cpp index aa4766488a..79b1b23de5 100644 --- a/interface/src/ui/overlays/ImageOverlay.cpp +++ b/interface/src/ui/overlays/ImageOverlay.cpp @@ -19,7 +19,7 @@ #include "ImageOverlay.h" ImageOverlay::ImageOverlay() : - _manager(0), + _manager(NULL), _textureID(0), _renderImage(false), _textureBound(false), @@ -37,6 +37,7 @@ ImageOverlay::~ImageOverlay() { // TODO: handle setting image multiple times, how do we manage releasing the bound texture? void ImageOverlay::setImageURL(const QUrl& url) { // TODO: are we creating too many QNetworkAccessManager() when multiple calls to setImageURL are made? + _manager->deleteLater(); _manager = new QNetworkAccessManager(); connect(_manager, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*))); _manager->get(QNetworkRequest(url)); @@ -49,6 +50,7 @@ void ImageOverlay::replyFinished(QNetworkReply* reply) { _textureImage.loadFromData(rawData); _renderImage = true; _manager->deleteLater(); + _manager = NULL; } void ImageOverlay::render() { From d63ad86db08e658de5a5082d11d859fb6718c9c3 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 1 Jul 2014 15:37:52 -0700 Subject: [PATCH 25/28] Removed unnecessary function --- interface/src/ui/overlays/BillboardOverlay.cpp | 3 --- interface/src/ui/overlays/BillboardOverlay.h | 1 - 2 files changed, 4 deletions(-) diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index 0f0733afc3..40de565155 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -19,9 +19,6 @@ BillboardOverlay::BillboardOverlay() _isFacingAvatar(true) { } -void BillboardOverlay::update(float deltatime) { -} - void BillboardOverlay::render() { if (_billboard.isEmpty()) { return; diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index 2cbe4f7afd..473e8a066f 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -23,7 +23,6 @@ class BillboardOverlay : public Base3DOverlay { public: BillboardOverlay(); - virtual void update(float deltatime); virtual void render(); virtual void setProperties(const QScriptValue& properties); From 49d54dd6f0d900e92325006ab53620cebee16fb2 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Jul 2014 09:33:51 -0700 Subject: [PATCH 26/28] Add Model::inverseKinematics() for stable hand IK (removed constraint enforcement which was causing problems, will add that back later) --- interface/src/avatar/SkeletonModel.cpp | 15 +-- interface/src/renderer/JointState.cpp | 55 ++++++++--- interface/src/renderer/JointState.h | 22 ++++- interface/src/renderer/Model.cpp | 122 +++++++++++++++++++++++++ interface/src/renderer/Model.h | 2 + 5 files changed, 195 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/SkeletonModel.cpp b/interface/src/avatar/SkeletonModel.cpp index 0229d686d4..e5751360b5 100644 --- a/interface/src/avatar/SkeletonModel.cpp +++ b/interface/src/avatar/SkeletonModel.cpp @@ -222,8 +222,7 @@ void SkeletonModel::applyPalmData(int jointIndex, PalmData& palm) { // lock hand to forearm by slamming its rotation (in parent-frame) to identity _jointStates[jointIndex].setRotationInParentFrame(glm::quat()); } else { - setJointPosition(jointIndex, palmPosition, palmRotation, - true, -1, false, glm::vec3(0.0f, -1.0f, 0.0f), PALM_PRIORITY); + inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY); } } @@ -642,13 +641,15 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) { // compute the default transforms and slam the ragdoll positions accordingly // (which puts the shapes where we want them) - transforms[0] = _jointStates[0].getTransform(); - _ragdollPoints[0]._position = extractTranslation(transforms[0]); - _ragdollPoints[0]._lastPosition = _ragdollPoints[0]._position; - for (int i = 1; i < numJoints; i++) { + for (int i = 0; i < numJoints; i++) { const FBXJoint& joint = geometry.joints.at(i); int parentIndex = joint.parentIndex; - assert(parentIndex != -1); + if (parentIndex == -1) { + transforms[i] = _jointStates[i].getTransform(); + _ragdollPoints[i]._position = extractTranslation(transforms[i]); + _ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position; + continue; + } glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation; transforms[i] = transforms[parentIndex] * glm::translate(joint.translation) diff --git a/interface/src/renderer/JointState.cpp b/interface/src/renderer/JointState.cpp index f75bbe8bec..8e4aecdf51 100644 --- a/interface/src/renderer/JointState.cpp +++ b/interface/src/renderer/JointState.cpp @@ -17,7 +17,8 @@ JointState::JointState() : _animationPriority(0.0f), - _fbxJoint(NULL) { + _fbxJoint(NULL), + _isConstrained(false) { } void JointState::copyState(const JointState& state) { @@ -37,6 +38,10 @@ void JointState::setFBXJoint(const FBXJoint* joint) { _rotationInParentFrame = joint->rotation; // NOTE: JointState does not own the FBXJoint to which it points. _fbxJoint = joint; + // precompute whether there are any constraints or not + float distanceMin = glm::distance(_fbxJoint->rotationMin, glm::vec3(-PI)); + float distanceMax = glm::distance(_fbxJoint->rotationMax, glm::vec3(PI)); + _isConstrained = distanceMin > EPSILON || distanceMax > EPSILON; } void JointState::computeTransform(const glm::mat4& parentTransform) { @@ -60,16 +65,16 @@ glm::quat JointState::getRotationFromBindToModelFrame() const { void JointState::restoreRotation(float fraction, float priority) { assert(_fbxJoint != NULL); if (priority == _animationPriority || _animationPriority == 0.0f) { - _rotationInParentFrame = safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction); + setRotationInParentFrame(safeMix(_rotationInParentFrame, _fbxJoint->rotation, fraction)); _animationPriority = 0.0f; } } void JointState::setRotationFromBindFrame(const glm::quat& rotation, float priority) { + // rotation is from bind- to model-frame assert(_fbxJoint != NULL); if (priority >= _animationPriority) { - // rotation is from bind- to model-frame - _rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation); + setRotationInParentFrame(_rotationInParentFrame * glm::inverse(_rotation) * rotation * glm::inverse(_fbxJoint->inverseBindRotation)); _animationPriority = priority; } } @@ -88,24 +93,50 @@ void JointState::setRotation(const glm::quat& rotation, bool constrain, float pr } void JointState::applyRotationDelta(const glm::quat& delta, bool constrain, float priority) { - // NOTE: delta is in jointParent-frame + // NOTE: delta is in model-frame assert(_fbxJoint != NULL); if (priority < _animationPriority) { return; } _animationPriority = priority; - if (!constrain || (_fbxJoint->rotationMin == glm::vec3(-PI, -PI, -PI) && - _fbxJoint->rotationMax == glm::vec3(PI, PI, PI))) { + if (!constrain || !_isConstrained) { // no constraints _rotationInParentFrame = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; _rotation = delta * _rotation; return; } - glm::quat targetRotation = delta * _rotation; - glm::vec3 eulers = safeEulerAngles(_rotationInParentFrame * glm::inverse(_rotation) * targetRotation); - glm::quat newRotation = glm::quat(glm::clamp(eulers, _fbxJoint->rotationMin, _fbxJoint->rotationMax)); - _rotation = _rotation * glm::inverse(_rotationInParentFrame) * newRotation; - _rotationInParentFrame = newRotation; + glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; + setRotationInParentFrame(targetRotation); +} + +/// Applies delta rotation to joint but mixes a little bit of the default pose as well. +/// This helps keep an IK solution stable. +void JointState::mixRotationDelta(const glm::quat& delta, float mixFactor, float priority) { + // NOTE: delta is in model-frame + assert(_fbxJoint != NULL); + if (priority < _animationPriority) { + return; + } + _animationPriority = priority; + glm::quat targetRotation = _rotationInParentFrame * glm::inverse(_rotation) * delta * _rotation; + if (mixFactor > 0.0f && mixFactor <= 1.0f) { + targetRotation = safeMix(targetRotation, _fbxJoint->rotation, mixFactor); + } + setRotationInParentFrame(targetRotation); +} + + +glm::quat JointState::computeParentRotation() const { + // R = Rp * Rpre * r * Rpost + // Rp = R * (Rpre * r * Rpost)^ + return _rotation * glm::inverse(_fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation); +} + +void JointState::setRotationInParentFrame(const glm::quat& targetRotation) { + glm::quat parentRotation = computeParentRotation(); + _rotationInParentFrame = targetRotation; + // R' = Rp * Rpre * r' * Rpost + _rotation = parentRotation * _fbxJoint->preRotation * _rotationInParentFrame * _fbxJoint->postRotation; } const glm::vec3& JointState::getDefaultTranslationInParentFrame() const { diff --git a/interface/src/renderer/JointState.h b/interface/src/renderer/JointState.h index b42132d0a0..8412cfd0cb 100644 --- a/interface/src/renderer/JointState.h +++ b/interface/src/renderer/JointState.h @@ -46,9 +46,19 @@ public: /// \param rotation rotation of joint in model-frame void setRotation(const glm::quat& rotation, bool constrain, float priority); - /// \param delta is in the jointParent-frame + /// \param delta is in the model-frame void applyRotationDelta(const glm::quat& delta, bool constrain = true, float priority = 1.0f); + /// Applies delta rotation to joint but mixes a little bit of the default pose as well. + /// This helps keep an IK solution stable. + /// \param delta rotation change in model-frame + /// \param mixFactor fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) + /// \param priority priority level of this animation blend + void mixRotationDelta(const glm::quat& delta, float mixFactor, float priority = 1.0f); + + /// Blends a fraciton of default pose into joint rotation. + /// \param fraction fraction in range [0,1] of how much default pose to blend in (0 is none, 1 is all) + /// \param priority priority level of this animation blend void restoreRotation(float fraction, float priority); /// \param rotation is from bind- to model-frame @@ -56,7 +66,7 @@ public: /// NOTE: the JointState's model-frame transform/rotation are NOT updated! void setRotationFromBindFrame(const glm::quat& rotation, float priority); - void setRotationInParentFrame(const glm::quat& rotation) { _rotationInParentFrame = rotation; } + void setRotationInParentFrame(const glm::quat& targetRotation); const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; } const glm::vec3& getDefaultTranslationInParentFrame() const; @@ -69,6 +79,13 @@ public: float _animationPriority; // the priority of the animation affecting this joint private: + /// \return parent model-frame rotation + // (used to keep _rotation consistent when modifying _rotationInWorldFrame directly) + glm::quat computeParentRotation() const; + + /// debug helper function + void loadBindRotation(); + glm::mat4 _transform; // joint- to model-frame glm::quat _rotation; // joint- to model-frame glm::quat _rotationInParentFrame; // joint- to parentJoint-frame @@ -78,6 +95,7 @@ private: glm::quat _visibleRotationInParentFrame; const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint + bool _isConstrained; }; #endif // hifi_JointState_h diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index 5d82b111b7..ffca2c8ea6 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -1100,6 +1100,128 @@ bool Model::setJointPosition(int jointIndex, const glm::vec3& position, const gl return true; } +void Model::inverseKinematics(int endIndex, glm::vec3 targetPosition, const glm::quat& targetRotation, float priority) { + // NOTE: targetRotation is from bind- to model-frame + + if (endIndex == -1 || _jointStates.isEmpty()) { + return; + } + + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + const QVector& freeLineage = geometry.joints.at(endIndex).freeLineage; + if (freeLineage.isEmpty()) { + return; + } + int numFree = freeLineage.size(); + + // store and remember topmost parent transform + glm::mat4 topParentTransform; + { + int index = freeLineage.last(); + const JointState& state = _jointStates.at(index); + const FBXJoint& joint = state.getFBXJoint(); + int parentIndex = joint.parentIndex; + if (parentIndex == -1) { + const FBXGeometry& geometry = _geometry->getFBXGeometry(); + topParentTransform = glm::scale(_scale) * glm::translate(_offset) * geometry.offset; + } else { + topParentTransform = _jointStates[parentIndex].getTransform(); + } + } + + // this is a cyclic coordinate descent algorithm: see + // http://www.ryanjuckett.com/programming/animation/21-cyclic-coordinate-descent-in-2d + + // keep track of the position of the end-effector + JointState& endState = _jointStates[endIndex]; + glm::vec3 endPosition = endState.getPosition(); + float distanceToGo = glm::distance(targetPosition, endPosition); + + const int MAX_ITERATION_COUNT = 2; + const float ACCEPTABLE_IK_ERROR = 0.005f; // 5mm + int numIterations = 0; + do { + ++numIterations; + // moving up, rotate each free joint to get endPosition closer to target + for (int j = 1; j < numFree; j++) { + int nextIndex = freeLineage.at(j); + JointState& nextState = _jointStates[nextIndex]; + FBXJoint nextJoint = nextState.getFBXJoint(); + if (! nextJoint.isFree) { + continue; + } + + glm::vec3 pivot = nextState.getPosition(); + glm::vec3 leverArm = endPosition - pivot; + float leverLength = glm::length(leverArm); + if (leverLength < EPSILON) { + continue; + } + glm::quat deltaRotation = rotationBetween(leverArm, targetPosition - pivot); + + /* DON'T REMOVE! This code provides the gravitational effect on the IK solution. + * It is commented out for the moment because we're blending the IK solution with + * the default pose which provides similar stability, but we might want to use + * gravity again later. + + // We want to mix the shortest rotation with one that will pull the system down with gravity. + // So we compute a simplified center of mass, where each joint has a mass of 1.0 and we don't + // bother averaging it because we only need direction. + if (j > 1) { + + glm::vec3 centerOfMass(0.0f); + for (int k = 0; k < j; ++k) { + int massIndex = freeLineage.at(k); + centerOfMass += _jointStates[massIndex].getPosition() - pivot; + } + // the gravitational effect is a rotation that tends to align the two cross products + const glm::vec3 worldAlignment = glm::vec3(0.0f, -1.f, 0.0f); + glm::quat gravityDelta = rotationBetween(glm::cross(centerOfMass, leverArm), + glm::cross(worldAlignment, leverArm)); + + float gravityAngle = glm::angle(gravityDelta); + const float MIN_GRAVITY_ANGLE = 0.1f; + float mixFactor = 0.5f; + if (gravityAngle < MIN_GRAVITY_ANGLE) { + // the final rotation is a mix of the two + mixFactor = 0.5f * gravityAngle / MIN_GRAVITY_ANGLE; + } + deltaRotation = safeMix(deltaRotation, gravityDelta, mixFactor); + } + */ + + // Apply the rotation, but use mixRotationDelta() which blends a bit of the default pose + // at in the process. This provides stability to the IK solution and removes the necessity + // for the gravity effect. + glm::quat oldNextRotation = nextState.getRotation(); + float mixFactor = 0.03f; + nextState.mixRotationDelta(deltaRotation, mixFactor, priority); + + // measure the result of the rotation which may have been modified by + // blending and constraints + glm::quat actualDelta = nextState.getRotation() * glm::inverse(oldNextRotation); + endPosition = pivot + actualDelta * leverArm; + } + + // recompute transforms from the top down + glm::mat4 parentTransform = topParentTransform; + for (int j = numFree - 1; j >= 0; --j) { + JointState& freeState = _jointStates[freeLineage.at(j)]; + freeState.computeTransform(parentTransform); + parentTransform = freeState.getTransform(); + } + + // measure our success + endPosition = endState.getPosition(); + distanceToGo = glm::distance(targetPosition, endPosition); + } while (numIterations < MAX_ITERATION_COUNT && distanceToGo < ACCEPTABLE_IK_ERROR); + + // set final rotation of the end joint + endState.setRotationFromBindFrame(targetRotation, priority); + + _shapesAreDirty = !_shapes.isEmpty(); +} + bool Model::restoreJointPosition(int jointIndex, float fraction, float priority) { if (jointIndex == -1 || _jointStates.isEmpty()) { return false; diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 8572831d8b..835207b7eb 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -194,6 +194,8 @@ protected: bool setJointPosition(int jointIndex, const glm::vec3& position, const glm::quat& rotation = glm::quat(), bool useRotation = false, int lastFreeIndex = -1, bool allIntermediatesFree = false, const glm::vec3& alignment = glm::vec3(0.0f, -1.0f, 0.0f), float priority = 1.0f); + + void inverseKinematics(int jointIndex, glm::vec3 position, const glm::quat& rotation, float priority); /// Restores the indexed joint to its default position. /// \param fraction the fraction of the default position to apply (i.e., 0.25f to slerp one fourth of the way to From ec80ccdb9cfab7d0ded131c982ac3bf85f96d42a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Jul 2014 10:49:05 -0700 Subject: [PATCH 27/28] remove warning about signed/unsigned comparison --- libraries/audio/src/PositionalAudioRingBuffer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/audio/src/PositionalAudioRingBuffer.cpp b/libraries/audio/src/PositionalAudioRingBuffer.cpp index 546ed97fe2..e11d73358c 100644 --- a/libraries/audio/src/PositionalAudioRingBuffer.cpp +++ b/libraries/audio/src/PositionalAudioRingBuffer.cpp @@ -212,7 +212,7 @@ bool PositionalAudioRingBuffer::shouldBeAddedToMix() { } return false; - } else if (samplesAvailable() < (unsigned int)samplesPerFrame) { + } else if (samplesAvailable() < samplesPerFrame) { // if the buffer doesn't have a full frame of samples to take for mixing, it is starved _isStarved = true; From ed78855eb6fae5bbee55a35e9da33d7c0de42e16 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 2 Jul 2014 10:49:37 -0700 Subject: [PATCH 28/28] remove warnings about signed/unsigned comparisons --- .../src/SequenceNumberStatsTests.cpp | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/tests/networking/src/SequenceNumberStatsTests.cpp b/tests/networking/src/SequenceNumberStatsTests.cpp index 89a14deb20..62651eaa6f 100644 --- a/tests/networking/src/SequenceNumberStatsTests.cpp +++ b/tests/networking/src/SequenceNumberStatsTests.cpp @@ -23,7 +23,7 @@ void SequenceNumberStatsTests::runAllTests() { pruneTest(); } -const int UINT16_RANGE = std::numeric_limits::max() + 1; +const quint32 UINT16_RANGE = std::numeric_limits::max() + 1; void SequenceNumberStatsTests::rolloverTest() { @@ -34,7 +34,7 @@ void SequenceNumberStatsTests::rolloverTest() { quint16 seq = 79; // start on some random number for (int R = 0; R < 2; R++) { - for (int i = 0; i < 3 * UINT16_RANGE; i++) { + for (quint32 i = 0; i < 3 * UINT16_RANGE; i++) { stats.sequenceNumberReceived(seq); seq = seq + (quint16)1; @@ -53,12 +53,12 @@ void SequenceNumberStatsTests::earlyLateTest() { SequenceNumberStats stats; quint16 seq = 65530; - int numSent = 0; + quint32 numSent = 0; - int numEarly = 0; - int numLate = 0; - int numLost = 0; - int numRecovered = 0; + quint32 numEarly = 0; + quint32 numLate = 0; + quint32 numLost = 0; + quint32 numRecovered = 0; for (int R = 0; R < 2; R++) { for (int T = 0; T < 10000; T++) { @@ -122,12 +122,12 @@ void SequenceNumberStatsTests::duplicateTest() { SequenceNumberStats stats; quint16 seq = 12345; - int numSent = 0; + quint32 numSent = 0; - int numDuplicate = 0; - int numEarly = 0; - int numLate = 0; - int numLost = 0; + quint32 numDuplicate = 0; + quint32 numEarly = 0; + quint32 numLate = 0; + quint32 numLost = 0; for (int R = 0; R < 2; R++) { for (int T = 0; T < 10000; T++) { @@ -210,10 +210,10 @@ void SequenceNumberStatsTests::pruneTest() { SequenceNumberStats stats; quint16 seq = 54321; - int numSent = 0; + quint32 numSent = 0; - int numEarly = 0; - int numLost = 0; + quint32 numEarly = 0; + quint32 numLost = 0; for (int R = 0; R < 2; R++) { for (int T = 0; T < 1000; T++) {