mirror of
https://github.com/overte-org/overte.git
synced 2025-04-21 19:04:32 +02:00
Merge branch 'master' of https://github.com/worklist/hifi
This commit is contained in:
commit
1a3becc123
30 changed files with 934 additions and 105 deletions
|
@ -60,6 +60,7 @@
|
|||
#include <ParticlesScriptingInterface.h>
|
||||
#include <PerfStat.h>
|
||||
#include <ResourceCache.h>
|
||||
#include <UserActivityLogger.h>
|
||||
#include <UUID.h>
|
||||
#include <OctreeSceneStats.h>
|
||||
#include <LocalVoxelsList.h>
|
||||
|
@ -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(applicationVersion());
|
||||
|
||||
// once the event loop has started, check and signal for an access token
|
||||
QMetaObject::invokeMethod(&accountManager, "checkAndSignalForAccessToken", Qt::QueuedConnection);
|
||||
|
@ -396,7 +398,9 @@ Application::Application(int& argc, char** argv, QElapsedTimer &startup_time) :
|
|||
}
|
||||
|
||||
Application::~Application() {
|
||||
|
||||
int DELAY_TIME = 1000;
|
||||
UserActivityLogger::getInstance().close(DELAY_TIME);
|
||||
|
||||
qInstallMessageHandler(NULL);
|
||||
|
||||
// make sure we don't call the idle timer any more
|
||||
|
@ -3577,6 +3581,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
|
||||
|
|
|
@ -432,7 +432,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();
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -219,10 +219,9 @@ 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);
|
||||
inverseKinematics(jointIndex, palmPosition, palmRotation, PALM_PRIORITY);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -258,9 +257,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) {
|
||||
|
@ -537,6 +536,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;
|
||||
|
@ -644,7 +648,6 @@ void SkeletonModel::computeBoundingShape(const FBXGeometry& geometry) {
|
|||
_ragdollPoints[i]._lastPosition = _ragdollPoints[i]._position;
|
||||
continue;
|
||||
}
|
||||
assert(parentIndex != -1);
|
||||
|
||||
glm::quat modifiedRotation = joint.preRotation * joint.rotation * joint.postRotation;
|
||||
transforms[i] = transforms[parentIndex] * glm::translate(joint.translation)
|
||||
|
@ -700,7 +703,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.
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
#include <QOpenGLFramebufferObject>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <UserActivityLogger.h>
|
||||
|
||||
#include "Application.h"
|
||||
|
||||
|
@ -61,6 +62,9 @@ void OculusManager::connect() {
|
|||
|
||||
_ovrHmd = ovrHmd_Create(0);
|
||||
if (_ovrHmd) {
|
||||
if (!_isConnected) {
|
||||
UserActivityLogger::getInstance().connectedDevice("hmd", "oculus");
|
||||
}
|
||||
_isConnected = true;
|
||||
|
||||
ovrHmd_GetDesc(_ovrHmd, &_ovrHmdDesc);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include "Application.h"
|
||||
#include "SixenseManager.h"
|
||||
#include "UserActivityLogger.h"
|
||||
|
||||
#ifdef HAVE_SIXENSE
|
||||
const int CALIBRATION_STATE_IDLE = 0;
|
||||
|
@ -39,6 +40,7 @@ SixenseManager::SixenseManager() {
|
|||
|
||||
sixenseInit();
|
||||
#endif
|
||||
_hydrasConnected = false;
|
||||
_triggerPressed[0] = false;
|
||||
_bumperPressed[0] = false;
|
||||
_oldX[0] = -1;
|
||||
|
@ -70,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();
|
||||
|
|
|
@ -71,6 +71,7 @@ private:
|
|||
float _lastDistance;
|
||||
|
||||
#endif
|
||||
bool _hydrasConnected;
|
||||
quint64 _lastMovement;
|
||||
glm::vec3 _amountMoved;
|
||||
|
||||
|
|
|
@ -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<ModelTreeElement*>(element);
|
||||
|
||||
QList<ModelItem>& modelItems = modelTreeElement->getModels();
|
||||
|
||||
|
|
|
@ -11,14 +11,26 @@
|
|||
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
//#include <GeometryUtil.h>
|
||||
#include <SharedUtil.h>
|
||||
|
||||
#include "JointState.h"
|
||||
|
||||
JointState::JointState() :
|
||||
_animationPriority(0.0f),
|
||||
_fbxJoint(NULL) {
|
||||
_fbxJoint(NULL),
|
||||
_isConstrained(false) {
|
||||
}
|
||||
|
||||
void JointState::copyState(const JointState& state) {
|
||||
_animationPriority = state._animationPriority;
|
||||
_transform = state._transform;
|
||||
_rotation = extractRotation(_transform);
|
||||
_rotationInParentFrame = state._rotationInParentFrame;
|
||||
|
||||
_visibleTransform = state._visibleTransform;
|
||||
_visibleRotation = extractRotation(_visibleTransform);
|
||||
_visibleRotationInParentFrame = state._visibleRotationInParentFrame;
|
||||
// DO NOT copy _fbxJoint
|
||||
}
|
||||
|
||||
void JointState::setFBXJoint(const FBXJoint* joint) {
|
||||
|
@ -26,14 +38,10 @@ void JointState::setFBXJoint(const FBXJoint* joint) {
|
|||
_rotationInParentFrame = joint->rotation;
|
||||
// NOTE: JointState does not own the FBXJoint to which it points.
|
||||
_fbxJoint = joint;
|
||||
}
|
||||
|
||||
void JointState::copyState(const JointState& state) {
|
||||
_rotationInParentFrame = state._rotationInParentFrame;
|
||||
_transform = state._transform;
|
||||
_rotation = extractRotation(_transform);
|
||||
_animationPriority = state._animationPriority;
|
||||
// DO NOT copy _fbxJoint
|
||||
// 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) {
|
||||
|
@ -43,6 +51,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;
|
||||
}
|
||||
|
@ -50,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;
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +83,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) {
|
||||
|
@ -75,27 +93,59 @@ 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 {
|
||||
assert(_fbxJoint != NULL);
|
||||
return _fbxJoint->translation;
|
||||
}
|
||||
|
||||
void JointState::slaveVisibleTransform() {
|
||||
_visibleTransform = _transform;
|
||||
_visibleRotation = _rotation;
|
||||
_visibleRotationInParentFrame = _rotationInParentFrame;
|
||||
}
|
||||
|
|
|
@ -22,12 +22,19 @@ 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);
|
||||
|
||||
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; }
|
||||
|
||||
glm::quat getRotation() const { return _rotation; }
|
||||
|
@ -39,11 +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);
|
||||
|
||||
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
||||
/// 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
|
||||
|
@ -51,16 +66,36 @@ 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& targetRotation);
|
||||
const glm::quat& getRotationInParentFrame() const { return _rotationInParentFrame; }
|
||||
|
||||
const glm::vec3& getDefaultTranslationInParentFrame() const;
|
||||
|
||||
|
||||
void clearTransformTranslation();
|
||||
|
||||
glm::quat _rotationInParentFrame; // joint- to parentJoint-frame
|
||||
void slaveVisibleTransform();
|
||||
|
||||
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
|
||||
|
||||
glm::mat4 _visibleTransform;
|
||||
glm::quat _visibleRotation;
|
||||
glm::quat _visibleRotationInParentFrame;
|
||||
|
||||
const FBXJoint* _fbxJoint; // JointState does NOT own its FBXJoint
|
||||
bool _isConstrained;
|
||||
};
|
||||
|
||||
#endif // hifi_JointState_h
|
||||
|
|
|
@ -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") {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -571,6 +571,9 @@ void Model::setJointStates(QVector<JointState> states) {
|
|||
radius = distance;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < _jointStates.size(); i++) {
|
||||
_jointStates[i].slaveVisibleTransform();
|
||||
}
|
||||
_boundingRadius = radius;
|
||||
}
|
||||
|
||||
|
@ -686,7 +689,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 +702,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);
|
||||
|
@ -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()) {
|
||||
|
@ -1058,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<int>& 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;
|
||||
|
@ -1605,7 +1769,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<JointState> _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
|
||||
|
@ -188,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
|
||||
|
|
|
@ -41,8 +41,7 @@ ApplicationOverlay::ApplicationOverlay() :
|
|||
_textureFov(DEFAULT_OCULUS_UI_ANGULAR_SIZE * RADIANS_PER_DEGREE),
|
||||
_alpha(1.0f),
|
||||
_active(true),
|
||||
_crosshairTexture(0)
|
||||
{
|
||||
_crosshairTexture(0) {
|
||||
|
||||
memset(_reticleActive, 0, sizeof(_reticleActive));
|
||||
memset(_magActive, 0, sizeof(_reticleActive));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
136
interface/src/ui/overlays/BillboardOverlay.cpp
Normal file
136
interface/src/ui/overlays/BillboardOverlay.cpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
//
|
||||
// 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::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<float>(), 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;
|
||||
}
|
46
interface/src/ui/overlays/BillboardOverlay.h
Normal file
46
interface/src/ui/overlays/BillboardOverlay.h
Normal file
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// 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 <QScopedPointer>
|
||||
#include <QUrl>
|
||||
|
||||
#include "Base3DOverlay.h"
|
||||
#include "../../renderer/TextureCache.h"
|
||||
|
||||
class BillboardOverlay : public Base3DOverlay {
|
||||
Q_OBJECT
|
||||
public:
|
||||
BillboardOverlay();
|
||||
|
||||
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<Texture> _billboardTexture;
|
||||
|
||||
glm::quat _rotation;
|
||||
float _scale;
|
||||
bool _isFacingAvatar;
|
||||
};
|
||||
|
||||
#endif // hifi_BillboardOverlay_h
|
|
@ -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() {
|
||||
|
|
115
interface/src/ui/overlays/ModelOverlay.cpp
Normal file
115
interface/src/ui/overlays/ModelOverlay.cpp
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
38
interface/src/ui/overlays/ModelOverlay.h
Normal file
38
interface/src/ui/overlays/ModelOverlay.h
Normal file
|
@ -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
|
|
@ -10,13 +10,15 @@
|
|||
|
||||
#include <Application.h>
|
||||
|
||||
#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"
|
||||
|
||||
Overlays::Overlays() : _nextOverlayID(1) {
|
||||
}
|
||||
|
@ -82,13 +84,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()) {
|
||||
|
@ -156,6 +158,18 @@ 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;
|
||||
} else if (type == "billboard") {
|
||||
thisOverlay = new BillboardOverlay();
|
||||
thisOverlay->init(_parent);
|
||||
thisOverlay->setProperties(properties);
|
||||
created = true;
|
||||
is3D = true;
|
||||
}
|
||||
|
||||
if (created) {
|
||||
|
|
|
@ -137,7 +137,7 @@ background: transparent;</string>
|
|||
<property name="font">
|
||||
<font>
|
||||
<family>Helvetica,Arial,sans-serif</family>
|
||||
<pointsize>16</pointsize>
|
||||
<pointsize>-1</pointsize>
|
||||
<weight>75</weight>
|
||||
<italic>false</italic>
|
||||
<bold>true</bold>
|
||||
|
@ -326,9 +326,6 @@ padding-top: 3px;</string>
|
|||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">margin: 0;</string>
|
||||
</property>
|
||||
<property name="frameShape">
|
||||
<enum>QFrame::NoFrame</enum>
|
||||
</property>
|
||||
|
@ -336,7 +333,7 @@ padding-top: 3px;</string>
|
|||
<number>0</number>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
|
@ -352,7 +349,7 @@ padding-top: 3px;</string>
|
|||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>269</width>
|
||||
<width>284</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
|
@ -460,7 +457,6 @@ font: bold 16px;</string>
|
|||
<string notr="true">background: transparent;
|
||||
font-size: 14px;</string>
|
||||
</property>
|
||||
<zorder>runningScriptsList</zorder>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@ -627,10 +623,10 @@ QListView::item {
|
|||
<enum>QFrame::Plain</enum>
|
||||
</property>
|
||||
<property name="verticalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
<enum>Qt::ScrollBarAlwaysOn</enum>
|
||||
</property>
|
||||
<property name="horizontalScrollBarPolicy">
|
||||
<enum>Qt::ScrollBarAsNeeded</enum>
|
||||
<enum>Qt::ScrollBarAlwaysOff</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
|
|
|
@ -53,7 +53,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];
|
||||
|
@ -70,14 +70,14 @@ int AudioRingBuffer::parseData(const QByteArray& packet) {
|
|||
return writeData(packet.data() + numBytesBeforeAudioData, packet.size() - numBytesBeforeAudioData);
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -118,14 +118,14 @@ 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);
|
||||
|
||||
int samplesRoomFor = _sampleCapacity - samplesAvailable();
|
||||
if (samplesToCopy > samplesRoomFor) {
|
||||
|
@ -167,7 +167,7 @@ void AudioRingBuffer::shiftReadPosition(unsigned int numSamples) {
|
|||
}
|
||||
}
|
||||
|
||||
unsigned int AudioRingBuffer::samplesAvailable() const {
|
||||
int AudioRingBuffer::samplesAvailable() const {
|
||||
if (!_endOfLastWrite) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ int AudioRingBuffer::addSilentFrame(int numSilentSamples) {
|
|||
return numSilentSamples * sizeof(int16_t);
|
||||
}
|
||||
|
||||
bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(unsigned int numRequiredSamples) const {
|
||||
bool AudioRingBuffer::isNotStarvedOrHasMinimumSamples(int numRequiredSamples) const {
|
||||
if (!_isStarved) {
|
||||
return true;
|
||||
} else {
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
155
libraries/networking/src/UserActivityLogger.cpp
Normal file
155
libraries/networking/src/UserActivityLogger.cpp
Normal file
|
@ -0,0 +1,155 @@
|
|||
//
|
||||
// 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 <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QTimer>
|
||||
|
||||
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, QJsonObject details, JSONCallbackParameters params) {
|
||||
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;"
|
||||
" name=\"action_details\"");
|
||||
detailsPart.setBody(QJsonDocument(details).toJson(QJsonDocument::Compact));
|
||||
multipart->append(detailsPart);
|
||||
}
|
||||
qDebug() << "Logging activity" << action;
|
||||
|
||||
// if no callbacks specified, call our owns
|
||||
if (params.isEmpty()) {
|
||||
params.jsonCallbackReceiver = this;
|
||||
params.jsonCallbackMethod = "requestFinished";
|
||||
params.errorCallbackReceiver = this;
|
||||
params.errorCallbackMethod = "requestError";
|
||||
}
|
||||
|
||||
accountManager.authenticatedRequest(USER_ACTIVITY_URL,
|
||||
QNetworkAccessManager::PostOperation,
|
||||
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::launch(QString applicationVersion) {
|
||||
const QString ACTION_NAME = "launch";
|
||||
QJsonObject actionDetails;
|
||||
QString VERSION_KEY = "version";
|
||||
actionDetails.insert(VERSION_KEY, applicationVersion);
|
||||
|
||||
logAction(ACTION_NAME, actionDetails);
|
||||
}
|
||||
|
||||
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) {
|
||||
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(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);
|
||||
|
||||
}
|
47
libraries/networking/src/UserActivityLogger.h
Normal file
47
libraries/networking/src/UserActivityLogger.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
//
|
||||
// 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 "AccountManager.h"
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QJsonObject>
|
||||
#include <QNetworkReply>
|
||||
|
||||
class UserActivityLogger : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static UserActivityLogger& getInstance();
|
||||
|
||||
public slots:
|
||||
void logAction(QString action, QJsonObject details = QJsonObject(), JSONCallbackParameters params = JSONCallbackParameters());
|
||||
|
||||
void launch(QString applicationVersion);
|
||||
void close(int delayTime);
|
||||
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);
|
||||
void requestError(QNetworkReply::NetworkError error,const QString& string);
|
||||
|
||||
private:
|
||||
UserActivityLogger();
|
||||
};
|
||||
|
||||
#endif // hifi_UserActivityLogger_h
|
|
@ -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) {
|
||||
}
|
||||
|
|
|
@ -23,7 +23,7 @@ void SequenceNumberStatsTests::runAllTests() {
|
|||
pruneTest();
|
||||
}
|
||||
|
||||
const int UINT16_RANGE = std::numeric_limits<quint16>::max() + 1;
|
||||
const quint32 UINT16_RANGE = std::numeric_limits<quint16>::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++) {
|
||||
|
|
Loading…
Reference in a new issue