From 33f9acde67e8ac1a7707e9880c5662de271a3eb1 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 17 Jul 2018 17:42:11 -0700 Subject: [PATCH 001/207] latest --- interface/src/avatar/MyAvatar.cpp | 20 +++++++++++++++++++- interface/src/avatar/MyAvatar.h | 2 ++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index d084ca69f9..9eaf42d25d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -89,9 +89,10 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; +const int HEAD_FACING_RING_BUFFER_SIZE = 250; const float CENTIMETERS_PER_METER = 100.0f; -//#define DEBUG_DRAW_HMD_MOVING_AVERAGE +#define DEBUG_DRAW_HMD_MOVING_AVERAGE MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), @@ -112,6 +113,7 @@ MyAvatar::MyAvatar(QThread* thread) : _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), + _headFacingBuffer(HEAD_FACING_RING_BUFFER_SIZE), _recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE), _bodySensorMatrix(), _goToPending(false), @@ -434,11 +436,25 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); + + glm::vec2 bodyFacing = getFacingDir2D(_bodySensorMatrix); + _headFacingBuffer.insert(_headControllerFacing); + //qCDebug(interfaceapp) << "the body facing is " << -bodyFacing.x << " " << -bodyFacing.y << " the head controller facing is " << _headControllerFacing.x << " " << _headControllerFacing.y; + + float sumHeadFacingX = 0.0f; + float sumHeadFacingY = 0.0f; + for (auto headFacingIterator = _headFacingBuffer.begin(); headFacingIterator != _headFacingBuffer.end(); ++headFacingIterator) { + sumHeadFacingX += (*headFacingIterator).x; + sumHeadFacingY += (*headFacingIterator).y; + } + _averageFacing.x = sumHeadFacingX / (float)_headFacingBuffer.getNumEntries(); + _averageFacing.y= sumHeadFacingY / (float)_headFacingBuffer.getNumEntries(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); + //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); @@ -3478,6 +3494,8 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co const float FOLLOW_ROTATION_THRESHOLD = cosf(myAvatar.getRotationThreshold()); glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; + + //return glm::dot(myAvatar._averageFacing, -bodyFacing) < FOLLOW_ROTATION_THRESHOLD; } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 819d5b0066..828ca48e32 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1546,6 +1546,8 @@ private: // cache head controller pose in sensor space glm::vec2 _headControllerFacing; // facing vector in xz plane (sensor space) glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) + RingBufferHistory _headFacingBuffer; + vec2 _averageFacing { 0.0f, 0.0f }; glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; float _currentStandingHeight { 0.0f }; From 7cc724a27bf59709be09e9dc4f48ed0ffcd11eb2 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 18 Jul 2018 17:08:04 -0700 Subject: [PATCH 002/207] added properties to allow the toggling of the debug draw the look at vector, and the reset moving average look at on step detection --- interface/src/avatar/MyAvatar.cpp | 22 +++++++++++++++------- interface/src/avatar/MyAvatar.h | 8 ++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 1b9eed9187..63a26fb2ad 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -471,13 +471,15 @@ void MyAvatar::update(float deltaTime) { _averageFacing.y= sumHeadFacingY / (float)_headFacingBuffer.getNumEntries(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE - auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); - glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); - glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); - //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); - glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); - DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); - DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + if (_drawAverageFacingEnabled) { + auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); + glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); + glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); + //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); + glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); + DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + } #endif if (_goToPending) { @@ -3594,10 +3596,16 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); + if (myAvatar.getEnableStepResetRotation()) { + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + } } } else { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); + if (myAvatar.getEnableStepResetRotation()) { + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + } } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 0a40ac471c..c0f0166e49 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -198,6 +198,8 @@ class MyAvatar : public Avatar { Q_PROPERTY(bool hasAudioEnabledFaceMovement READ getHasAudioEnabledFaceMovement WRITE setHasAudioEnabledFaceMovement) Q_PROPERTY(float rotationRecenterFilterLength READ getRotationRecenterFilterLength WRITE setRotationRecenterFilterLength) Q_PROPERTY(float rotationThreshold READ getRotationThreshold WRITE setRotationThreshold) + Q_PROPERTY(bool enableStepResetRotation READ getEnableStepResetRotation WRITE setEnableStepResetRotation) + Q_PROPERTY(bool enableDrawAverageFacing READ getEnableDrawAverageFacing WRITE setEnableDrawAverageFacing) //TODO: make gravity feature work Q_PROPERTY(glm::vec3 gravity READ getGravity WRITE setGravity) Q_PROPERTY(glm::vec3 leftHandPosition READ getLeftHandPosition) @@ -1464,6 +1466,10 @@ private: float getRotationRecenterFilterLength() const { return _rotationRecenterFilterLength; } void setRotationThreshold(float angleRadians); float getRotationThreshold() const { return _rotationThreshold; } + void setEnableStepResetRotation(bool stepReset) { _stepResetRotationEnabled = stepReset; } + bool getEnableStepResetRotation() const { return _stepResetRotationEnabled; } + void setEnableDrawAverageFacing(bool drawAverage) { _drawAverageFacingEnabled = drawAverage; } + bool getEnableDrawAverageFacing() const { return _drawAverageFacingEnabled; } bool isMyAvatar() const override { return true; } virtual int parseDataFromBuffer(const QByteArray& buffer) override; virtual glm::vec3 getSkeletonPosition() const override; @@ -1574,6 +1580,8 @@ private: std::atomic _hasScriptedBlendShapes { false }; std::atomic _rotationRecenterFilterLength { 4.0f }; std::atomic _rotationThreshold { 0.5235f }; // 30 degrees in radians + std::atomic _stepResetRotationEnabled { false }; + std::atomic _drawAverageFacingEnabled { false }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access glm::mat4 _sensorToWorldMatrix { glm::mat4() }; From a8c4f4e6507913a8fde6fce37f14418f4a11027c Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 18 Jul 2018 17:10:09 -0700 Subject: [PATCH 003/207] removed extra whitespace --- interface/src/avatar/MyAvatar.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 63a26fb2ad..34d5017c50 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -456,11 +456,11 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - + glm::vec2 bodyFacing = getFacingDir2D(_bodySensorMatrix); _headFacingBuffer.insert(_headControllerFacing); //qCDebug(interfaceapp) << "the body facing is " << -bodyFacing.x << " " << -bodyFacing.y << " the head controller facing is " << _headControllerFacing.x << " " << _headControllerFacing.y; - + float sumHeadFacingX = 0.0f; float sumHeadFacingY = 0.0f; for (auto headFacingIterator = _headFacingBuffer.begin(); headFacingIterator != _headFacingBuffer.end(); ++headFacingIterator) { From 5ec7ab3a7d10f50eaefd4638852d68af8b365d6b Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 18 Jul 2018 17:20:47 -0700 Subject: [PATCH 004/207] removed the other way of computing the average lookat which used a ring buffer --- interface/src/avatar/MyAvatar.cpp | 17 +---------------- interface/src/avatar/MyAvatar.h | 2 -- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 34d5017c50..10e38afbe3 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -89,7 +89,6 @@ const float MyAvatar::ZOOM_MAX = 25.0f; const float MyAvatar::ZOOM_DEFAULT = 1.5f; const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; -const int HEAD_FACING_RING_BUFFER_SIZE = 250; const float CENTIMETERS_PER_METER = 100.0f; #define DEBUG_DRAW_HMD_MOVING_AVERAGE @@ -113,7 +112,6 @@ MyAvatar::MyAvatar(QThread* thread) : _hmdSensorMatrix(), _hmdSensorOrientation(), _hmdSensorPosition(), - _headFacingBuffer(HEAD_FACING_RING_BUFFER_SIZE), _recentModeReadings(MODE_READINGS_RING_BUFFER_SIZE), _bodySensorMatrix(), _goToPending(false), @@ -457,25 +455,14 @@ void MyAvatar::update(float deltaTime) { setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - glm::vec2 bodyFacing = getFacingDir2D(_bodySensorMatrix); - _headFacingBuffer.insert(_headControllerFacing); - //qCDebug(interfaceapp) << "the body facing is " << -bodyFacing.x << " " << -bodyFacing.y << " the head controller facing is " << _headControllerFacing.x << " " << _headControllerFacing.y; - float sumHeadFacingX = 0.0f; - float sumHeadFacingY = 0.0f; - for (auto headFacingIterator = _headFacingBuffer.begin(); headFacingIterator != _headFacingBuffer.end(); ++headFacingIterator) { - sumHeadFacingX += (*headFacingIterator).x; - sumHeadFacingY += (*headFacingIterator).y; - } - _averageFacing.x = sumHeadFacingX / (float)_headFacingBuffer.getNumEntries(); - _averageFacing.y= sumHeadFacingY / (float)_headFacingBuffer.getNumEntries(); + #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); - //glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_averageFacing.x, 0.0f, _averageFacing.y)); glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); @@ -3516,8 +3503,6 @@ bool MyAvatar::FollowHelper::shouldActivateRotation(const MyAvatar& myAvatar, co const float FOLLOW_ROTATION_THRESHOLD = cosf(myAvatar.getRotationThreshold()); glm::vec2 bodyFacing = getFacingDir2D(currentBodyMatrix); return glm::dot(-myAvatar.getHeadControllerFacingMovingAverage(), bodyFacing) < FOLLOW_ROTATION_THRESHOLD; - - //return glm::dot(myAvatar._averageFacing, -bodyFacing) < FOLLOW_ROTATION_THRESHOLD; } bool MyAvatar::FollowHelper::shouldActivateHorizontal(const MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix) const { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c0f0166e49..81315900a7 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1593,8 +1593,6 @@ private: // cache head controller pose in sensor space glm::vec2 _headControllerFacing; // facing vector in xz plane (sensor space) glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) - RingBufferHistory _headFacingBuffer; - vec2 _averageFacing { 0.0f, 0.0f }; glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; float _currentStandingHeight { 0.0f }; From 09d33177fcfadabae4a85b5b778d65d412ec3925 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 6 Aug 2018 15:36:25 -0700 Subject: [PATCH 005/207] Adjust Workload and better representation of the workload debuging features --- .../render-utils/src/WorkloadResource.slh | 82 +++++++++++++ .../render-utils/src/drawWorkloadProxy.slf | 4 +- .../render-utils/src/drawWorkloadProxy.slv | 36 +----- .../render-utils/src/drawWorkloadView.slf | 4 +- .../render-utils/src/drawWorkloadView.slv | 44 +------ libraries/workload/src/workload/ViewTask.cpp | 19 ++- libraries/workload/src/workload/ViewTask.h | 6 +- .../developer/utilities/workload/workload.js | 109 +++++++++++++----- .../utilities/workload/workloadInspector.qml | 71 ++++++++++-- 9 files changed, 256 insertions(+), 119 deletions(-) create mode 100644 libraries/render-utils/src/WorkloadResource.slh diff --git a/libraries/render-utils/src/WorkloadResource.slh b/libraries/render-utils/src/WorkloadResource.slh new file mode 100644 index 0000000000..c1d0d21fa1 --- /dev/null +++ b/libraries/render-utils/src/WorkloadResource.slh @@ -0,0 +1,82 @@ +// +// Created by Sam Gateau on 7/31/2018. +// Copyright 2018 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 +// +<@if not WORKLOAD_RESOURCE_SLH@> +<@def WORKLOAD_RESOURCE_SLH@> + +<@include gpu/Color.slh@> +<$declareColorWheel()$> + +const vec4 REGION_COLOR[4] = vec4[4]( + vec4(0.0, 1.0, 0.0, 1.0), + vec4(1.0, 0.6, 0.0, 1.0), + vec4(1.0, 0.0, 0.0, 1.0), + vec4(0.3, 0.0, 0.8, 1.0) +); + +<@func declareWorkloadProxies() @> + +struct WorkloadProxy { + vec4 sphere; + vec4 region; +}; + +#if defined(GPU_GL410) +uniform samplerBuffer workloadProxiesBuffer; +WorkloadProxy getWorkloadProxy(int i) { + int offset = 2 * i; + WorkloadProxy proxy; + proxy.sphere = texelFetch(workloadProxiesBuffer, offset); + proxy.region = texelFetch(workloadProxiesBuffer, offset + 1); + return proxy; +} +#else +layout(std140) buffer workloadProxiesBuffer { + WorkloadProxy _proxies[]; +}; +WorkloadProxy getWorkloadProxy(int i) { + WorkloadProxy proxy = _proxies[i]; + return proxy; +} +#endif + +<@endfunc@> + + +<@func declareWorkloadViews() @> + +struct WorkloadView { + vec4 direction_far; + vec4 fov; + vec4 origin; + vec4 backFront[2]; + vec4 regions[3]; +}; + +#if defined(GPU_GL410) +uniform samplerBuffer workloadViewsBuffer; +WorkloadView getWorkloadView(int i) { + int offset = 2 * i; + WorkloadView view; + view.origin = texelFetch(workloadViewsBuffer, offset); + view.radiuses = texelFetch(workloadViewsBuffer, offset + 1); + return view; +} +#else +layout(std140) buffer workloadViewsBuffer { + WorkloadView _views[]; +}; +WorkloadView getWorkloadView(int i) { + WorkloadView view = _views[i]; + return view; +} +#endif + +<@endfunc@> + +<@endif@> + diff --git a/libraries/render-utils/src/drawWorkloadProxy.slf b/libraries/render-utils/src/drawWorkloadProxy.slf index 1304e68c7f..32dceab00a 100644 --- a/libraries/render-utils/src/drawWorkloadProxy.slf +++ b/libraries/render-utils/src/drawWorkloadProxy.slf @@ -15,11 +15,13 @@ in vec4 varColor; in vec3 varTexcoord; +in vec3 varEyePos; void main(void) { if (varColor.w > 0.0) { float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz)); - float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w); + float d = varColor.w / abs(varEyePos.z); + float a = paintStripe(r * d, 0.0, 1.0 / d, 0.002 / d); if (a <= 0.1 || r > 1.1) { discard; } diff --git a/libraries/render-utils/src/drawWorkloadProxy.slv b/libraries/render-utils/src/drawWorkloadProxy.slv index 64fb335fd6..1b91e349ab 100644 --- a/libraries/render-utils/src/drawWorkloadProxy.slv +++ b/libraries/render-utils/src/drawWorkloadProxy.slv @@ -15,40 +15,13 @@ <@include gpu/Transform.slh@> <$declareStandardTransform()$> -<@include gpu/Color.slh@> -<$declareColorWheel()$> - -uniform vec4 inColor; - - -struct WorkloadProxy { - vec4 sphere; - vec4 region; -}; - -#if defined(GPU_GL410) -uniform samplerBuffer workloadProxiesBuffer; -WorkloadProxy getWorkloadProxy(int i) { - int offset = 2 * i; - WorkloadProxy proxy; - proxy.sphere = texelFetch(workloadProxiesBuffer, offset); - proxy.region = texelFetch(workloadProxiesBuffer, offset + 1); - return proxy; -} -#else -layout(std140) buffer workloadProxiesBuffer { - WorkloadProxy _proxies[]; -}; -WorkloadProxy getWorkloadProxy(int i) { - WorkloadProxy proxy = _proxies[i]; - return proxy; -} -#endif - +<@include WorkloadResource.slh@> +<$declareWorkloadProxies()$> out vec4 varColor; out vec3 varTexcoord; +out vec3 varEyePos; void main(void) { const vec4 UNIT_SPRITE[3] = vec4[3]( @@ -79,6 +52,7 @@ void main(void) { vec3 dirY = vec3(0.0, 1.0, 0.0); vec4 pos = vec4(proxyPosEye.xyz + proxy.sphere.w * ( dirX * spriteVert.x + dirY * spriteVert.y /* + dirZ * spriteVert.z*/), 1.0); + varEyePos = pos.xyz; varTexcoord = spriteVert.xyz; <$transformEyeToClipPos(cam, pos, gl_Position)$> @@ -86,7 +60,7 @@ void main(void) { int region = floatBitsToInt(proxy.region.x); region = (0x000000FF & region); - varColor = vec4(colorWheel(float(region) / 4.0), proxy.sphere.w); + varColor = vec4(REGION_COLOR[region].xyz, proxy.sphere.w); if (region == 4) { gl_Position = vec4(0.0); diff --git a/libraries/render-utils/src/drawWorkloadView.slf b/libraries/render-utils/src/drawWorkloadView.slf index 1304e68c7f..a3779ad6f4 100644 --- a/libraries/render-utils/src/drawWorkloadView.slf +++ b/libraries/render-utils/src/drawWorkloadView.slf @@ -15,11 +15,13 @@ in vec4 varColor; in vec3 varTexcoord; +in vec3 varEyePos; void main(void) { if (varColor.w > 0.0) { float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz)); - float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w); + float d = varColor.w / abs(varEyePos.z); + float a = paintStripe(r * d, 0.0, 1.0 / d, 0.005 / d); if (a <= 0.1 || r > 1.1) { discard; } diff --git a/libraries/render-utils/src/drawWorkloadView.slv b/libraries/render-utils/src/drawWorkloadView.slv index f5497d250c..3212bc055c 100644 --- a/libraries/render-utils/src/drawWorkloadView.slv +++ b/libraries/render-utils/src/drawWorkloadView.slv @@ -15,45 +15,12 @@ <@include gpu/Transform.slh@> <$declareStandardTransform()$> -<@include gpu/Color.slh@> -<$declareColorWheel()$> - -uniform vec4 inColor; - - -struct WorkloadView { - vec4 direction_far; - vec4 fov; - vec4 origin; - vec4 backFront[2]; - vec4 regions[3]; -}; - -#if defined(GPU_GL410) -uniform samplerBuffer workloadViewsBuffer; -WorkloadView getWorkloadView(int i) { - int offset = 2 * i; - WorkloadView view; - view.origin = texelFetch(workloadViewsBuffer, offset); - view.radiuses = texelFetch(workloadViewsBuffer, offset + 1); - return view; -} -#else -layout(std140) buffer workloadViewsBuffer { - WorkloadView _views[]; -}; -WorkloadView getWorkloadView(int i) { - WorkloadView view = _views[i]; - return view; -} -#endif - - - - +<@include WorkloadResource.slh@> +<$declareWorkloadViews()$> out vec4 varColor; out vec3 varTexcoord; +out vec3 varEyePos; const int NUM_VERTICES_PER_SEGMENT = 2; const int NUM_SEGMENT_PER_VIEW_REGION = 65; @@ -109,12 +76,13 @@ void main(void) { <$transformModelToEyeDir(cam, obj, originSpaceTan, tanEye)$> lateralDir = normalize(cross(vec3(0.0, 0.0, 1.0), normalize(tanEye))); - posEye.xyz += (0.05 * (regionID + 1)) * (-1.0 + 2.0 * float(segmentVertexID)) * lateralDir; + posEye.xyz += (0.005 * abs(posEye.z) * (regionID + 1)) * (-1.0 + 2.0 * float(segmentVertexID)) * lateralDir; + varEyePos = posEye.xyz; <$transformEyeToClipPos(cam, posEye, gl_Position)$> varTexcoord = spriteVert.xyz; // Convert region to color - varColor = vec4(colorWheel(float(regionID) / 4.0), -1.0); + varColor = vec4(REGION_COLOR[regionID].xyz, -1.0); } diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 4b77733c52..5d9953cdf4 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -91,12 +91,24 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const regulateViews(outViews, inTimings); } - // Export the timings for debuging + // Export the ranges for debuging + bool doExport = false; + if (outViews.size()) { + _dataExport.ranges[workload::Region::R1] = outViews[0].regionBackFronts[workload::Region::R1]; + _dataExport.ranges[workload::Region::R2] = outViews[0].regionBackFronts[workload::Region::R2]; + _dataExport.ranges[workload::Region::R3] = outViews[0].regionBackFronts[workload::Region::R3]; + doExport = true; + } + + // Export the ranges and timings for debuging if (inTimings.size()) { _dataExport.timings[workload::Region::R1] = std::chrono::duration(inTimings[0]).count(); _dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1]; _dataExport.timings[workload::Region::R3] = std::chrono::duration(inTimings[1]).count(); + doExport = true; + } + if (doExport) { auto config = std::static_pointer_cast(runContext->jobConfig); config->dataExport = _dataExport; config->emitDirty(); @@ -129,11 +141,6 @@ void ControlViews::regulateViews(workload::Views& outViews, const workload::Timi regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]); enforceRegionContainment(); - - _dataExport.ranges[workload::Region::R1] = regionBackFronts[workload::Region::R1]; - _dataExport.ranges[workload::Region::R2] = regionBackFronts[workload::Region::R2]; - _dataExport.ranges[workload::Region::R3] = regionBackFronts[workload::Region::R3]; - for (auto& outView : outViews) { outView.regionBackFronts[workload::Region::R1] = regionBackFronts[workload::Region::R1]; outView.regionBackFronts[workload::Region::R2] = regionBackFronts[workload::Region::R2]; diff --git a/libraries/workload/src/workload/ViewTask.h b/libraries/workload/src/workload/ViewTask.h index 867f22d534..c20314d514 100644 --- a/libraries/workload/src/workload/ViewTask.h +++ b/libraries/workload/src/workload/ViewTask.h @@ -32,9 +32,9 @@ namespace workload { }; const std::vector MAX_VIEW_BACK_FRONTS = { - { 100.0f, 200.0f }, - { 150.0f, 300.0f }, - { 250.0f, 500.0f } + { 100.0f, 1600.0f }, + { 150.0f, 10000.0f }, + { 250.0f, 16000.0f } }; const float RELATIVE_STEP_DOWN = 0.05f; diff --git a/scripts/developer/utilities/workload/workload.js b/scripts/developer/utilities/workload/workload.js index d74eb4e6d5..ce41e0ecb8 100644 --- a/scripts/developer/utilities/workload/workload.js +++ b/scripts/developer/utilities/workload/workload.js @@ -16,16 +16,9 @@ var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg"); var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg"); - - var onAppScreen = false; + var onTablet = false; // set this to true to use the tablet, false use a floating window - function onClicked() { - if (onAppScreen) { - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(QMLAPP_URL); - } - } + var onAppScreen = false; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ @@ -36,33 +29,82 @@ var hasEventBridge = false; - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; + var onScreen = false; + var window; + + function onClicked() { + if (onTablet) { + if (onAppScreen) { + tablet.gotoHomeScreen(); + } else { + tablet.loadQMLSource(QMLAPP_URL); } } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; + if (onScreen) { + killWindow() + } else { + createWindow() + } + } + } + + function createWindow() { + var qml = Script.resolvePath(QMLAPP_URL); + window = Desktop.createWindow(Script.resolvePath(QMLAPP_URL), { + title: 'Workload Inspector', + flags: Desktop.ALWAYS_ON_TOP, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 400, y: 600} + }); + window.setPosition(200, 50); + window.closed.connect(killWindow); + window.fromQml.connect(fromQml); + onScreen = true + button.editProperties({isActive: true}); + } + + function killWindow() { + if (window !== undefined) { + window.closed.disconnect(killWindow); + window.fromQml.disconnect(fromQml); + window.close() + window = undefined + } + onScreen = false + button.editProperties({isActive: false}) + } + + function wireEventBridge(on) { + if (onTablet) { + if (!tablet) { + print("Warning in wireEventBridge(): 'tablet' undefined!"); + return; + } + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQml); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQml); + hasEventBridge = false; + } } } } function onScreenChanged(type, url) { - if (url === QMLAPP_URL) { - onAppScreen = true; - } else { - onAppScreen = false; + if (onTablet) { + if (url === QMLAPP_URL) { + onAppScreen = true; + } else { + onAppScreen = false; + } + + button.editProperties({isActive: onAppScreen}); + wireEventBridge(onAppScreen); } - - button.editProperties({isActive: onAppScreen}); - wireEventBridge(onAppScreen); } function fromQml(message) { @@ -72,6 +114,7 @@ tablet.screenChanged.connect(onScreenChanged); Script.scriptEnding.connect(function () { + killWindow() if (onAppScreen) { tablet.gotoHomeScreen(); } @@ -113,8 +156,14 @@ } function sendToQml(message) { - tablet.sendToQml(message); + if (onTablet) { + tablet.sendToQml(message); + } else { + if (window) { + window.sendToQml(message); + } + } } updateGridInQML() -}()); \ No newline at end of file +}()); diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index 8076f5c1c2..c55fdee040 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -259,18 +259,71 @@ Rectangle { } ] } + /* PlotPerf { + title: "Ranges" + height: 100 + object: stats.controlViews + valueScale: 1.0 + valueUnit: "m" + plots: [ + { + prop: "r3RangeFront", + label: "R3 F", + color: "#FF0000" + }, + { + prop: "r3RangeBack", + label: "R3 B", + color: "#EF0000" + }, + { + prop: "r2RangeFront", + label: "R2 F", + color: "orange" + }, + { + prop: "r2RangeBack", + label: "R2 B", + color: "magenta" + }, + { + prop: "r1RangeFront", + label: "R1 F", + color: "#00FF00" + }, + { + prop: "r1RangeBack", + label: "R1 B", + color: "#00EF00" + }, + ] + }*/ Separator {} HifiControls.Label { - text: "Numbers:"; + text: "Ranges & Numbers:"; } - HifiControls.Label { - text: "R1= " + Workload.getConfig("regionState")["numR1"]; - } - HifiControls.Label { - text: "R2= " + Workload.getConfig("regionState")["numR2"]; - } - HifiControls.Label { - text: "R3= " + Workload.getConfig("regionState")["numR3"]; + Repeater { + model: [ + "green:R1:numR1:r1RangeBack:r1RangeFront", + "orange:R2:numR2:r2RangeBack:r2RangeFront", + "red:R3:numR3:r3RangeBack:r3RangeFront" + ] + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + HifiControls.Label { + text: modelData.split(":")[1] + " : " + Workload.getConfig("regionState")[modelData.split(":")[2]] ; + color: modelData.split(":")[0] + } + HifiControls.Label { + text: Workload.getConfig("controlViews")[modelData.split(":")[3]].toFixed(0) ; + color: modelData.split(":")[0] + } + HifiControls.Label { + text: Workload.getConfig("controlViews")[modelData.split(":")[4]].toFixed(0) ; + color: modelData.split(":")[0] + } + } } Separator {} From 790fb43b420cf03bdbcdb49a09048491d80e37dd Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Tue, 7 Aug 2018 11:34:05 -0700 Subject: [PATCH 006/207] Start of safe landing client work --- .../src/octree/OctreePacketProcessor.cpp | 3 +++ interface/src/octree/OctreePacketProcessor.h | 5 +++- interface/src/octree/SafeLanding.cpp | 17 ++++++++++++ interface/src/octree/SafeLanding.h | 26 +++++++++++++++++++ 4 files changed, 50 insertions(+), 1 deletion(-) create mode 100644 interface/src/octree/SafeLanding.cpp create mode 100644 interface/src/octree/SafeLanding.h diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 063b07f699..52885e8902 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -16,6 +16,7 @@ #include "Application.h" #include "Menu.h" #include "SceneScriptingInterface.h" +#include "SafeLanding.h" OctreePacketProcessor::OctreePacketProcessor() { setObjectName("Octree Packet Processor"); @@ -26,6 +27,8 @@ OctreePacketProcessor::OctreePacketProcessor() { packetReceiver.registerDirectListenerForTypes(octreePackets, this, "handleOctreePacket"); } +OctreePacketProcessor::~OctreePacketProcessor() { } + void OctreePacketProcessor::handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode) { queueReceivedPacket(message, senderNode); } diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index edba48a238..866a59eaa6 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -15,12 +15,15 @@ #include #include +class SafeLanding; + /// Handles processing of incoming voxel packets for the interface application. As with other ReceivedPacketProcessor classes /// the user is responsible for reading inbound packets and adding them to the processing queue by calling queueReceivedPacket() class OctreePacketProcessor : public ReceivedPacketProcessor { Q_OBJECT public: OctreePacketProcessor(); + ~OctreePacketProcessor(); bool octreeSequenceIsComplete(int sequenceNumber) const; @@ -39,6 +42,6 @@ private slots: private: static constexpr int INVALID_SEQUENCE = -1; std::atomic _completionSequenceNumber { INVALID_SEQUENCE }; - + std::unique_ptr _safeLanding; }; #endif // hifi_OctreePacketProcessor_h diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp new file mode 100644 index 0000000000..c4a2fa13e9 --- /dev/null +++ b/interface/src/octree/SafeLanding.cpp @@ -0,0 +1,17 @@ +// +// SafeLanding.cpp +// interface/src/octree +// +// Created by Simon Walton. +// Copyright 2018 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 "SafeLanding.h" + +SafeLanding::SafeLanding() { +} + +SafeLanding::~SafeLanding() { } diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h new file mode 100644 index 0000000000..af13ef7539 --- /dev/null +++ b/interface/src/octree/SafeLanding.h @@ -0,0 +1,26 @@ +// +// SafeLanding.h +// interface/src/octree +// +// Created by Simon Walton. +// Copyright 2018 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 +// + +// Controller for logic to wait for local collision bodies before enabling physics. + +#ifndef hifi_SafeLanding_h +#define hifi_SafeLanding_h + +class SafeLanding { +public: + SafeLanding(); + ~SafeLanding(); + +private: + +}; + +#endif // hifi_SafeLanding_h From 0dd8d2d3d4d3ca5555b5beb03392e19f89580e13 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 7 Aug 2018 18:18:58 -0700 Subject: [PATCH 007/207] working on enabling bloom --- .../src/RenderableZoneEntityItem.cpp | 49 ++++++ .../src/RenderableZoneEntityItem.h | 31 ++-- libraries/entities/src/BloomPropertyGroup.cpp | 159 +++++++++++++++++ libraries/entities/src/BloomPropertyGroup.h | 94 ++++++++++ .../entities/src/EntityItemProperties.cpp | 51 +++++- libraries/entities/src/EntityItemProperties.h | 4 + libraries/entities/src/EntityPropertyFlags.h | 5 + libraries/entities/src/ZoneEntityItem.cpp | 165 ++++++++++-------- libraries/entities/src/ZoneEntityItem.h | 14 +- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- .../render-utils/src/UpdateSceneTask.cpp | 2 + 12 files changed, 483 insertions(+), 96 deletions(-) create mode 100644 libraries/entities/src/BloomPropertyGroup.cpp create mode 100644 libraries/entities/src/BloomPropertyGroup.h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c5035431f6..f23e8096f1 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -64,6 +64,13 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity _hazeIndex = INVALID_INDEX; } } + + if (_bloomStage) { + if (!BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomStage->removeBloom(_bloomIndex); + _bloomIndex = INVALID_INDEX; + } + } } void ZoneEntityRenderer::doRender(RenderArgs* args) { @@ -112,6 +119,11 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { assert(_hazeStage); } + if (!_bloomStage) { + _bloomStage = args->_scene->getStage(); + assert(_bloomStage); + } + { // Sun // Need an update ? if (_needSunUpdate) { @@ -161,6 +173,15 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } + { + if (_needBloomUpdate) { + if (BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomIndex = _bloomStage->addBloom(_bloom); + } + _needBloomUpdate = false; + } + } + if (_visible) { // Finally, push the lights visible in the frame // @@ -190,6 +211,11 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { if (_hazeMode != COMPONENT_MODE_INHERIT) { _hazeStage->_currentFrame.pushHaze(_hazeIndex); } + + // Bloom only if the mode is not inherit, as the model deals with on/off + if (_bloomMode != COMPONENT_MODE_INHERIT) { + _bloomStage->_currentFrame.pushBloom(_bloomIndex); + } } } @@ -211,6 +237,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen bool ambientLightChanged = entity->ambientLightPropertiesChanged(); bool skyboxChanged = entity->skyboxPropertiesChanged(); bool hazeChanged = entity->hazePropertiesChanged(); + bool bloomChanged = entity->bloomPropertiesChanged(); entity->resetRenderingPropertiesChanged(); _lastPosition = entity->getWorldPosition(); @@ -221,6 +248,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _ambientLightProperties = entity->getAmbientLightProperties(); _skyboxProperties = entity->getSkyboxProperties(); _hazeProperties = entity->getHazeProperties(); + _bloomProperties = entity->getBloomProperties(); #if 0 if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) { @@ -258,6 +286,10 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen if (hazeChanged) { updateHazeFromEntity(entity); } + + if (bloomChanged) { + updateBloomFromEntity(entity); + } } void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { @@ -276,6 +308,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint if (entity->keyLightPropertiesChanged() || entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || + entity->bloomPropertiesChanged() || entity->skyboxPropertiesChanged()) { return true; @@ -388,6 +421,18 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setTransform(entity->getTransform().getMatrix()); } +void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) { + setBloomMode((ComponentMode)entity->getBloomMode()); + + const auto& bloom = editBloom(); + + const uint32_t bloomMode = entity->getBloomMode(); + bloom->setBloomActive(bloomMode == COMPONENT_MODE_ENABLED); + bloom->setBloomIntensity(_bloomProperties.getBloomIntensity()); + bloom->setBloomThreshold(_bloomProperties.getBloomThreshold()); + bloom->setBloomSize(_bloomProperties.getBloomSize()); +} + void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { setSkyboxMode((ComponentMode)entity->getSkyboxMode()); @@ -510,6 +555,10 @@ void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) { _skyboxMode = mode; } +void ZoneEntityRenderer::setBloomMode(ComponentMode mode) { + _bloomMode = mode; +} + void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) { editSkybox()->setColor(color); } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index c48679e5d4..3e2690e1bd 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -1,7 +1,6 @@ // // RenderableZoneEntityItem.h // -// // Created by Clement on 4/22/15. // Copyright 2015 High Fidelity, Inc. // @@ -15,10 +14,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include "RenderableEntityItem.h" #include @@ -50,6 +51,7 @@ private: void updateAmbientLightFromEntity(const TypedEntityPointer& entity); void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); + void updateBloomFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); @@ -59,6 +61,7 @@ private: void setKeyLightMode(ComponentMode mode); void setAmbientLightMode(ComponentMode mode); void setSkyboxMode(ComponentMode mode); + void setBloomMode(ComponentMode mode); void setSkyboxColor(const glm::vec3& color); void setProceduralUserData(const QString& userData); @@ -68,6 +71,7 @@ private: graphics::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; } graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } + graphics::BloomPointer editBloom() { _needBloomUpdate = true; return _bloom; } glm::vec3 _lastPosition; glm::vec3 _lastDimensions; @@ -82,36 +86,43 @@ private: #endif LightStagePointer _stage; - const graphics::LightPointer _sunLight{ std::make_shared() }; - const graphics::LightPointer _ambientLight{ std::make_shared() }; - const graphics::SunSkyStagePointer _background{ std::make_shared() }; - const graphics::HazePointer _haze{ std::make_shared() }; + const graphics::LightPointer _sunLight { std::make_shared() }; + const graphics::LightPointer _ambientLight { std::make_shared() }; + const graphics::SunSkyStagePointer _background { std::make_shared() }; + const graphics::HazePointer _haze { std::make_shared() }; + const graphics::BloomPointer _bloom { std::make_shared() }; ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; + ComponentMode _bloomMode { COMPONENT_MODE_INHERIT }; - indexed_container::Index _sunIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _shadowIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _ambientIndex{ LightStage::INVALID_INDEX }; + indexed_container::Index _sunIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _shadowIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _ambientIndex { LightStage::INVALID_INDEX }; BackgroundStagePointer _backgroundStage; - BackgroundStage::Index _backgroundIndex{ BackgroundStage::INVALID_INDEX }; + BackgroundStage::Index _backgroundIndex { BackgroundStage::INVALID_INDEX }; HazeStagePointer _hazeStage; - HazeStage::Index _hazeIndex{ HazeStage::INVALID_INDEX }; + HazeStage::Index _hazeIndex { HazeStage::INVALID_INDEX }; + + BloomStagePointer _bloomStage; + BloomStage::Index _bloomIndex { BloomStage::INVALID_INDEX }; bool _needUpdate{ true }; bool _needSunUpdate{ true }; bool _needAmbientUpdate{ true }; bool _needBackgroundUpdate{ true }; bool _needHazeUpdate{ true }; + bool _needBloomUpdate { true }; KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; SkyboxPropertyGroup _skyboxProperties; + BloomPropertyGroup _bloomProperties; // More attributes used for rendering: QString _ambientTextureURL; diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp new file mode 100644 index 0000000000..2c4d46ab35 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.cpp @@ -0,0 +1,159 @@ +// +// BloomPropertyGroup.cpp +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); +} + +void BloomPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomIntensity, float, setBloomIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomThreshold, float, setBloomThreshold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomSize, float, setBloomSize); +} + +void BloomPropertyGroup::merge(const BloomPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(bloomIntensity); + COPY_PROPERTY_IF_CHANGED(bloomThreshold); + COPY_PROPERTY_IF_CHANGED(bloomSize); +} + +void BloomPropertyGroup::debugDump() const { + qCDebug(entities) << " BloomPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " _bloomIntensity:" << _bloomIntensity; + qCDebug(entities) << " _bloomThreshold:" << _bloomThreshold; + qCDebug(entities) << " _bloomSize:" << _bloomSize; +} + +void BloomPropertyGroup::listChangedProperties(QList& out) { + if (bloomIntensityChanged()) { + out << "bloom-bloomIntensity"; + } + if (bloomThresholdChanged()) { + out << "bloom-bloomThreshold"; + } + if (bloomSizeChanged()) { + out << "bloom-bloomSize"; + } +} + +bool BloomPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); + + return true; +} + +bool BloomPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_INTENSITY, BloomIntensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_THRESHOLD, BloomThreshold); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_SIZE, BloomSize); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void BloomPropertyGroup::markAllChanged() { + _bloomIntensityChanged = true; + _bloomThresholdChanged = true; + _bloomSizeChanged = true; +} + +EntityPropertyFlags BloomPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_BLOOM_INTENSITY, bloomIntensity); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_THRESHOLD, bloomThreshold); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_SIZE, bloomSize); + + return changedProperties; +} + +void BloomPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomIntensity, getBloomIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomThreshold, getBloomThreshold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomSize, getBloomSize); +} + +bool BloomPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomIntensity, bloomIntensity, setBloomIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomThreshold, bloomThreshold, setBloomThreshold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomSize, bloomSize, setBloomSize); + + return somethingChanged; +} + +EntityPropertyFlags BloomPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_BLOOM_INTENSITY; + requestedProperties += PROP_BLOOM_THRESHOLD; + requestedProperties += PROP_BLOOM_SIZE; + + return requestedProperties; +} + +void BloomPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); +} + +int BloomPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + return bytesRead; +} diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h new file mode 100644 index 0000000000..a1f9b6d748 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.h @@ -0,0 +1,94 @@ +// +// BloomPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_BloomPropertyGroup_h +#define hifi_BloomPropertyGroup_h + +#include +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; + +static const float INITIAL_BLOOM_INTENSITY { 0.25f }; +static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; +static const float INITIAL_BLOOM_SIZE { 0.9f }; + +/**jsdoc + * Bloom is defined by the following properties. + * @typedef {object} Entities.Bloom + * + * @property {number} bloomIntensity=0.25 - The intensity of the bloom effect. + * @property {number} bloomThreshold=0.7 - The threshold for the bloom effect. + * @property {number} bloomSize=0.9 - The size of the bloom effect. + */ +class BloomPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + + void merge(const BloomPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY(PROP_BLOOM_INTENSITY, BloomIntensity, bloomIntensity, float, INITIAL_BLOOM_INTENSITY); + DEFINE_PROPERTY(PROP_BLOOM_THRESHOLD, BloomThreshold, bloomThreshold, float, INITIAL_BLOOM_THRESHOLD); + DEFINE_PROPERTY(PROP_BLOOM_SIZE, BloomSize, bloomSize, float, INITIAL_BLOOM_SIZE); + +}; + +#endif // hifi_BloomPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 1f9bf2eb18..68d0e6aed4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -37,6 +37,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; HazePropertyGroup EntityItemProperties::_staticHaze; +BloomPropertyGroup EntityItemProperties::_staticBloom; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; @@ -84,6 +85,7 @@ void EntityItemProperties::debugDump() const { getHaze().debugDump(); getKeyLight().debugDump(); getAmbientLight().debugDump(); + getBloom().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -211,6 +213,10 @@ QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } +QString EntityItemProperties::getBloomModeAsString() const { + return getComponentModeAsString(_bloomMode); +} + QString EntityItemProperties::getComponentModeString(uint32_t mode) { // return "inherit" if mode is not valid if (mode < COMPONENT_MODE_ITEM_COUNT) { @@ -235,6 +241,15 @@ void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { } } +void EntityItemProperties::setBloomModeFromString(const QString& bloomMode) { + auto result = findComponent(bloomMode); + + if (result != COMPONENT_MODES.end()) { + _bloomMode = result->first; + _bloomModeChanged = true; + } +} + QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } @@ -394,6 +409,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -454,6 +470,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _ambientLight.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); + changedProperties += _bloom.getChangedProperties(); return changedProperties; } @@ -1164,6 +1181,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * "enabled": The haze properties of this zone are enabled, overriding the haze from any enclosing zone. * @property {Entities.Haze} haze - The haze properties of the zone. * + * @property {string} bloomMode="inherit" - Configures the bloom in the zone. Possible values:
+ * "inherit": The bloom from any enclosing zone continues into this zone.
+ * "disabled": The bloom from any enclosing zone and the bloom of this zone are disabled in this zone.
+ * "enabled": The bloom properties of this zone are enabled, overriding the bloom from any enclosing zone. + * @property {Entities.Bloom} bloom - The bloom properties of the zone. + * * @property {boolean} flyingAllowed=true - If true then visitors can fly in the zone; otherwise they cannot. * @property {boolean} ghostingAllowed=true - If true then visitors with avatar collisions turned off will not * collide with content in the zone; otherwise visitors will always collide with content in the zone. @@ -1382,6 +1405,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); + _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); @@ -1630,6 +1656,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -1662,6 +1689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _ambientLight.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); + _bloom.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); @@ -1803,6 +1831,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(keyLightMode); COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); + COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -1825,6 +1854,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _ambientLight.merge(other._ambientLight); _skybox.merge(other._skybox); _haze.merge(other._haze); + _bloom.merge(other._bloom); COPY_PROPERTY_IF_CHANGED(xTextureURL); COPY_PROPERTY_IF_CHANGED(yTextureURL); @@ -2096,6 +2126,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); + ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); + ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t); @@ -2357,6 +2392,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticHaze.setProperties(properties); _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); + _staticBloom.setProperties(properties); + _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); @@ -2731,6 +2770,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); + properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); @@ -3044,10 +3086,12 @@ void EntityItemProperties::markAllChanged() { _skyboxModeChanged = true; _ambientLightModeChanged = true; _hazeModeChanged = true; + _bloomModeChanged = true; _animation.markAllChanged(); _skybox.markAllChanged(); _haze.markAllChanged(); + _bloom.markAllChanged(); _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; @@ -3442,15 +3486,15 @@ QList EntityItemProperties::listChangedProperties() { if (hazeModeChanged()) { out += "hazeMode"; } - + if (bloomModeChanged()) { + out += "bloomMode"; + } if (keyLightModeChanged()) { out += "keyLightMode"; } - if (ambientLightModeChanged()) { out += "ambientLightMode"; } - if (skyboxModeChanged()) { out += "skyboxMode"; } @@ -3581,6 +3625,7 @@ QList EntityItemProperties::listChangedProperties() { getAmbientLight().listChangedProperties(out); getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); + getBloom().listChangedProperties(out); return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 04e54c54a5..50305345de 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -42,6 +42,7 @@ #include "SimulationOwner.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include "TextEntityItem.h" #include "ZoneEntityItem.h" @@ -195,9 +196,11 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); + DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); @@ -533,6 +536,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, BloomMode, bloomMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 156c5d9dd4..3932730661 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -257,6 +257,11 @@ enum EntityPropertyList { PROP_SPIN_SPREAD, PROP_PARTICLE_ROTATE_WITH_ENTITY, + PROP_BLOOM_MODE, + PROP_BLOOM_INTENSITY, + PROP_BLOOM_THRESHOLD, + PROP_BLOOM_SIZE, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index f2550e5d3c..a7dfa1a41e 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -47,33 +47,27 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - // Contains a QString property, must be synchronized + // Contain QString properties, must be synchronized withReadLock([&] { _keyLightProperties.getProperties(properties); - }); - - withReadLock([&] { _ambientLightProperties.getProperties(properties); + _skyboxProperties.getProperties(properties); }); + _hazeProperties.getProperties(properties); + _bloomProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - // Contains a QString property, must be synchronized - withReadLock([&] { - _skyboxProperties.getProperties(properties); - }); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); - _hazeProperties.getProperties(properties); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); return properties; } @@ -102,32 +96,27 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie // Contains a QString property, must be synchronized withWriteLock([&] { _keyLightPropertiesChanged = _keyLightProperties.setProperties(properties); - }); - withWriteLock([&] { _ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties); + _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); }); + _hazePropertiesChanged = _hazeProperties.setProperties(properties); + _bloomPropertiesChanged = _bloomProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - // Contains a QString property, must be synchronized - withWriteLock([&] { - _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); - }); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); - _hazePropertiesChanged = _hazeProperties.setProperties(properties); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || - _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged; + _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged; return somethingChanged; } @@ -139,54 +128,67 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - int bytesFromKeylight; - withWriteLock([&] { - bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); - }); + { + int bytesFromKeylight; + withWriteLock([&] { + bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _keyLightPropertiesChanged; + bytesRead += bytesFromKeylight; + dataAt += bytesFromKeylight; + } - somethingChanged = somethingChanged || _keyLightPropertiesChanged; - bytesRead += bytesFromKeylight; - dataAt += bytesFromKeylight; + { + int bytesFromAmbientlight; + withWriteLock([&] { + bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _ambientLightPropertiesChanged; + bytesRead += bytesFromAmbientlight; + dataAt += bytesFromAmbientlight; + } - int bytesFromAmbientlight; - withWriteLock([&] { - bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); - }); + { + int bytesFromSkybox; + withWriteLock([&] { + bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); + }); + somethingChanged = somethingChanged || _skyboxPropertiesChanged; + bytesRead += bytesFromSkybox; + dataAt += bytesFromSkybox; + } - somethingChanged = somethingChanged || _ambientLightPropertiesChanged; - bytesRead += bytesFromAmbientlight; - dataAt += bytesFromAmbientlight; + { + int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _hazePropertiesChanged); + somethingChanged = somethingChanged || _hazePropertiesChanged; + bytesRead += bytesFromHaze; + dataAt += bytesFromHaze; + } + + { + int bytesFromBloom = _bloomProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _bloomPropertiesChanged); + somethingChanged = somethingChanged || _bloomPropertiesChanged; + bytesRead += bytesFromBloom; + dataAt += bytesFromBloom; + } READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - int bytesFromSkybox; - withWriteLock([&] { - bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); - }); - somethingChanged = somethingChanged || _skyboxPropertiesChanged; - bytesRead += bytesFromSkybox; - dataAt += bytesFromSkybox; - READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); - - int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _hazePropertiesChanged); - - somethingChanged = somethingChanged || _hazePropertiesChanged; - bytesRead += bytesFromHaze; - dataAt += bytesFromHaze; - READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); return bytesRead; } @@ -196,29 +198,24 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p withReadLock([&] { requestedProperties += _keyLightProperties.getEntityProperties(params); - }); - - withReadLock([&] { requestedProperties += _ambientLightProperties.getEntityProperties(params); + requestedProperties += _skyboxProperties.getEntityProperties(params); }); + requestedProperties += _hazeProperties.getEntityProperties(params); + requestedProperties += _bloomProperties.getEntityProperties(params); requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; - withReadLock([&] { - requestedProperties += _skyboxProperties.getEntityProperties(params); - }); - requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; requestedProperties += PROP_FILTER_URL; requestedProperties += PROP_HAZE_MODE; - requestedProperties += _hazeProperties.getEntityProperties(params); - requestedProperties += PROP_KEY_LIGHT_MODE; requestedProperties += PROP_AMBIENT_LIGHT_MODE; requestedProperties += PROP_SKYBOX_MODE; + requestedProperties += PROP_BLOOM_MODE; return requestedProperties; } @@ -235,44 +232,46 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); - _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); } void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeString(_bloomMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); _skyboxProperties.debugDump(); _hazeProperties.debugDump(); + _bloomProperties.debugDump(); } ShapeType ZoneEntityItem::getShapeType() const { @@ -344,6 +343,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { _ambientLightPropertiesChanged = false; _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; + _bloomPropertiesChanged = false; _stagePropertiesChanged = false; }); } @@ -359,6 +359,17 @@ uint32_t ZoneEntityItem::getHazeMode() const { return _hazeMode; } +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getBloomMode() const { + return _bloomMode; +} + void ZoneEntityItem::setKeyLightMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { _keyLightMode = value; diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 0aaa32a57a..e2ebf16f11 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -18,6 +18,7 @@ #include "EntityTree.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include class ZoneEntityItem : public EntityItem { @@ -79,9 +80,13 @@ public: void setSkyboxMode(uint32_t value); uint32_t getSkyboxMode() const; + void setBloomMode(const uint32_t value); + uint32_t getBloomMode() const; + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } + const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } bool getFlyingAllowed() const { return _flyingAllowed; } void setFlyingAllowed(bool value) { _flyingAllowed = value; } @@ -93,10 +98,8 @@ public: bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } - - bool hazePropertiesChanged() const { - return _hazePropertiesChanged; - } + bool hazePropertiesChanged() const { return _hazePropertiesChanged; } + bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; } bool stagePropertiesChanged() const { return _stagePropertiesChanged; } @@ -133,9 +136,11 @@ protected: uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; + uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; + BloomPropertyGroup _bloomProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; @@ -146,6 +151,7 @@ protected: bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged{ false }; + bool _bloomPropertiesChanged { false }; bool _stagePropertiesChanged { false }; static bool _drawZoneBoundaries; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 82e9820509..ae165af5d1 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::ParticleSpin); + return static_cast(EntityVersion::BloomEffect); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 64c5bfe534..bfc92b9bde 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -240,7 +240,8 @@ enum class EntityVersion : PacketVersion { CollisionMask16Bytes, YieldSimulationOwnership, ParticleEntityFix, - ParticleSpin + ParticleSpin, + BloomEffect }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index e05f28ef0d..be61953073 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -14,6 +14,7 @@ #include "LightStage.h" #include "BackgroundStage.h" #include "HazeStage.h" +#include "BloomStage.h" #include #include #include "DeferredLightingEffect.h" @@ -22,6 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("LightStageSetup"); task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); + task.addJob("BloomStageSetup"); task.addJob("TransitionStageSetup"); task.addJob("HighlightStageSetup"); From 274bf33318d06924fcdd6bd537f7723254c6792b Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 7 Aug 2018 18:29:51 -0700 Subject: [PATCH 008/207] fix for the avatar scale reset when we re-enter hmd mode --- interface/src/Application.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 6 ++++++ interface/src/avatar/MySkeletonModel.cpp | 2 ++ libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 1 + libraries/render-utils/src/Model.cpp | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bbd2b1c17..d1871c7692 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8133,7 +8133,6 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { // reset the avatar, to set head and hand palms back to a reasonable default pose. getMyAvatar()->reset(false); - // switch to first person if entering hmd and setting is checked if (menu) { QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3f738ea4cb..0fdd7edb7a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -806,6 +806,7 @@ void MyAvatar::updateSensorToWorldMatrix() { // update the sensor mat so that the body position will end up in the desired // position when driven from the head. float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); + qCDebug(interfaceapp) << sensorToWorldScale; glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); @@ -1742,6 +1743,10 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); + glm::mat4 printout = getSensorToWorldMatrix(); + if (action == controller::Action::HEAD) { + //qCDebug(interfaceapp) << "matrix 4 sensor to world" << printout; + } if (pose.valid) { return pose.transform(getSensorToWorldMatrix()); } else { @@ -1751,6 +1756,7 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); + // qCDebug(interfaceapp) << " the head position in world frame is " << pose.getTranslation(); if (pose.valid) { glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0fc5e7521e..6bc0c8fefa 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -116,7 +116,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); + if (avatarHeadPose.isValid()) { + //qCDebug(interfaceapp) << " the head position in avatar frame is " << avatarHeadPose.getTranslation(); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 69356cdfaa..0fda5be77b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1761,6 +1761,7 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& // thread-safe float Avatar::getEyeHeight() const { + qCDebug(animation) << "modelscale "< Date: Tue, 7 Aug 2018 18:18:58 -0700 Subject: [PATCH 009/207] working on enabling bloom --- .../src/RenderableZoneEntityItem.cpp | 49 ++++++ .../src/RenderableZoneEntityItem.h | 31 ++-- libraries/entities/src/BloomPropertyGroup.cpp | 159 +++++++++++++++++ libraries/entities/src/BloomPropertyGroup.h | 94 ++++++++++ .../entities/src/EntityItemProperties.cpp | 51 +++++- libraries/entities/src/EntityItemProperties.h | 4 + libraries/entities/src/EntityPropertyFlags.h | 5 + libraries/entities/src/ZoneEntityItem.cpp | 165 ++++++++++-------- libraries/entities/src/ZoneEntityItem.h | 14 +- libraries/graphics/src/graphics/Bloom.cpp | 18 ++ libraries/graphics/src/graphics/Bloom.h | 44 +++++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/render-utils/src/BloomEffect.cpp | 93 +++++----- libraries/render-utils/src/BloomEffect.h | 49 +----- libraries/render-utils/src/BloomStage.cpp | 80 +++++++++ libraries/render-utils/src/BloomStage.h | 122 +++++++++++++ libraries/render-utils/src/HazeStage.h | 1 - .../render-utils/src/RenderDeferredTask.cpp | 8 +- .../render-utils/src/UpdateSceneTask.cpp | 2 + libraries/render-utils/src/ZoneRenderer.cpp | 10 +- scripts/system/html/entityProperties.html | 46 +++++ scripts/system/html/js/entityProperties.js | 88 +++++++--- 23 files changed, 917 insertions(+), 221 deletions(-) create mode 100644 libraries/entities/src/BloomPropertyGroup.cpp create mode 100644 libraries/entities/src/BloomPropertyGroup.h create mode 100644 libraries/graphics/src/graphics/Bloom.cpp create mode 100644 libraries/graphics/src/graphics/Bloom.h create mode 100644 libraries/render-utils/src/BloomStage.cpp create mode 100644 libraries/render-utils/src/BloomStage.h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c5035431f6..f23e8096f1 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -64,6 +64,13 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity _hazeIndex = INVALID_INDEX; } } + + if (_bloomStage) { + if (!BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomStage->removeBloom(_bloomIndex); + _bloomIndex = INVALID_INDEX; + } + } } void ZoneEntityRenderer::doRender(RenderArgs* args) { @@ -112,6 +119,11 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { assert(_hazeStage); } + if (!_bloomStage) { + _bloomStage = args->_scene->getStage(); + assert(_bloomStage); + } + { // Sun // Need an update ? if (_needSunUpdate) { @@ -161,6 +173,15 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } + { + if (_needBloomUpdate) { + if (BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomIndex = _bloomStage->addBloom(_bloom); + } + _needBloomUpdate = false; + } + } + if (_visible) { // Finally, push the lights visible in the frame // @@ -190,6 +211,11 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { if (_hazeMode != COMPONENT_MODE_INHERIT) { _hazeStage->_currentFrame.pushHaze(_hazeIndex); } + + // Bloom only if the mode is not inherit, as the model deals with on/off + if (_bloomMode != COMPONENT_MODE_INHERIT) { + _bloomStage->_currentFrame.pushBloom(_bloomIndex); + } } } @@ -211,6 +237,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen bool ambientLightChanged = entity->ambientLightPropertiesChanged(); bool skyboxChanged = entity->skyboxPropertiesChanged(); bool hazeChanged = entity->hazePropertiesChanged(); + bool bloomChanged = entity->bloomPropertiesChanged(); entity->resetRenderingPropertiesChanged(); _lastPosition = entity->getWorldPosition(); @@ -221,6 +248,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _ambientLightProperties = entity->getAmbientLightProperties(); _skyboxProperties = entity->getSkyboxProperties(); _hazeProperties = entity->getHazeProperties(); + _bloomProperties = entity->getBloomProperties(); #if 0 if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) { @@ -258,6 +286,10 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen if (hazeChanged) { updateHazeFromEntity(entity); } + + if (bloomChanged) { + updateBloomFromEntity(entity); + } } void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { @@ -276,6 +308,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint if (entity->keyLightPropertiesChanged() || entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || + entity->bloomPropertiesChanged() || entity->skyboxPropertiesChanged()) { return true; @@ -388,6 +421,18 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setTransform(entity->getTransform().getMatrix()); } +void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) { + setBloomMode((ComponentMode)entity->getBloomMode()); + + const auto& bloom = editBloom(); + + const uint32_t bloomMode = entity->getBloomMode(); + bloom->setBloomActive(bloomMode == COMPONENT_MODE_ENABLED); + bloom->setBloomIntensity(_bloomProperties.getBloomIntensity()); + bloom->setBloomThreshold(_bloomProperties.getBloomThreshold()); + bloom->setBloomSize(_bloomProperties.getBloomSize()); +} + void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { setSkyboxMode((ComponentMode)entity->getSkyboxMode()); @@ -510,6 +555,10 @@ void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) { _skyboxMode = mode; } +void ZoneEntityRenderer::setBloomMode(ComponentMode mode) { + _bloomMode = mode; +} + void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) { editSkybox()->setColor(color); } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index c48679e5d4..3e2690e1bd 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -1,7 +1,6 @@ // // RenderableZoneEntityItem.h // -// // Created by Clement on 4/22/15. // Copyright 2015 High Fidelity, Inc. // @@ -15,10 +14,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include "RenderableEntityItem.h" #include @@ -50,6 +51,7 @@ private: void updateAmbientLightFromEntity(const TypedEntityPointer& entity); void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); + void updateBloomFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); @@ -59,6 +61,7 @@ private: void setKeyLightMode(ComponentMode mode); void setAmbientLightMode(ComponentMode mode); void setSkyboxMode(ComponentMode mode); + void setBloomMode(ComponentMode mode); void setSkyboxColor(const glm::vec3& color); void setProceduralUserData(const QString& userData); @@ -68,6 +71,7 @@ private: graphics::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; } graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } + graphics::BloomPointer editBloom() { _needBloomUpdate = true; return _bloom; } glm::vec3 _lastPosition; glm::vec3 _lastDimensions; @@ -82,36 +86,43 @@ private: #endif LightStagePointer _stage; - const graphics::LightPointer _sunLight{ std::make_shared() }; - const graphics::LightPointer _ambientLight{ std::make_shared() }; - const graphics::SunSkyStagePointer _background{ std::make_shared() }; - const graphics::HazePointer _haze{ std::make_shared() }; + const graphics::LightPointer _sunLight { std::make_shared() }; + const graphics::LightPointer _ambientLight { std::make_shared() }; + const graphics::SunSkyStagePointer _background { std::make_shared() }; + const graphics::HazePointer _haze { std::make_shared() }; + const graphics::BloomPointer _bloom { std::make_shared() }; ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; + ComponentMode _bloomMode { COMPONENT_MODE_INHERIT }; - indexed_container::Index _sunIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _shadowIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _ambientIndex{ LightStage::INVALID_INDEX }; + indexed_container::Index _sunIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _shadowIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _ambientIndex { LightStage::INVALID_INDEX }; BackgroundStagePointer _backgroundStage; - BackgroundStage::Index _backgroundIndex{ BackgroundStage::INVALID_INDEX }; + BackgroundStage::Index _backgroundIndex { BackgroundStage::INVALID_INDEX }; HazeStagePointer _hazeStage; - HazeStage::Index _hazeIndex{ HazeStage::INVALID_INDEX }; + HazeStage::Index _hazeIndex { HazeStage::INVALID_INDEX }; + + BloomStagePointer _bloomStage; + BloomStage::Index _bloomIndex { BloomStage::INVALID_INDEX }; bool _needUpdate{ true }; bool _needSunUpdate{ true }; bool _needAmbientUpdate{ true }; bool _needBackgroundUpdate{ true }; bool _needHazeUpdate{ true }; + bool _needBloomUpdate { true }; KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; SkyboxPropertyGroup _skyboxProperties; + BloomPropertyGroup _bloomProperties; // More attributes used for rendering: QString _ambientTextureURL; diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp new file mode 100644 index 0000000000..2c4d46ab35 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.cpp @@ -0,0 +1,159 @@ +// +// BloomPropertyGroup.cpp +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); +} + +void BloomPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomIntensity, float, setBloomIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomThreshold, float, setBloomThreshold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomSize, float, setBloomSize); +} + +void BloomPropertyGroup::merge(const BloomPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(bloomIntensity); + COPY_PROPERTY_IF_CHANGED(bloomThreshold); + COPY_PROPERTY_IF_CHANGED(bloomSize); +} + +void BloomPropertyGroup::debugDump() const { + qCDebug(entities) << " BloomPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " _bloomIntensity:" << _bloomIntensity; + qCDebug(entities) << " _bloomThreshold:" << _bloomThreshold; + qCDebug(entities) << " _bloomSize:" << _bloomSize; +} + +void BloomPropertyGroup::listChangedProperties(QList& out) { + if (bloomIntensityChanged()) { + out << "bloom-bloomIntensity"; + } + if (bloomThresholdChanged()) { + out << "bloom-bloomThreshold"; + } + if (bloomSizeChanged()) { + out << "bloom-bloomSize"; + } +} + +bool BloomPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); + + return true; +} + +bool BloomPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_INTENSITY, BloomIntensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_THRESHOLD, BloomThreshold); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_SIZE, BloomSize); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void BloomPropertyGroup::markAllChanged() { + _bloomIntensityChanged = true; + _bloomThresholdChanged = true; + _bloomSizeChanged = true; +} + +EntityPropertyFlags BloomPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_BLOOM_INTENSITY, bloomIntensity); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_THRESHOLD, bloomThreshold); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_SIZE, bloomSize); + + return changedProperties; +} + +void BloomPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomIntensity, getBloomIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomThreshold, getBloomThreshold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomSize, getBloomSize); +} + +bool BloomPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomIntensity, bloomIntensity, setBloomIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomThreshold, bloomThreshold, setBloomThreshold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomSize, bloomSize, setBloomSize); + + return somethingChanged; +} + +EntityPropertyFlags BloomPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_BLOOM_INTENSITY; + requestedProperties += PROP_BLOOM_THRESHOLD; + requestedProperties += PROP_BLOOM_SIZE; + + return requestedProperties; +} + +void BloomPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); +} + +int BloomPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + return bytesRead; +} diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h new file mode 100644 index 0000000000..a1f9b6d748 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.h @@ -0,0 +1,94 @@ +// +// BloomPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_BloomPropertyGroup_h +#define hifi_BloomPropertyGroup_h + +#include +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; + +static const float INITIAL_BLOOM_INTENSITY { 0.25f }; +static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; +static const float INITIAL_BLOOM_SIZE { 0.9f }; + +/**jsdoc + * Bloom is defined by the following properties. + * @typedef {object} Entities.Bloom + * + * @property {number} bloomIntensity=0.25 - The intensity of the bloom effect. + * @property {number} bloomThreshold=0.7 - The threshold for the bloom effect. + * @property {number} bloomSize=0.9 - The size of the bloom effect. + */ +class BloomPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + + void merge(const BloomPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY(PROP_BLOOM_INTENSITY, BloomIntensity, bloomIntensity, float, INITIAL_BLOOM_INTENSITY); + DEFINE_PROPERTY(PROP_BLOOM_THRESHOLD, BloomThreshold, bloomThreshold, float, INITIAL_BLOOM_THRESHOLD); + DEFINE_PROPERTY(PROP_BLOOM_SIZE, BloomSize, bloomSize, float, INITIAL_BLOOM_SIZE); + +}; + +#endif // hifi_BloomPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 1f9bf2eb18..68d0e6aed4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -37,6 +37,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; HazePropertyGroup EntityItemProperties::_staticHaze; +BloomPropertyGroup EntityItemProperties::_staticBloom; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; @@ -84,6 +85,7 @@ void EntityItemProperties::debugDump() const { getHaze().debugDump(); getKeyLight().debugDump(); getAmbientLight().debugDump(); + getBloom().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -211,6 +213,10 @@ QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } +QString EntityItemProperties::getBloomModeAsString() const { + return getComponentModeAsString(_bloomMode); +} + QString EntityItemProperties::getComponentModeString(uint32_t mode) { // return "inherit" if mode is not valid if (mode < COMPONENT_MODE_ITEM_COUNT) { @@ -235,6 +241,15 @@ void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { } } +void EntityItemProperties::setBloomModeFromString(const QString& bloomMode) { + auto result = findComponent(bloomMode); + + if (result != COMPONENT_MODES.end()) { + _bloomMode = result->first; + _bloomModeChanged = true; + } +} + QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } @@ -394,6 +409,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -454,6 +470,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _ambientLight.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); + changedProperties += _bloom.getChangedProperties(); return changedProperties; } @@ -1164,6 +1181,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * "enabled": The haze properties of this zone are enabled, overriding the haze from any enclosing zone. * @property {Entities.Haze} haze - The haze properties of the zone. * + * @property {string} bloomMode="inherit" - Configures the bloom in the zone. Possible values:
+ * "inherit": The bloom from any enclosing zone continues into this zone.
+ * "disabled": The bloom from any enclosing zone and the bloom of this zone are disabled in this zone.
+ * "enabled": The bloom properties of this zone are enabled, overriding the bloom from any enclosing zone. + * @property {Entities.Bloom} bloom - The bloom properties of the zone. + * * @property {boolean} flyingAllowed=true - If true then visitors can fly in the zone; otherwise they cannot. * @property {boolean} ghostingAllowed=true - If true then visitors with avatar collisions turned off will not * collide with content in the zone; otherwise visitors will always collide with content in the zone. @@ -1382,6 +1405,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); + _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); @@ -1630,6 +1656,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -1662,6 +1689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _ambientLight.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); + _bloom.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); @@ -1803,6 +1831,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(keyLightMode); COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); + COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -1825,6 +1854,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _ambientLight.merge(other._ambientLight); _skybox.merge(other._skybox); _haze.merge(other._haze); + _bloom.merge(other._bloom); COPY_PROPERTY_IF_CHANGED(xTextureURL); COPY_PROPERTY_IF_CHANGED(yTextureURL); @@ -2096,6 +2126,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); + ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); + ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t); @@ -2357,6 +2392,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticHaze.setProperties(properties); _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); + _staticBloom.setProperties(properties); + _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); @@ -2731,6 +2770,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); + properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); @@ -3044,10 +3086,12 @@ void EntityItemProperties::markAllChanged() { _skyboxModeChanged = true; _ambientLightModeChanged = true; _hazeModeChanged = true; + _bloomModeChanged = true; _animation.markAllChanged(); _skybox.markAllChanged(); _haze.markAllChanged(); + _bloom.markAllChanged(); _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; @@ -3442,15 +3486,15 @@ QList EntityItemProperties::listChangedProperties() { if (hazeModeChanged()) { out += "hazeMode"; } - + if (bloomModeChanged()) { + out += "bloomMode"; + } if (keyLightModeChanged()) { out += "keyLightMode"; } - if (ambientLightModeChanged()) { out += "ambientLightMode"; } - if (skyboxModeChanged()) { out += "skyboxMode"; } @@ -3581,6 +3625,7 @@ QList EntityItemProperties::listChangedProperties() { getAmbientLight().listChangedProperties(out); getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); + getBloom().listChangedProperties(out); return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 04e54c54a5..50305345de 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -42,6 +42,7 @@ #include "SimulationOwner.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include "TextEntityItem.h" #include "ZoneEntityItem.h" @@ -195,9 +196,11 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); + DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); @@ -533,6 +536,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, BloomMode, bloomMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 156c5d9dd4..3932730661 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -257,6 +257,11 @@ enum EntityPropertyList { PROP_SPIN_SPREAD, PROP_PARTICLE_ROTATE_WITH_ENTITY, + PROP_BLOOM_MODE, + PROP_BLOOM_INTENSITY, + PROP_BLOOM_THRESHOLD, + PROP_BLOOM_SIZE, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index f2550e5d3c..a7dfa1a41e 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -47,33 +47,27 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - // Contains a QString property, must be synchronized + // Contain QString properties, must be synchronized withReadLock([&] { _keyLightProperties.getProperties(properties); - }); - - withReadLock([&] { _ambientLightProperties.getProperties(properties); + _skyboxProperties.getProperties(properties); }); + _hazeProperties.getProperties(properties); + _bloomProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - // Contains a QString property, must be synchronized - withReadLock([&] { - _skyboxProperties.getProperties(properties); - }); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); - _hazeProperties.getProperties(properties); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); return properties; } @@ -102,32 +96,27 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie // Contains a QString property, must be synchronized withWriteLock([&] { _keyLightPropertiesChanged = _keyLightProperties.setProperties(properties); - }); - withWriteLock([&] { _ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties); + _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); }); + _hazePropertiesChanged = _hazeProperties.setProperties(properties); + _bloomPropertiesChanged = _bloomProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - // Contains a QString property, must be synchronized - withWriteLock([&] { - _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); - }); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); - _hazePropertiesChanged = _hazeProperties.setProperties(properties); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || - _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged; + _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged; return somethingChanged; } @@ -139,54 +128,67 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - int bytesFromKeylight; - withWriteLock([&] { - bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); - }); + { + int bytesFromKeylight; + withWriteLock([&] { + bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _keyLightPropertiesChanged; + bytesRead += bytesFromKeylight; + dataAt += bytesFromKeylight; + } - somethingChanged = somethingChanged || _keyLightPropertiesChanged; - bytesRead += bytesFromKeylight; - dataAt += bytesFromKeylight; + { + int bytesFromAmbientlight; + withWriteLock([&] { + bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _ambientLightPropertiesChanged; + bytesRead += bytesFromAmbientlight; + dataAt += bytesFromAmbientlight; + } - int bytesFromAmbientlight; - withWriteLock([&] { - bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); - }); + { + int bytesFromSkybox; + withWriteLock([&] { + bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); + }); + somethingChanged = somethingChanged || _skyboxPropertiesChanged; + bytesRead += bytesFromSkybox; + dataAt += bytesFromSkybox; + } - somethingChanged = somethingChanged || _ambientLightPropertiesChanged; - bytesRead += bytesFromAmbientlight; - dataAt += bytesFromAmbientlight; + { + int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _hazePropertiesChanged); + somethingChanged = somethingChanged || _hazePropertiesChanged; + bytesRead += bytesFromHaze; + dataAt += bytesFromHaze; + } + + { + int bytesFromBloom = _bloomProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _bloomPropertiesChanged); + somethingChanged = somethingChanged || _bloomPropertiesChanged; + bytesRead += bytesFromBloom; + dataAt += bytesFromBloom; + } READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - int bytesFromSkybox; - withWriteLock([&] { - bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); - }); - somethingChanged = somethingChanged || _skyboxPropertiesChanged; - bytesRead += bytesFromSkybox; - dataAt += bytesFromSkybox; - READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); - - int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _hazePropertiesChanged); - - somethingChanged = somethingChanged || _hazePropertiesChanged; - bytesRead += bytesFromHaze; - dataAt += bytesFromHaze; - READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); return bytesRead; } @@ -196,29 +198,24 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p withReadLock([&] { requestedProperties += _keyLightProperties.getEntityProperties(params); - }); - - withReadLock([&] { requestedProperties += _ambientLightProperties.getEntityProperties(params); + requestedProperties += _skyboxProperties.getEntityProperties(params); }); + requestedProperties += _hazeProperties.getEntityProperties(params); + requestedProperties += _bloomProperties.getEntityProperties(params); requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; - withReadLock([&] { - requestedProperties += _skyboxProperties.getEntityProperties(params); - }); - requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; requestedProperties += PROP_FILTER_URL; requestedProperties += PROP_HAZE_MODE; - requestedProperties += _hazeProperties.getEntityProperties(params); - requestedProperties += PROP_KEY_LIGHT_MODE; requestedProperties += PROP_AMBIENT_LIGHT_MODE; requestedProperties += PROP_SKYBOX_MODE; + requestedProperties += PROP_BLOOM_MODE; return requestedProperties; } @@ -235,44 +232,46 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); - _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); } void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeString(_bloomMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); _skyboxProperties.debugDump(); _hazeProperties.debugDump(); + _bloomProperties.debugDump(); } ShapeType ZoneEntityItem::getShapeType() const { @@ -344,6 +343,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { _ambientLightPropertiesChanged = false; _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; + _bloomPropertiesChanged = false; _stagePropertiesChanged = false; }); } @@ -359,6 +359,17 @@ uint32_t ZoneEntityItem::getHazeMode() const { return _hazeMode; } +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getBloomMode() const { + return _bloomMode; +} + void ZoneEntityItem::setKeyLightMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { _keyLightMode = value; diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 0aaa32a57a..e2ebf16f11 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -18,6 +18,7 @@ #include "EntityTree.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include class ZoneEntityItem : public EntityItem { @@ -79,9 +80,13 @@ public: void setSkyboxMode(uint32_t value); uint32_t getSkyboxMode() const; + void setBloomMode(const uint32_t value); + uint32_t getBloomMode() const; + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } + const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } bool getFlyingAllowed() const { return _flyingAllowed; } void setFlyingAllowed(bool value) { _flyingAllowed = value; } @@ -93,10 +98,8 @@ public: bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } - - bool hazePropertiesChanged() const { - return _hazePropertiesChanged; - } + bool hazePropertiesChanged() const { return _hazePropertiesChanged; } + bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; } bool stagePropertiesChanged() const { return _stagePropertiesChanged; } @@ -133,9 +136,11 @@ protected: uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; + uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; + BloomPropertyGroup _bloomProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; @@ -146,6 +151,7 @@ protected: bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged{ false }; + bool _bloomPropertiesChanged { false }; bool _stagePropertiesChanged { false }; static bool _drawZoneBoundaries; diff --git a/libraries/graphics/src/graphics/Bloom.cpp b/libraries/graphics/src/graphics/Bloom.cpp new file mode 100644 index 0000000000..f8dcda3292 --- /dev/null +++ b/libraries/graphics/src/graphics/Bloom.cpp @@ -0,0 +1,18 @@ +// +// Bloom.cpp +// libraries/graphics/src/graphics +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "Bloom.h" + +using namespace graphics; + +const float Bloom::INITIAL_BLOOM_INTENSITY { 0.25f }; +const float Bloom::INITIAL_BLOOM_THRESHOLD { 0.7f }; +const float Bloom::INITIAL_BLOOM_SIZE { 0.9f }; \ No newline at end of file diff --git a/libraries/graphics/src/graphics/Bloom.h b/libraries/graphics/src/graphics/Bloom.h new file mode 100644 index 0000000000..02362ce25d --- /dev/null +++ b/libraries/graphics/src/graphics/Bloom.h @@ -0,0 +1,44 @@ +// +// Bloom.h +// libraries/graphics/src/graphics +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_model_Bloom_h +#define hifi_model_Bloom_h + +#include + +namespace graphics { + class Bloom { + public: + // Initial values + static const float INITIAL_BLOOM_INTENSITY; + static const float INITIAL_BLOOM_THRESHOLD; + static const float INITIAL_BLOOM_SIZE; + + Bloom() {} + + void setBloomIntensity(const float bloomIntensity) { _bloomIntensity = bloomIntensity; } + void setBloomThreshold(const float bloomThreshold) { _bloomThreshold = bloomThreshold; } + void setBloomSize(const float bloomSize) { _bloomSize = bloomSize; } + void setBloomActive(const bool isBloomActive) { _isBloomActive = isBloomActive; } + + float getBloomIntensity() { return _bloomIntensity; } + float getBloomThreshold() { return _bloomThreshold; } + float getBloomSize() { return _bloomSize; } + bool getBloomActive() { return _isBloomActive; } + + private: + bool _isBloomActive { false }; + float _bloomIntensity { INITIAL_BLOOM_INTENSITY }; + float _bloomThreshold {INITIAL_BLOOM_THRESHOLD }; + float _bloomSize { INITIAL_BLOOM_SIZE }; + }; + using BloomPointer = std::shared_ptr; +} +#endif // hifi_model_Bloom_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 82e9820509..ae165af5d1 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::ParticleSpin); + return static_cast(EntityVersion::BloomEffect); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 64c5bfe534..bfc92b9bde 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -240,7 +240,8 @@ enum class EntityVersion : PacketVersion { CollisionMask16Bytes, YieldSimulationOwnership, ParticleEntityFix, - ParticleSpin + ParticleSpin, + BloomEffect }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 2a9a9b3850..91ba95f3af 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -25,11 +25,7 @@ BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) { _parameters.edit()._sampleCount = downsamplingFactor; } -void BloomThreshold::configure(const Config& config) { - if (_parameters.get()._threshold != config.threshold) { - _parameters.edit()._threshold = config.threshold; - } -} +void BloomThreshold::configure(const Config& config) {} void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); @@ -39,6 +35,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons const auto frameTransform = inputs.get0(); const auto inputFrameBuffer = inputs.get1(); + const auto bloom = inputs.get2(); assert(inputFrameBuffer->hasColor()); @@ -68,6 +65,28 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + if (!bloom || !bloom->getBloomActive()) { + outputs = _outputBuffer; + return; + } + + _parameters.edit()._threshold = bloom->getBloomThreshold(); + + //{ + // std::string blurName { "BloomBlurN" }; + // auto sigma = 0.5f + bloom->getBloomSize() * 3.5f; + // auto task = static_cast(this); + + // for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { + // blurName.back() = '0' + i; + // auto blurJobIt = task->editJob(blurName); + // assert(blurJobIt != task->_jobs.end()); + // auto& gaussianBlur = blurJobIt->edit(); + // auto gaussianBlurParams = gaussianBlur.getParameters(); + // gaussianBlurParams->setFilterGaussianTaps(9, sigma); + // } + //} + gpu::doInBatch("BloomThreshold::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -90,16 +109,7 @@ BloomApply::BloomApply() { } -void BloomApply::configure(const Config& config) { - const auto newIntensity = config.intensity / 3.0f; - - if (_parameters.get()._intensities.x != newIntensity) { - auto& parameters = _parameters.edit(); - parameters._intensities.x = newIntensity; - parameters._intensities.y = newIntensity; - parameters._intensities.z = newIntensity; - } -} +void BloomApply::configure(const Config& config) {} void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); @@ -123,8 +133,19 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In const auto blur0FB = inputs.get1(); const auto blur1FB = inputs.get2(); const auto blur2FB = inputs.get3(); + const auto bloom = inputs.get4(); const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y }; + if (!bloom || !bloom->getBloomActive()) { + return; + } + + const auto newIntensity = bloom->getBloomIntensity() / 3.0f; + auto& parameters = _parameters.edit(); + parameters._intensities.x = newIntensity; + parameters._intensities.y = newIntensity; + parameters._intensities.z = newIntensity; + gpu::doInBatch("BloomApply::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -266,43 +287,9 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In }); } -void BloomConfig::setIntensity(float value) { - auto task = static_cast(_task); - auto blurJobIt = task->editJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - blurJobIt->getConfiguration()->setProperty("intensity", value); -} +BloomEffect::BloomEffect() {} -float BloomConfig::getIntensity() const { - auto task = static_cast(_task); - auto blurJobIt = task->getJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - return blurJobIt->getConfiguration()->property("intensity").toFloat(); -} - -void BloomConfig::setSize(float value) { - std::string blurName{ "BloomBlurN" }; - auto sigma = 0.5f+value*3.5f; - auto task = static_cast(_task); - - for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { - blurName.back() = '0' + i; - auto blurJobIt = task->editJob(blurName); - assert(blurJobIt != task->_jobs.end()); - auto& gaussianBlur = blurJobIt->edit(); - auto gaussianBlurParams = gaussianBlur.getParameters(); - gaussianBlurParams->setFilterGaussianTaps(9, sigma); - } - auto blurJobIt = task->getJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - blurJobIt->getConfiguration()->setProperty("sigma", sigma); -} - -Bloom::Bloom() { - -} - -void Bloom::configure(const Config& config) { +void BloomEffect::configure(const Config& config) { std::string blurName{ "BloomBlurN" }; for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { @@ -312,7 +299,7 @@ void Bloom::configure(const Config& config) { } } -void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { +void BloomEffect::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { // Start by computing threshold of color buffer input at quarter resolution const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs, 4U); @@ -325,7 +312,7 @@ void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying const auto& frameBuffer = input[1]; // Mix all blur levels at quarter resolution - const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying(); + const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2, input.get2()).asVarying(); task.addJob("BloomApply", applyInput); // And then blend result in additive manner on top of final color buffer const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying(); diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 04cb4a9474..150a134dac 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -14,42 +14,21 @@ #include +#include "graphics/Bloom.h" + #include "DeferredFrameTransform.h" class BloomConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) - Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) - -public: - - BloomConfig() : render::Task::Config(false) {} - - float size{ 0.7f }; - - void setIntensity(float value); - float getIntensity() const; - void setSize(float value); - -signals: - void dirty(); }; class BloomThresholdConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) - -public: - - float threshold{ 0.9f }; - -signals: - void dirty(); }; class BloomThreshold { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using Outputs = gpu::FramebufferPointer; using Config = BloomThresholdConfig; using JobModel = render::Job::ModelIO; @@ -71,21 +50,11 @@ private: class BloomApplyConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) - Q_PROPERTY(float sigma MEMBER sigma NOTIFY dirty) - -public: - - float intensity{ 0.25f }; - float sigma{ 1.0f }; - -signals: - void dirty(); }; class BloomApply { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = BloomApplyConfig; using JobModel = render::Job::ModelI; @@ -118,7 +87,7 @@ private: class DebugBloomConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) + Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) public: @@ -155,13 +124,13 @@ private: DebugBloomConfig::Mode _mode; }; -class Bloom { +class BloomEffect { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using Config = BloomConfig; - using JobModel = render::Task::ModelI; + using JobModel = render::Task::ModelI; - Bloom(); + BloomEffect(); void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); diff --git a/libraries/render-utils/src/BloomStage.cpp b/libraries/render-utils/src/BloomStage.cpp new file mode 100644 index 0000000000..9a1c8ee183 --- /dev/null +++ b/libraries/render-utils/src/BloomStage.cpp @@ -0,0 +1,80 @@ +// +// BloomStage.cpp +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomStage.h" + +#include "DeferredLightingEffect.h" + +#include + +std::string BloomStage::_stageName { "BLOOM_STAGE"}; +const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +FetchBloomStage::FetchBloomStage() { + _bloom = std::make_shared(); +} + +void FetchBloomStage::configure(const Config& config) { + _bloom->setBloomIntensity(config.bloomIntensity); + _bloom->setBloomThreshold(config.bloomThreshold); + _bloom->setBloomSize(config.bloomSize); + _bloom->setBloomActive(config.isBloomActive); +} + +BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const { + auto found = _bloomMap.find(bloom); + if (found != _bloomMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +BloomStage::Index BloomStage::addBloom(const BloomPointer& bloom) { + auto found = _bloomMap.find(bloom); + if (found == _bloomMap.end()) { + auto bloomId = _blooms.newElement(bloom); + // Avoid failing to allocate a bloom, just pass + if (bloomId != INVALID_INDEX) { + // Insert the bloom and its index in the reverse map + _bloomMap.insert(BloomMap::value_type(bloom, bloomId)); + } + return bloomId; + } else { + return (*found).second; + } +} + +BloomStage::BloomPointer BloomStage::removeBloom(Index index) { + BloomPointer removed = _blooms.freeElement(index); + if (removed) { + _bloomMap.erase(removed); + } + return removed; +} + +BloomStageSetup::BloomStageSetup() {} + +void BloomStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(BloomStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(BloomStage::getName(), std::make_shared()); + } +} + +void FetchBloomStage::run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom) { + auto bloomStage = renderContext->_scene->getStage(); + assert(bloomStage); + + bloom = nullptr; + if (bloomStage->_currentFrame._blooms.size() != 0) { + auto bloomId = bloomStage->_currentFrame._blooms.front(); + bloom = bloomStage->getBloom(bloomId); + } +} diff --git a/libraries/render-utils/src/BloomStage.h b/libraries/render-utils/src/BloomStage.h new file mode 100644 index 0000000000..76829a19a6 --- /dev/null +++ b/libraries/render-utils/src/BloomStage.h @@ -0,0 +1,122 @@ +// +// BloomStage.h + +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_render_utils_BloomStage_h +#define hifi_render_utils_BloomStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Bloom stage to set up bloom-related rendering tasks +class BloomStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using BloomPointer = graphics::BloomPointer; + using Blooms = render::indexed_container::IndexedPointerVector; + using BloomMap = std::unordered_map; + + using BloomIndices = std::vector; + + Index findBloom(const BloomPointer& bloom) const; + Index addBloom(const BloomPointer& bloom); + + BloomPointer removeBloom(Index index); + + bool checkBloomId(Index index) const { return _blooms.checkIndex(index); } + + Index getNumBlooms() const { return _blooms.getNumElements(); } + Index getNumFreeBlooms() const { return _blooms.getNumFreeIndices(); } + Index getNumAllocatedBlooms() const { return _blooms.getNumAllocatedIndices(); } + + BloomPointer getBloom(Index bloomId) const { + return _blooms.get(bloomId); + } + + Blooms _blooms; + BloomMap _bloomMap; + + class Frame { + public: + Frame() {} + + void clear() { _blooms.clear(); } + + void pushBloom(BloomStage::Index index) { _blooms.emplace_back(index); } + + BloomStage::BloomIndices _blooms; + }; + + Frame _currentFrame; +}; +using BloomStagePointer = std::shared_ptr; + +class BloomStageSetup { +public: + using JobModel = render::Job::Model; + + BloomStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +class FetchBloomConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float bloomIntensity MEMBER bloomIntensity WRITE setBloomIntensity NOTIFY dirty); + Q_PROPERTY(float bloomThreshold MEMBER bloomThreshold WRITE setBloomThreshold NOTIFY dirty); + Q_PROPERTY(float bloomSize MEMBER bloomSize WRITE setBloomSize NOTIFY dirty); + Q_PROPERTY(bool isBloomActive MEMBER isBloomActive WRITE setBloomActive NOTIFY dirty); + +public: + FetchBloomConfig() : render::Job::Config() {} + + float bloomIntensity { graphics::Bloom::INITIAL_BLOOM_INTENSITY }; + float bloomThreshold { graphics::Bloom::INITIAL_BLOOM_THRESHOLD }; + float bloomSize { graphics::Bloom::INITIAL_BLOOM_SIZE }; + + bool isBloomActive { false }; + +public slots: + void setBloomIntensity(const float value) { bloomIntensity = value; emit dirty(); } + void setBloomThreshold(const float value) { bloomThreshold = value; emit dirty(); } + void setBloomSize(const float value) { bloomSize = value; emit dirty(); } + void setBloomActive(const bool active) { isBloomActive = active; emit dirty(); } + +signals: + void dirty(); +}; + +class FetchBloomStage { +public: + using Config = FetchBloomConfig; + using JobModel = render::Job::ModelO; + + FetchBloomStage(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom); + +private: + graphics::BloomPointer _bloom; +}; +#endif diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index 8f137cb280..b48168e376 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -163,6 +163,5 @@ public: private: graphics::HazePointer _haze; - gpu::PipelinePointer _hazePipeline; }; #endif diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 59c86acd19..b71d712a6e 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -45,6 +45,7 @@ #include "TextureCache.h" #include "ZoneRenderer.h" #include "FadeEffect.h" +#include "BloomStage.h" #include "RenderUtilsLogging.h" #include "AmbientOcclusionEffect.h" @@ -171,7 +172,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto velocityBufferOutputs = task.addJob("VelocityBuffer", velocityBufferInputs); const auto velocityBuffer = velocityBufferOutputs.getN(0); - // Clear Light, Haze and Skybox Stages and render zones from the general metas bucket + // Clear Light, Haze, Bloom, and Skybox Stages and render zones from the general metas bucket const auto zones = task.addJob("ZoneRenderer", metas); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. @@ -240,8 +241,9 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Antialiasing", antialiasingInputs); // Add bloom - const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying(); - task.addJob("Bloom", bloomInputs); + const auto bloomModel = task.addJob("BloomModel"); + const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomModel).asVarying(); + task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index e05f28ef0d..be61953073 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -14,6 +14,7 @@ #include "LightStage.h" #include "BackgroundStage.h" #include "HazeStage.h" +#include "BloomStage.h" #include #include #include "DeferredLightingEffect.h" @@ -22,6 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("LightStageSetup"); task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); + task.addJob("BloomStageSetup"); task.addJob("TransitionStageSetup"); task.addJob("HighlightStageSetup"); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 3299b0c41c..d85818a21a 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -21,11 +21,12 @@ #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" - #include "render-utils/ShaderConstants.h" #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" +#include "BloomStage.h" + namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer; @@ -63,7 +64,7 @@ void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& oupu } void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) { - // Grab light, background and haze stages and clear them + // Grab light, background, haze, and bloom stages and clear them auto lightStage = context->_scene->getStage(); assert(lightStage); lightStage->_currentFrame.clear(); @@ -76,6 +77,10 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) assert(hazeStage); hazeStage->_currentFrame.clear(); + auto bloomStage = context->_scene->getStage(); + assert(bloomStage); + bloomStage->_currentFrame.clear(); + // call render over the zones to grab their components in the correct order first... render::renderItems(context, inputs); @@ -84,6 +89,7 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight()); backgroundStage->_currentFrame.pushBackground(0); hazeStage->_currentFrame.pushHaze(0); + bloomStage->_currentFrame.pushBloom(0); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 9614f8b8fe..744150253d 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -678,6 +678,52 @@ +
+ + Bloom + +
+ Inherit + Off + On +
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index d2cea2d394..43ea6bf22f 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -839,7 +839,7 @@ function loaded() { var elZoneHazeModeInherit = document.getElementById("property-zone-haze-mode-inherit"); var elZoneHazeModeDisabled = document.getElementById("property-zone-haze-mode-disabled"); var elZoneHazeModeEnabled = document.getElementById("property-zone-haze-mode-enabled"); - + var elZoneHazeRange = document.getElementById("property-zone-haze-range"); var elZoneHazeColor = document.getElementById("property-zone-haze-color"); var elZoneHazeColorRed = document.getElementById("property-zone-haze-color-red"); @@ -858,6 +858,15 @@ function loaded() { var elZoneHazeBackgroundBlend = document.getElementById("property-zone-haze-background-blend"); + // Bloom + var elZoneBloomModeInherit = document.getElementById("property-zone-bloom-mode-inherit"); + var elZoneBloomModeDisabled = document.getElementById("property-zone-bloom-mode-disabled"); + var elZoneBloomModeEnabled = document.getElementById("property-zone-bloom-mode-enabled"); + + var elZoneBloomIntensity = document.getElementById("property-zone-bloom-intensity"); + var elZoneBloomThreshold = document.getElementById("property-zone-bloom-threshold"); + var elZoneBloomSize = document.getElementById("property-zone-bloom-size"); + var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color"); var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red"); var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); @@ -914,19 +923,19 @@ function loaded() { deleteJSONMaterialEditor(); } } - + elTypeIcon.style.display = "none"; elType.innerHTML = "No selection"; elPropertiesList.className = ''; - + elID.value = ""; elName.value = ""; elLocked.checked = false; elVisible.checked = false; - + elParentID.value = ""; elParentJointIndex.value = ""; - + elColorRed.value = ""; elColorGreen.value = ""; elColorBlue.value = ""; @@ -935,15 +944,15 @@ function loaded() { elPositionX.value = ""; elPositionY.value = ""; elPositionZ.value = ""; - + elRotationX.value = ""; elRotationY.value = ""; elRotationZ.value = ""; - + elDimensionsX.value = ""; elDimensionsY.value = ""; elDimensionsZ.value = ""; - + elRegistrationX.value = ""; elRegistrationY.value = ""; elRegistrationZ.value = ""; @@ -965,14 +974,14 @@ function loaded() { elAccelerationX.value = ""; elAccelerationY.value = ""; elAccelerationZ.value = ""; - + elRestitution.value = ""; elFriction.value = ""; elDensity.value = ""; - + elCollisionless.checked = false; elDynamic.checked = false; - + elCollideStatic.checked = false; elCollideKinematic.checked = false; elCollideDynamic.checked = false; @@ -989,27 +998,27 @@ function loaded() { elCloneableGroup.style.display = "none"; elCloneableLimit.value = ""; elCloneableLifetime.value = ""; - - showElements(document.getElementsByClassName('can-cast-shadow-section'), true); + + showElements(document.getElementsByClassName('can-cast-shadow-section'), true); elCanCastShadow.checked = false; - + elCollisionSoundURL.value = ""; elLifetime.value = ""; elScriptURL.value = ""; elServerScripts.value = ""; elHyperlinkHref.value = ""; elDescription.value = ""; - + deleteJSONEditor(); elUserData.value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); - + // Shape Properties elShape.value = "Cube"; setDropdownText(elShape); - + // Light Properties elLightSpotLight.checked = false; elLightColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; @@ -1020,7 +1029,7 @@ function loaded() { elLightFalloffRadius.value = ""; elLightExponent.value = ""; elLightCutoff.value = ""; - + // Model Properties elModelURL.value = ""; elCompoundShapeURL.value = ""; @@ -1037,7 +1046,7 @@ function loaded() { elModelAnimationAllowTranslation.checked = false; elModelTextures.value = ""; elModelOriginalTextures.value = ""; - + // Zone Properties elZoneFlyingAllowed.checked = false; elZoneGhostingAllowed.checked = false; @@ -1067,6 +1076,9 @@ function loaded() { elZoneHazeAltitudeEffect.checked = false; elZoneHazeBaseRef.value = ""; elZoneHazeCeiling.value = ""; + elZoneBloomIntensity.value = ""; + elZoneBloomThreshold.value = ""; + elZoneBloomSize.value = ""; elZoneSkyboxColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; elZoneSkyboxColorRed.value = ""; elZoneSkyboxColorGreen.value = ""; @@ -1076,7 +1088,8 @@ function loaded() { showElements(document.getElementsByClassName('skybox-section'), true); showElements(document.getElementsByClassName('ambient-section'), true); showElements(document.getElementsByClassName('haze-section'), true); - + showElements(document.getElementsByClassName('bloom-section'), true); + // Text Properties elTextText.value = ""; elTextLineHeight.value = ""; @@ -1089,14 +1102,14 @@ function loaded() { elTextBackgroundColorRed.value = ""; elTextBackgroundColorGreen.value = ""; elTextBackgroundColorBlue.value = ""; - + // Image Properties elImageURL.value = ""; - + // Web Properties elWebSourceURL.value = ""; elWebDPI.value = ""; - + // Material Properties elMaterialURL.value = ""; elParentMaterialNameNumber.value = ""; @@ -1107,13 +1120,13 @@ function loaded() { elMaterialMappingScaleX.value = ""; elMaterialMappingScaleY.value = ""; elMaterialMappingRot.value = ""; - + deleteJSONMaterialEditor(); elMaterialData.value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); - + disableProperties(); } else if (data.selections.length > 1) { deleteJSONEditor(); @@ -1250,7 +1263,7 @@ function loaded() { elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; elCloneableLimit.value = properties.cloneLimit; elCloneableLifetime.value = properties.cloneLifetime; - + var grabbablesSet = false; var parsedUserData = {}; try { @@ -1476,6 +1489,14 @@ function loaded() { elZoneHazeBaseRef.value = properties.haze.hazeBaseRef.toFixed(0); elZoneHazeCeiling.value = properties.haze.hazeCeiling.toFixed(0); + elZoneBloomModeInherit.checked = (properties.bloomMode === 'inherit'); + elZoneBloomModeDisabled.checked = (properties.bloomMode === 'disabled'); + elZoneBloomModeEnabled.checked = (properties.bloomMode === 'enabled'); + + elZoneBloomIntensity.value = properties.bloom.bloomIntensity.toFixed(2); + elZoneBloomThreshold.value = properties.bloom.bloomThreshold.toFixed(2); + elZoneBloomSize.value = properties.bloom.bloomSize.toFixed(2); + elShapeType.value = properties.shapeType; elCompoundShapeURL.value = properties.compoundShapeURL; @@ -1502,6 +1523,9 @@ function loaded() { showElements(document.getElementsByClassName('haze-section'), elZoneHazeModeEnabled.checked); + + showElements(document.getElementsByClassName('bloom-section'), + elZoneBloomModeEnabled.checked); } else if (properties.type === "PolyVox") { elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); @@ -2067,6 +2091,18 @@ function loaded() { elZoneHazeBackgroundBlend.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBackgroundBlend')); + // Bloom + var bloomModeChanged = createZoneComponentModeChangedFunction('bloomMode', + elZoneBloomModeInherit, elZoneBloomModeDisabled, elZoneBloomModeEnabled); + + elZoneBloomModeInherit.addEventListener('change', bloomModeChanged); + elZoneBloomModeDisabled.addEventListener('change', bloomModeChanged); + elZoneBloomModeEnabled.addEventListener('change', bloomModeChanged); + + elZoneBloomIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomIntensity')); + elZoneBloomThreshold.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomThreshold')); + elZoneBloomSize.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomSize')); + var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox', 'color', elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue); elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction); From fb75b42059a6fc318f23579f9b9aebfaf8265459 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Tue, 7 Aug 2018 18:18:58 -0700 Subject: [PATCH 010/207] working on enabling bloom --- .../src/RenderableZoneEntityItem.cpp | 50 ++++++ .../src/RenderableZoneEntityItem.h | 31 ++-- libraries/entities/src/BloomPropertyGroup.cpp | 159 +++++++++++++++++ libraries/entities/src/BloomPropertyGroup.h | 94 ++++++++++ .../entities/src/EntityItemProperties.cpp | 51 +++++- libraries/entities/src/EntityItemProperties.h | 4 + libraries/entities/src/EntityPropertyFlags.h | 5 + libraries/entities/src/ZoneEntityItem.cpp | 165 ++++++++++-------- libraries/entities/src/ZoneEntityItem.h | 14 +- libraries/graphics/src/graphics/Bloom.cpp | 18 ++ libraries/graphics/src/graphics/Bloom.h | 44 +++++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- libraries/render-utils/src/BloomEffect.cpp | 93 +++++----- libraries/render-utils/src/BloomEffect.h | 49 +----- libraries/render-utils/src/BloomStage.cpp | 80 +++++++++ libraries/render-utils/src/BloomStage.h | 122 +++++++++++++ libraries/render-utils/src/HazeStage.h | 1 - .../render-utils/src/RenderDeferredTask.cpp | 9 +- .../render-utils/src/UpdateSceneTask.cpp | 2 + libraries/render-utils/src/ZoneRenderer.cpp | 10 +- scripts/system/html/entityProperties.html | 46 +++++ scripts/system/html/js/entityProperties.js | 88 +++++++--- 23 files changed, 919 insertions(+), 221 deletions(-) create mode 100644 libraries/entities/src/BloomPropertyGroup.cpp create mode 100644 libraries/entities/src/BloomPropertyGroup.h create mode 100644 libraries/graphics/src/graphics/Bloom.cpp create mode 100644 libraries/graphics/src/graphics/Bloom.h create mode 100644 libraries/render-utils/src/BloomStage.cpp create mode 100644 libraries/render-utils/src/BloomStage.h diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp index c5035431f6..928d5ca6a3 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.cpp @@ -64,6 +64,13 @@ void ZoneEntityRenderer::onRemoveFromSceneTyped(const TypedEntityPointer& entity _hazeIndex = INVALID_INDEX; } } + + if (_bloomStage) { + if (!BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomStage->removeBloom(_bloomIndex); + _bloomIndex = INVALID_INDEX; + } + } } void ZoneEntityRenderer::doRender(RenderArgs* args) { @@ -112,6 +119,11 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { assert(_hazeStage); } + if (!_bloomStage) { + _bloomStage = args->_scene->getStage(); + assert(_bloomStage); + } + { // Sun // Need an update ? if (_needSunUpdate) { @@ -161,6 +173,15 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { } } + { + if (_needBloomUpdate) { + if (BloomStage::isIndexInvalid(_bloomIndex)) { + _bloomIndex = _bloomStage->addBloom(_bloom); + } + _needBloomUpdate = false; + } + } + if (_visible) { // Finally, push the lights visible in the frame // @@ -190,6 +211,12 @@ void ZoneEntityRenderer::doRender(RenderArgs* args) { if (_hazeMode != COMPONENT_MODE_INHERIT) { _hazeStage->_currentFrame.pushHaze(_hazeIndex); } + + if (_bloomMode == COMPONENT_MODE_DISABLED) { + _bloomStage->_currentFrame.pushBloom(INVALID_INDEX); + } else if (_ambientLightMode == COMPONENT_MODE_ENABLED) { + _bloomStage->_currentFrame.pushBloom(_bloomIndex); + } } } @@ -211,6 +238,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen bool ambientLightChanged = entity->ambientLightPropertiesChanged(); bool skyboxChanged = entity->skyboxPropertiesChanged(); bool hazeChanged = entity->hazePropertiesChanged(); + bool bloomChanged = entity->bloomPropertiesChanged(); entity->resetRenderingPropertiesChanged(); _lastPosition = entity->getWorldPosition(); @@ -221,6 +249,7 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen _ambientLightProperties = entity->getAmbientLightProperties(); _skyboxProperties = entity->getSkyboxProperties(); _hazeProperties = entity->getHazeProperties(); + _bloomProperties = entity->getBloomProperties(); #if 0 if (_lastShapeURL != _typedEntity->getCompoundShapeURL()) { @@ -258,6 +287,10 @@ void ZoneEntityRenderer::doRenderUpdateSynchronousTyped(const ScenePointer& scen if (hazeChanged) { updateHazeFromEntity(entity); } + + if (bloomChanged) { + updateBloomFromEntity(entity); + } } void ZoneEntityRenderer::doRenderUpdateAsynchronousTyped(const TypedEntityPointer& entity) { @@ -276,6 +309,7 @@ bool ZoneEntityRenderer::needsRenderUpdateFromTypedEntity(const TypedEntityPoint if (entity->keyLightPropertiesChanged() || entity->ambientLightPropertiesChanged() || entity->hazePropertiesChanged() || + entity->bloomPropertiesChanged() || entity->skyboxPropertiesChanged()) { return true; @@ -388,6 +422,18 @@ void ZoneEntityRenderer::updateHazeFromEntity(const TypedEntityPointer& entity) haze->setTransform(entity->getTransform().getMatrix()); } +void ZoneEntityRenderer::updateBloomFromEntity(const TypedEntityPointer& entity) { + setBloomMode((ComponentMode)entity->getBloomMode()); + + const auto& bloom = editBloom(); + + const uint32_t bloomMode = entity->getBloomMode(); + bloom->setBloomActive(bloomMode == COMPONENT_MODE_ENABLED); + bloom->setBloomIntensity(_bloomProperties.getBloomIntensity()); + bloom->setBloomThreshold(_bloomProperties.getBloomThreshold()); + bloom->setBloomSize(_bloomProperties.getBloomSize()); +} + void ZoneEntityRenderer::updateKeyBackgroundFromEntity(const TypedEntityPointer& entity) { setSkyboxMode((ComponentMode)entity->getSkyboxMode()); @@ -510,6 +556,10 @@ void ZoneEntityRenderer::setSkyboxMode(ComponentMode mode) { _skyboxMode = mode; } +void ZoneEntityRenderer::setBloomMode(ComponentMode mode) { + _bloomMode = mode; +} + void ZoneEntityRenderer::setSkyboxColor(const glm::vec3& color) { editSkybox()->setColor(color); } diff --git a/libraries/entities-renderer/src/RenderableZoneEntityItem.h b/libraries/entities-renderer/src/RenderableZoneEntityItem.h index c48679e5d4..3e2690e1bd 100644 --- a/libraries/entities-renderer/src/RenderableZoneEntityItem.h +++ b/libraries/entities-renderer/src/RenderableZoneEntityItem.h @@ -1,7 +1,6 @@ // // RenderableZoneEntityItem.h // -// // Created by Clement on 4/22/15. // Copyright 2015 High Fidelity, Inc. // @@ -15,10 +14,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include "RenderableEntityItem.h" #include @@ -50,6 +51,7 @@ private: void updateAmbientLightFromEntity(const TypedEntityPointer& entity); void updateHazeFromEntity(const TypedEntityPointer& entity); void updateKeyBackgroundFromEntity(const TypedEntityPointer& entity); + void updateBloomFromEntity(const TypedEntityPointer& entity); void updateAmbientMap(); void updateSkyboxMap(); void setAmbientURL(const QString& ambientUrl); @@ -59,6 +61,7 @@ private: void setKeyLightMode(ComponentMode mode); void setAmbientLightMode(ComponentMode mode); void setSkyboxMode(ComponentMode mode); + void setBloomMode(ComponentMode mode); void setSkyboxColor(const glm::vec3& color); void setProceduralUserData(const QString& userData); @@ -68,6 +71,7 @@ private: graphics::SunSkyStagePointer editBackground() { _needBackgroundUpdate = true; return _background; } graphics::SkyboxPointer editSkybox() { return editBackground()->getSkybox(); } graphics::HazePointer editHaze() { _needHazeUpdate = true; return _haze; } + graphics::BloomPointer editBloom() { _needBloomUpdate = true; return _bloom; } glm::vec3 _lastPosition; glm::vec3 _lastDimensions; @@ -82,36 +86,43 @@ private: #endif LightStagePointer _stage; - const graphics::LightPointer _sunLight{ std::make_shared() }; - const graphics::LightPointer _ambientLight{ std::make_shared() }; - const graphics::SunSkyStagePointer _background{ std::make_shared() }; - const graphics::HazePointer _haze{ std::make_shared() }; + const graphics::LightPointer _sunLight { std::make_shared() }; + const graphics::LightPointer _ambientLight { std::make_shared() }; + const graphics::SunSkyStagePointer _background { std::make_shared() }; + const graphics::HazePointer _haze { std::make_shared() }; + const graphics::BloomPointer _bloom { std::make_shared() }; ComponentMode _keyLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _ambientLightMode { COMPONENT_MODE_INHERIT }; ComponentMode _skyboxMode { COMPONENT_MODE_INHERIT }; ComponentMode _hazeMode { COMPONENT_MODE_INHERIT }; + ComponentMode _bloomMode { COMPONENT_MODE_INHERIT }; - indexed_container::Index _sunIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _shadowIndex{ LightStage::INVALID_INDEX }; - indexed_container::Index _ambientIndex{ LightStage::INVALID_INDEX }; + indexed_container::Index _sunIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _shadowIndex { LightStage::INVALID_INDEX }; + indexed_container::Index _ambientIndex { LightStage::INVALID_INDEX }; BackgroundStagePointer _backgroundStage; - BackgroundStage::Index _backgroundIndex{ BackgroundStage::INVALID_INDEX }; + BackgroundStage::Index _backgroundIndex { BackgroundStage::INVALID_INDEX }; HazeStagePointer _hazeStage; - HazeStage::Index _hazeIndex{ HazeStage::INVALID_INDEX }; + HazeStage::Index _hazeIndex { HazeStage::INVALID_INDEX }; + + BloomStagePointer _bloomStage; + BloomStage::Index _bloomIndex { BloomStage::INVALID_INDEX }; bool _needUpdate{ true }; bool _needSunUpdate{ true }; bool _needAmbientUpdate{ true }; bool _needBackgroundUpdate{ true }; bool _needHazeUpdate{ true }; + bool _needBloomUpdate { true }; KeyLightPropertyGroup _keyLightProperties; AmbientLightPropertyGroup _ambientLightProperties; HazePropertyGroup _hazeProperties; SkyboxPropertyGroup _skyboxProperties; + BloomPropertyGroup _bloomProperties; // More attributes used for rendering: QString _ambientTextureURL; diff --git a/libraries/entities/src/BloomPropertyGroup.cpp b/libraries/entities/src/BloomPropertyGroup.cpp new file mode 100644 index 0000000000..2c4d46ab35 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.cpp @@ -0,0 +1,159 @@ +// +// BloomPropertyGroup.cpp +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomPropertyGroup.h" + +#include + +#include "EntityItemProperties.h" +#include "EntityItemPropertiesMacros.h" + +void BloomPropertyGroup::copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, QScriptEngine* engine, bool skipDefaults, EntityItemProperties& defaultEntityProperties) const { + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + COPY_GROUP_PROPERTY_TO_QSCRIPTVALUE(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); +} + +void BloomPropertyGroup::copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) { + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomIntensity, float, setBloomIntensity); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomThreshold, float, setBloomThreshold); + COPY_GROUP_PROPERTY_FROM_QSCRIPTVALUE(bloom, bloomSize, float, setBloomSize); +} + +void BloomPropertyGroup::merge(const BloomPropertyGroup& other) { + COPY_PROPERTY_IF_CHANGED(bloomIntensity); + COPY_PROPERTY_IF_CHANGED(bloomThreshold); + COPY_PROPERTY_IF_CHANGED(bloomSize); +} + +void BloomPropertyGroup::debugDump() const { + qCDebug(entities) << " BloomPropertyGroup: ---------------------------------------------"; + qCDebug(entities) << " _bloomIntensity:" << _bloomIntensity; + qCDebug(entities) << " _bloomThreshold:" << _bloomThreshold; + qCDebug(entities) << " _bloomSize:" << _bloomSize; +} + +void BloomPropertyGroup::listChangedProperties(QList& out) { + if (bloomIntensityChanged()) { + out << "bloom-bloomIntensity"; + } + if (bloomThresholdChanged()) { + out << "bloom-bloomThreshold"; + } + if (bloomSizeChanged()) { + out << "bloom-bloomSize"; + } +} + +bool BloomPropertyGroup::appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); + + return true; +} + +bool BloomPropertyGroup::decodeFromEditPacket(EntityPropertyFlags& propertyFlags, const unsigned char*& dataAt , int& processedBytes) { + int bytesRead = 0; + bool overwriteLocalData = true; + bool somethingChanged = false; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_INTENSITY, BloomIntensity); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_THRESHOLD, BloomThreshold); + DECODE_GROUP_PROPERTY_HAS_CHANGED(PROP_BLOOM_SIZE, BloomSize); + + processedBytes += bytesRead; + + Q_UNUSED(somethingChanged); + + return true; +} + +void BloomPropertyGroup::markAllChanged() { + _bloomIntensityChanged = true; + _bloomThresholdChanged = true; + _bloomSizeChanged = true; +} + +EntityPropertyFlags BloomPropertyGroup::getChangedProperties() const { + EntityPropertyFlags changedProperties; + + CHECK_PROPERTY_CHANGE(PROP_BLOOM_INTENSITY, bloomIntensity); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_THRESHOLD, bloomThreshold); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_SIZE, bloomSize); + + return changedProperties; +} + +void BloomPropertyGroup::getProperties(EntityItemProperties& properties) const { + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomIntensity, getBloomIntensity); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomThreshold, getBloomThreshold); + COPY_ENTITY_GROUP_PROPERTY_TO_PROPERTIES(Bloom, BloomSize, getBloomSize); +} + +bool BloomPropertyGroup::setProperties(const EntityItemProperties& properties) { + bool somethingChanged = false; + + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomIntensity, bloomIntensity, setBloomIntensity); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomThreshold, bloomThreshold, setBloomThreshold); + SET_ENTITY_GROUP_PROPERTY_FROM_PROPERTIES(Bloom, BloomSize, bloomSize, setBloomSize); + + return somethingChanged; +} + +EntityPropertyFlags BloomPropertyGroup::getEntityProperties(EncodeBitstreamParams& params) const { + EntityPropertyFlags requestedProperties; + + requestedProperties += PROP_BLOOM_INTENSITY; + requestedProperties += PROP_BLOOM_THRESHOLD; + requestedProperties += PROP_BLOOM_SIZE; + + return requestedProperties; +} + +void BloomPropertyGroup::appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const { + bool successPropertyFits = true; + + APPEND_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, getBloomIntensity()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, getBloomThreshold()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_SIZE, getBloomSize()); +} + +int BloomPropertyGroup::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) { + int bytesRead = 0; + const unsigned char* dataAt = data; + + READ_ENTITY_PROPERTY(PROP_BLOOM_INTENSITY, float, setBloomIntensity); + READ_ENTITY_PROPERTY(PROP_BLOOM_THRESHOLD, float, setBloomThreshold); + READ_ENTITY_PROPERTY(PROP_BLOOM_SIZE, float, setBloomSize); + + return bytesRead; +} diff --git a/libraries/entities/src/BloomPropertyGroup.h b/libraries/entities/src/BloomPropertyGroup.h new file mode 100644 index 0000000000..a1f9b6d748 --- /dev/null +++ b/libraries/entities/src/BloomPropertyGroup.h @@ -0,0 +1,94 @@ +// +// BloomPropertyGroup.h +// libraries/entities/src +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_BloomPropertyGroup_h +#define hifi_BloomPropertyGroup_h + +#include +#include + +#include + +#include "PropertyGroup.h" +#include "EntityItemPropertiesMacros.h" + +class EntityItemProperties; +class EncodeBitstreamParams; +class OctreePacketData; +class EntityTreeElementExtraEncodeData; +class ReadBitstreamToTreeParams; + +static const float INITIAL_BLOOM_INTENSITY { 0.25f }; +static const float INITIAL_BLOOM_THRESHOLD { 0.7f }; +static const float INITIAL_BLOOM_SIZE { 0.9f }; + +/**jsdoc + * Bloom is defined by the following properties. + * @typedef {object} Entities.Bloom + * + * @property {number} bloomIntensity=0.25 - The intensity of the bloom effect. + * @property {number} bloomThreshold=0.7 - The threshold for the bloom effect. + * @property {number} bloomSize=0.9 - The size of the bloom effect. + */ +class BloomPropertyGroup : public PropertyGroup { +public: + // EntityItemProperty related helpers + virtual void copyToScriptValue(const EntityPropertyFlags& desiredProperties, QScriptValue& properties, + QScriptEngine* engine, bool skipDefaults, + EntityItemProperties& defaultEntityProperties) const override; + virtual void copyFromScriptValue(const QScriptValue& object, bool& _defaultSettings) override; + + void merge(const BloomPropertyGroup& other); + + virtual void debugDump() const override; + virtual void listChangedProperties(QList& out) override; + + virtual bool appendToEditPacket(OctreePacketData* packetData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual bool decodeFromEditPacket(EntityPropertyFlags& propertyFlags, + const unsigned char*& dataAt, int& processedBytes) override; + virtual void markAllChanged() override; + virtual EntityPropertyFlags getChangedProperties() const override; + + // EntityItem related helpers + // methods for getting/setting all properties of an entity + virtual void getProperties(EntityItemProperties& propertiesOut) const override; + + /// returns true if something changed + virtual bool setProperties(const EntityItemProperties& properties) override; + + virtual EntityPropertyFlags getEntityProperties(EncodeBitstreamParams& params) const override; + + virtual void appendSubclassData(OctreePacketData* packetData, EncodeBitstreamParams& params, + EntityTreeElementExtraEncodeDataPointer entityTreeElementExtraEncodeData, + EntityPropertyFlags& requestedProperties, + EntityPropertyFlags& propertyFlags, + EntityPropertyFlags& propertiesDidntFit, + int& propertyCount, + OctreeElement::AppendState& appendState) const override; + + virtual int readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesLeftToRead, + ReadBitstreamToTreeParams& args, + EntityPropertyFlags& propertyFlags, bool overwriteLocalData, + bool& somethingChanged) override; + + DEFINE_PROPERTY(PROP_BLOOM_INTENSITY, BloomIntensity, bloomIntensity, float, INITIAL_BLOOM_INTENSITY); + DEFINE_PROPERTY(PROP_BLOOM_THRESHOLD, BloomThreshold, bloomThreshold, float, INITIAL_BLOOM_THRESHOLD); + DEFINE_PROPERTY(PROP_BLOOM_SIZE, BloomSize, bloomSize, float, INITIAL_BLOOM_SIZE); + +}; + +#endif // hifi_BloomPropertyGroup_h diff --git a/libraries/entities/src/EntityItemProperties.cpp b/libraries/entities/src/EntityItemProperties.cpp index 1f9bf2eb18..68d0e6aed4 100644 --- a/libraries/entities/src/EntityItemProperties.cpp +++ b/libraries/entities/src/EntityItemProperties.cpp @@ -37,6 +37,7 @@ AnimationPropertyGroup EntityItemProperties::_staticAnimation; SkyboxPropertyGroup EntityItemProperties::_staticSkybox; HazePropertyGroup EntityItemProperties::_staticHaze; +BloomPropertyGroup EntityItemProperties::_staticBloom; KeyLightPropertyGroup EntityItemProperties::_staticKeyLight; AmbientLightPropertyGroup EntityItemProperties::_staticAmbientLight; @@ -84,6 +85,7 @@ void EntityItemProperties::debugDump() const { getHaze().debugDump(); getKeyLight().debugDump(); getAmbientLight().debugDump(); + getBloom().debugDump(); qCDebug(entities) << " changed properties..."; EntityPropertyFlags props = getChangedProperties(); @@ -211,6 +213,10 @@ QString EntityItemProperties::getHazeModeAsString() const { return getComponentModeAsString(_hazeMode); } +QString EntityItemProperties::getBloomModeAsString() const { + return getComponentModeAsString(_bloomMode); +} + QString EntityItemProperties::getComponentModeString(uint32_t mode) { // return "inherit" if mode is not valid if (mode < COMPONENT_MODE_ITEM_COUNT) { @@ -235,6 +241,15 @@ void EntityItemProperties::setHazeModeFromString(const QString& hazeMode) { } } +void EntityItemProperties::setBloomModeFromString(const QString& bloomMode) { + auto result = findComponent(bloomMode); + + if (result != COMPONENT_MODES.end()) { + _bloomMode = result->first; + _bloomModeChanged = true; + } +} + QString EntityItemProperties::getKeyLightModeAsString() const { return getComponentModeAsString(_keyLightMode); } @@ -394,6 +409,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { CHECK_PROPERTY_CHANGE(PROP_KEY_LIGHT_MODE, keyLightMode); CHECK_PROPERTY_CHANGE(PROP_AMBIENT_LIGHT_MODE, ambientLightMode); CHECK_PROPERTY_CHANGE(PROP_SKYBOX_MODE, skyboxMode); + CHECK_PROPERTY_CHANGE(PROP_BLOOM_MODE, bloomMode); CHECK_PROPERTY_CHANGE(PROP_SOURCE_URL, sourceUrl); CHECK_PROPERTY_CHANGE(PROP_VOXEL_VOLUME_SIZE, voxelVolumeSize); @@ -454,6 +470,7 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { changedProperties += _ambientLight.getChangedProperties(); changedProperties += _skybox.getChangedProperties(); changedProperties += _haze.getChangedProperties(); + changedProperties += _bloom.getChangedProperties(); return changedProperties; } @@ -1164,6 +1181,12 @@ EntityPropertyFlags EntityItemProperties::getChangedProperties() const { * "enabled": The haze properties of this zone are enabled, overriding the haze from any enclosing zone. * @property {Entities.Haze} haze - The haze properties of the zone. * + * @property {string} bloomMode="inherit" - Configures the bloom in the zone. Possible values:
+ * "inherit": The bloom from any enclosing zone continues into this zone.
+ * "disabled": The bloom from any enclosing zone and the bloom of this zone are disabled in this zone.
+ * "enabled": The bloom properties of this zone are enabled, overriding the bloom from any enclosing zone. + * @property {Entities.Bloom} bloom - The bloom properties of the zone. + * * @property {boolean} flyingAllowed=true - If true then visitors can fly in the zone; otherwise they cannot. * @property {boolean} ghostingAllowed=true - If true then visitors with avatar collisions turned off will not * collide with content in the zone; otherwise visitors will always collide with content in the zone. @@ -1382,6 +1405,9 @@ QScriptValue EntityItemProperties::copyToScriptValue(QScriptEngine* engine, bool COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_HAZE_MODE, hazeMode, getHazeModeAsString()); _haze.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_BLOOM_MODE, bloomMode, getBloomModeAsString()); + _bloom.copyToScriptValue(_desiredProperties, properties, engine, skipDefaults, defaultEntityProperties); + COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_KEY_LIGHT_MODE, keyLightMode, getKeyLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_AMBIENT_LIGHT_MODE, ambientLightMode, getAmbientLightModeAsString()); COPY_PROPERTY_TO_QSCRIPTVALUE_GETTER(PROP_SKYBOX_MODE, skyboxMode, getSkyboxModeAsString()); @@ -1630,6 +1656,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(keyLightMode, KeyLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(ambientLightMode, AmbientLightMode); COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(skyboxMode, SkyboxMode); + COPY_PROPERTY_FROM_QSCRIPTVALUE_ENUM(bloomMode, BloomMode); COPY_PROPERTY_FROM_QSCRIPTVALUE(sourceUrl, QString, setSourceUrl); COPY_PROPERTY_FROM_QSCRIPTVALUE(voxelVolumeSize, vec3, setVoxelVolumeSize); @@ -1662,6 +1689,7 @@ void EntityItemProperties::copyFromScriptValue(const QScriptValue& object, bool _ambientLight.copyFromScriptValue(object, _defaultSettings); _skybox.copyFromScriptValue(object, _defaultSettings); _haze.copyFromScriptValue(object, _defaultSettings); + _bloom.copyFromScriptValue(object, _defaultSettings); COPY_PROPERTY_FROM_QSCRIPTVALUE(xTextureURL, QString, setXTextureURL); COPY_PROPERTY_FROM_QSCRIPTVALUE(yTextureURL, QString, setYTextureURL); @@ -1803,6 +1831,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { COPY_PROPERTY_IF_CHANGED(keyLightMode); COPY_PROPERTY_IF_CHANGED(ambientLightMode); COPY_PROPERTY_IF_CHANGED(skyboxMode); + COPY_PROPERTY_IF_CHANGED(bloomMode); COPY_PROPERTY_IF_CHANGED(sourceUrl); COPY_PROPERTY_IF_CHANGED(voxelVolumeSize); @@ -1825,6 +1854,7 @@ void EntityItemProperties::merge(const EntityItemProperties& other) { _ambientLight.merge(other._ambientLight); _skybox.merge(other._skybox); _haze.merge(other._haze); + _bloom.merge(other._bloom); COPY_PROPERTY_IF_CHANGED(xTextureURL); COPY_PROPERTY_IF_CHANGED(yTextureURL); @@ -2096,6 +2126,11 @@ void EntityItemProperties::entityPropertyFlagsFromScriptValue(const QScriptValue ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_RANGE, Haze, haze, HazeKeyLightRange, hazeKeyLightRange); ADD_GROUP_PROPERTY_TO_MAP(PROP_HAZE_KEYLIGHT_ALTITUDE, Haze, haze, HazeKeyLightAltitude, hazeKeyLightAltitude); + ADD_PROPERTY_TO_MAP(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_INTENSITY, Bloom, bloom, BloomIntensity, bloomIntensity); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_THRESHOLD, Bloom, bloom, BloomThreshold, bloomThreshold); + ADD_GROUP_PROPERTY_TO_MAP(PROP_BLOOM_SIZE, Bloom, bloom, BloomSize, bloomSize); + ADD_PROPERTY_TO_MAP(PROP_KEY_LIGHT_MODE, KeyLightMode, keyLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t); ADD_PROPERTY_TO_MAP(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t); @@ -2357,6 +2392,10 @@ OctreeElement::AppendState EntityItemProperties::encodeEntityEditPacket(PacketTy _staticHaze.setProperties(properties); _staticHaze.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)properties.getBloomMode()); + _staticBloom.setProperties(properties); + _staticBloom.appendToEditPacket(packetData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)properties.getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)properties.getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)properties.getSkyboxMode()); @@ -2731,6 +2770,9 @@ bool EntityItemProperties::decodeEntityEditPacket(const unsigned char* data, int READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_HAZE_MODE, uint32_t, setHazeMode); properties.getHaze().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_BLOOM_MODE, uint32_t, setBloomMode); + properties.getBloom().decodeFromEditPacket(propertyFlags, dataAt, processedBytes); + READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY_TO_PROPERTIES(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); @@ -3044,10 +3086,12 @@ void EntityItemProperties::markAllChanged() { _skyboxModeChanged = true; _ambientLightModeChanged = true; _hazeModeChanged = true; + _bloomModeChanged = true; _animation.markAllChanged(); _skybox.markAllChanged(); _haze.markAllChanged(); + _bloom.markAllChanged(); _sourceUrlChanged = true; _voxelVolumeSizeChanged = true; @@ -3442,15 +3486,15 @@ QList EntityItemProperties::listChangedProperties() { if (hazeModeChanged()) { out += "hazeMode"; } - + if (bloomModeChanged()) { + out += "bloomMode"; + } if (keyLightModeChanged()) { out += "keyLightMode"; } - if (ambientLightModeChanged()) { out += "ambientLightMode"; } - if (skyboxModeChanged()) { out += "skyboxMode"; } @@ -3581,6 +3625,7 @@ QList EntityItemProperties::listChangedProperties() { getAmbientLight().listChangedProperties(out); getSkybox().listChangedProperties(out); getHaze().listChangedProperties(out); + getBloom().listChangedProperties(out); return out; } diff --git a/libraries/entities/src/EntityItemProperties.h b/libraries/entities/src/EntityItemProperties.h index 04e54c54a5..50305345de 100644 --- a/libraries/entities/src/EntityItemProperties.h +++ b/libraries/entities/src/EntityItemProperties.h @@ -42,6 +42,7 @@ #include "SimulationOwner.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include "TextEntityItem.h" #include "ZoneEntityItem.h" @@ -195,9 +196,11 @@ public: DEFINE_PROPERTY_REF_ENUM(PROP_SKYBOX_MODE, SkyboxMode, skyboxMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_AMBIENT_LIGHT_MODE, AmbientLightMode, ambientLightMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_REF_ENUM(PROP_HAZE_MODE, HazeMode, hazeMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); + DEFINE_PROPERTY_REF_ENUM(PROP_BLOOM_MODE, BloomMode, bloomMode, uint32_t, (uint32_t)COMPONENT_MODE_INHERIT); DEFINE_PROPERTY_GROUP(Skybox, skybox, SkyboxPropertyGroup); DEFINE_PROPERTY_GROUP(Haze, haze, HazePropertyGroup); + DEFINE_PROPERTY_GROUP(Bloom, bloom, BloomPropertyGroup); DEFINE_PROPERTY_GROUP(Animation, animation, AnimationPropertyGroup); DEFINE_PROPERTY_REF(PROP_SOURCE_URL, SourceUrl, sourceUrl, QString, ""); DEFINE_PROPERTY(PROP_LINE_WIDTH, LineWidth, lineWidth, float, LineEntityItem::DEFAULT_LINE_WIDTH); @@ -533,6 +536,7 @@ inline QDebug operator<<(QDebug debug, const EntityItemProperties& properties) { DEBUG_PROPERTY_IF_CHANGED(debug, properties, KeyLightMode, keyLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, AmbientLightMode, ambientLightMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, SkyboxMode, skyboxMode, ""); + DEBUG_PROPERTY_IF_CHANGED(debug, properties, BloomMode, bloomMode, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, Cloneable, cloneable, ""); DEBUG_PROPERTY_IF_CHANGED(debug, properties, CloneLifetime, cloneLifetime, ""); diff --git a/libraries/entities/src/EntityPropertyFlags.h b/libraries/entities/src/EntityPropertyFlags.h index 156c5d9dd4..3932730661 100644 --- a/libraries/entities/src/EntityPropertyFlags.h +++ b/libraries/entities/src/EntityPropertyFlags.h @@ -257,6 +257,11 @@ enum EntityPropertyList { PROP_SPIN_SPREAD, PROP_PARTICLE_ROTATE_WITH_ENTITY, + PROP_BLOOM_MODE, + PROP_BLOOM_INTENSITY, + PROP_BLOOM_THRESHOLD, + PROP_BLOOM_SIZE, + //////////////////////////////////////////////////////////////////////////////////////////////////// // ATTENTION: add new properties to end of list just ABOVE this line PROP_AFTER_LAST_ITEM, diff --git a/libraries/entities/src/ZoneEntityItem.cpp b/libraries/entities/src/ZoneEntityItem.cpp index f2550e5d3c..a7dfa1a41e 100644 --- a/libraries/entities/src/ZoneEntityItem.cpp +++ b/libraries/entities/src/ZoneEntityItem.cpp @@ -47,33 +47,27 @@ ZoneEntityItem::ZoneEntityItem(const EntityItemID& entityItemID) : EntityItem(en EntityItemProperties ZoneEntityItem::getProperties(EntityPropertyFlags desiredProperties) const { EntityItemProperties properties = EntityItem::getProperties(desiredProperties); // get the properties from our base class - // Contains a QString property, must be synchronized + // Contain QString properties, must be synchronized withReadLock([&] { _keyLightProperties.getProperties(properties); - }); - - withReadLock([&] { _ambientLightProperties.getProperties(properties); + _skyboxProperties.getProperties(properties); }); + _hazeProperties.getProperties(properties); + _bloomProperties.getProperties(properties); COPY_ENTITY_PROPERTY_TO_PROPERTIES(shapeType, getShapeType); COPY_ENTITY_PROPERTY_TO_PROPERTIES(compoundShapeURL, getCompoundShapeURL); - // Contains a QString property, must be synchronized - withReadLock([&] { - _skyboxProperties.getProperties(properties); - }); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(flyingAllowed, getFlyingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ghostingAllowed, getGhostingAllowed); COPY_ENTITY_PROPERTY_TO_PROPERTIES(filterURL, getFilterURL); COPY_ENTITY_PROPERTY_TO_PROPERTIES(hazeMode, getHazeMode); - _hazeProperties.getProperties(properties); - COPY_ENTITY_PROPERTY_TO_PROPERTIES(keyLightMode, getKeyLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(ambientLightMode, getAmbientLightMode); COPY_ENTITY_PROPERTY_TO_PROPERTIES(skyboxMode, getSkyboxMode); + COPY_ENTITY_PROPERTY_TO_PROPERTIES(bloomMode, getBloomMode); return properties; } @@ -102,32 +96,27 @@ bool ZoneEntityItem::setSubClassProperties(const EntityItemProperties& propertie // Contains a QString property, must be synchronized withWriteLock([&] { _keyLightPropertiesChanged = _keyLightProperties.setProperties(properties); - }); - withWriteLock([&] { _ambientLightPropertiesChanged = _ambientLightProperties.setProperties(properties); + _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); }); + _hazePropertiesChanged = _hazeProperties.setProperties(properties); + _bloomPropertiesChanged = _bloomProperties.setProperties(properties); SET_ENTITY_PROPERTY_FROM_PROPERTIES(shapeType, setShapeType); SET_ENTITY_PROPERTY_FROM_PROPERTIES(compoundShapeURL, setCompoundShapeURL); - // Contains a QString property, must be synchronized - withWriteLock([&] { - _skyboxPropertiesChanged = _skyboxProperties.setProperties(properties); - }); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(flyingAllowed, setFlyingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ghostingAllowed, setGhostingAllowed); SET_ENTITY_PROPERTY_FROM_PROPERTIES(filterURL, setFilterURL); SET_ENTITY_PROPERTY_FROM_PROPERTIES(hazeMode, setHazeMode); - _hazePropertiesChanged = _hazeProperties.setProperties(properties); - SET_ENTITY_PROPERTY_FROM_PROPERTIES(keyLightMode, setKeyLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(ambientLightMode, setAmbientLightMode); SET_ENTITY_PROPERTY_FROM_PROPERTIES(skyboxMode, setSkyboxMode); + SET_ENTITY_PROPERTY_FROM_PROPERTIES(bloomMode, setBloomMode); somethingChanged = somethingChanged || _keyLightPropertiesChanged || _ambientLightPropertiesChanged || - _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged; + _stagePropertiesChanged || _skyboxPropertiesChanged || _hazePropertiesChanged || _bloomPropertiesChanged; return somethingChanged; } @@ -139,54 +128,67 @@ int ZoneEntityItem::readEntitySubclassDataFromBuffer(const unsigned char* data, int bytesRead = 0; const unsigned char* dataAt = data; - int bytesFromKeylight; - withWriteLock([&] { - bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); - }); + { + int bytesFromKeylight; + withWriteLock([&] { + bytesFromKeylight = _keyLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _keyLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _keyLightPropertiesChanged; + bytesRead += bytesFromKeylight; + dataAt += bytesFromKeylight; + } - somethingChanged = somethingChanged || _keyLightPropertiesChanged; - bytesRead += bytesFromKeylight; - dataAt += bytesFromKeylight; + { + int bytesFromAmbientlight; + withWriteLock([&] { + bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); + }); + somethingChanged = somethingChanged || _ambientLightPropertiesChanged; + bytesRead += bytesFromAmbientlight; + dataAt += bytesFromAmbientlight; + } - int bytesFromAmbientlight; - withWriteLock([&] { - bytesFromAmbientlight = _ambientLightProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _ambientLightPropertiesChanged); - }); + { + int bytesFromSkybox; + withWriteLock([&] { + bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); + }); + somethingChanged = somethingChanged || _skyboxPropertiesChanged; + bytesRead += bytesFromSkybox; + dataAt += bytesFromSkybox; + } - somethingChanged = somethingChanged || _ambientLightPropertiesChanged; - bytesRead += bytesFromAmbientlight; - dataAt += bytesFromAmbientlight; + { + int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _hazePropertiesChanged); + somethingChanged = somethingChanged || _hazePropertiesChanged; + bytesRead += bytesFromHaze; + dataAt += bytesFromHaze; + } + + { + int bytesFromBloom = _bloomProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, + propertyFlags, overwriteLocalData, _bloomPropertiesChanged); + somethingChanged = somethingChanged || _bloomPropertiesChanged; + bytesRead += bytesFromBloom; + dataAt += bytesFromBloom; + } READ_ENTITY_PROPERTY(PROP_SHAPE_TYPE, ShapeType, setShapeType); READ_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, QString, setCompoundShapeURL); - int bytesFromSkybox; - withWriteLock([&] { - bytesFromSkybox = _skyboxProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _skyboxPropertiesChanged); - }); - somethingChanged = somethingChanged || _skyboxPropertiesChanged; - bytesRead += bytesFromSkybox; - dataAt += bytesFromSkybox; - READ_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, bool, setFlyingAllowed); READ_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, bool, setGhostingAllowed); READ_ENTITY_PROPERTY(PROP_FILTER_URL, QString, setFilterURL); READ_ENTITY_PROPERTY(PROP_HAZE_MODE, uint32_t, setHazeMode); - - int bytesFromHaze = _hazeProperties.readEntitySubclassDataFromBuffer(dataAt, (bytesLeftToRead - bytesRead), args, - propertyFlags, overwriteLocalData, _hazePropertiesChanged); - - somethingChanged = somethingChanged || _hazePropertiesChanged; - bytesRead += bytesFromHaze; - dataAt += bytesFromHaze; - READ_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, uint32_t, setKeyLightMode); READ_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, uint32_t, setAmbientLightMode); READ_ENTITY_PROPERTY(PROP_SKYBOX_MODE, uint32_t, setSkyboxMode); + READ_ENTITY_PROPERTY(PROP_BLOOM_MODE, uint32_t, setBloomMode); return bytesRead; } @@ -196,29 +198,24 @@ EntityPropertyFlags ZoneEntityItem::getEntityProperties(EncodeBitstreamParams& p withReadLock([&] { requestedProperties += _keyLightProperties.getEntityProperties(params); - }); - - withReadLock([&] { requestedProperties += _ambientLightProperties.getEntityProperties(params); + requestedProperties += _skyboxProperties.getEntityProperties(params); }); + requestedProperties += _hazeProperties.getEntityProperties(params); + requestedProperties += _bloomProperties.getEntityProperties(params); requestedProperties += PROP_SHAPE_TYPE; requestedProperties += PROP_COMPOUND_SHAPE_URL; - withReadLock([&] { - requestedProperties += _skyboxProperties.getEntityProperties(params); - }); - requestedProperties += PROP_FLYING_ALLOWED; requestedProperties += PROP_GHOSTING_ALLOWED; requestedProperties += PROP_FILTER_URL; requestedProperties += PROP_HAZE_MODE; - requestedProperties += _hazeProperties.getEntityProperties(params); - requestedProperties += PROP_KEY_LIGHT_MODE; requestedProperties += PROP_AMBIENT_LIGHT_MODE; requestedProperties += PROP_SKYBOX_MODE; + requestedProperties += PROP_BLOOM_MODE; return requestedProperties; } @@ -235,44 +232,46 @@ void ZoneEntityItem::appendSubclassData(OctreePacketData* packetData, EncodeBits _keyLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); - _ambientLightProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, propertyFlags, propertiesDidntFit, propertyCount, appendState); + _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); + _bloomProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, + propertyFlags, propertiesDidntFit, propertyCount, appendState); APPEND_ENTITY_PROPERTY(PROP_SHAPE_TYPE, (uint32_t)getShapeType()); APPEND_ENTITY_PROPERTY(PROP_COMPOUND_SHAPE_URL, getCompoundShapeURL()); - _skyboxProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_FLYING_ALLOWED, getFlyingAllowed()); APPEND_ENTITY_PROPERTY(PROP_GHOSTING_ALLOWED, getGhostingAllowed()); APPEND_ENTITY_PROPERTY(PROP_FILTER_URL, getFilterURL()); APPEND_ENTITY_PROPERTY(PROP_HAZE_MODE, (uint32_t)getHazeMode()); - _hazeProperties.appendSubclassData(packetData, params, modelTreeElementExtraEncodeData, requestedProperties, - propertyFlags, propertiesDidntFit, propertyCount, appendState); - APPEND_ENTITY_PROPERTY(PROP_KEY_LIGHT_MODE, (uint32_t)getKeyLightMode()); APPEND_ENTITY_PROPERTY(PROP_AMBIENT_LIGHT_MODE, (uint32_t)getAmbientLightMode()); APPEND_ENTITY_PROPERTY(PROP_SKYBOX_MODE, (uint32_t)getSkyboxMode()); + APPEND_ENTITY_PROPERTY(PROP_BLOOM_MODE, (uint32_t)getBloomMode()); } void ZoneEntityItem::debugDump() const { quint64 now = usecTimestampNow(); qCDebug(entities) << " ZoneEntityItem id:" << getEntityItemID() << "---------------------------------------------"; - qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); - qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); - qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); - qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); - qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); - qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); - qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " position:" << debugTreeVector(getWorldPosition()); + qCDebug(entities) << " dimensions:" << debugTreeVector(getScaledDimensions()); + qCDebug(entities) << " getLastEdited:" << debugTime(getLastEdited(), now); + qCDebug(entities) << " _hazeMode:" << EntityItemProperties::getComponentModeString(_hazeMode); + qCDebug(entities) << " _keyLightMode:" << EntityItemProperties::getComponentModeString(_keyLightMode); + qCDebug(entities) << " _ambientLightMode:" << EntityItemProperties::getComponentModeString(_ambientLightMode); + qCDebug(entities) << " _skyboxMode:" << EntityItemProperties::getComponentModeString(_skyboxMode); + qCDebug(entities) << " _bloomMode:" << EntityItemProperties::getComponentModeString(_bloomMode); _keyLightProperties.debugDump(); _ambientLightProperties.debugDump(); _skyboxProperties.debugDump(); _hazeProperties.debugDump(); + _bloomProperties.debugDump(); } ShapeType ZoneEntityItem::getShapeType() const { @@ -344,6 +343,7 @@ void ZoneEntityItem::resetRenderingPropertiesChanged() { _ambientLightPropertiesChanged = false; _skyboxPropertiesChanged = false; _hazePropertiesChanged = false; + _bloomPropertiesChanged = false; _stagePropertiesChanged = false; }); } @@ -359,6 +359,17 @@ uint32_t ZoneEntityItem::getHazeMode() const { return _hazeMode; } +void ZoneEntityItem::setBloomMode(const uint32_t value) { + if (value < COMPONENT_MODE_ITEM_COUNT && value != _bloomMode) { + _bloomMode = value; + _bloomPropertiesChanged = true; + } +} + +uint32_t ZoneEntityItem::getBloomMode() const { + return _bloomMode; +} + void ZoneEntityItem::setKeyLightMode(const uint32_t value) { if (value < COMPONENT_MODE_ITEM_COUNT && value != _keyLightMode) { _keyLightMode = value; diff --git a/libraries/entities/src/ZoneEntityItem.h b/libraries/entities/src/ZoneEntityItem.h index 0aaa32a57a..e2ebf16f11 100644 --- a/libraries/entities/src/ZoneEntityItem.h +++ b/libraries/entities/src/ZoneEntityItem.h @@ -18,6 +18,7 @@ #include "EntityTree.h" #include "SkyboxPropertyGroup.h" #include "HazePropertyGroup.h" +#include "BloomPropertyGroup.h" #include class ZoneEntityItem : public EntityItem { @@ -79,9 +80,13 @@ public: void setSkyboxMode(uint32_t value); uint32_t getSkyboxMode() const; + void setBloomMode(const uint32_t value); + uint32_t getBloomMode() const; + SkyboxPropertyGroup getSkyboxProperties() const { return resultWithReadLock([&] { return _skyboxProperties; }); } const HazePropertyGroup& getHazeProperties() const { return _hazeProperties; } + const BloomPropertyGroup& getBloomProperties() const { return _bloomProperties; } bool getFlyingAllowed() const { return _flyingAllowed; } void setFlyingAllowed(bool value) { _flyingAllowed = value; } @@ -93,10 +98,8 @@ public: bool keyLightPropertiesChanged() const { return _keyLightPropertiesChanged; } bool ambientLightPropertiesChanged() const { return _ambientLightPropertiesChanged; } bool skyboxPropertiesChanged() const { return _skyboxPropertiesChanged; } - - bool hazePropertiesChanged() const { - return _hazePropertiesChanged; - } + bool hazePropertiesChanged() const { return _hazePropertiesChanged; } + bool bloomPropertiesChanged() const { return _bloomPropertiesChanged; } bool stagePropertiesChanged() const { return _stagePropertiesChanged; } @@ -133,9 +136,11 @@ protected: uint32_t _ambientLightMode { COMPONENT_MODE_INHERIT }; uint32_t _hazeMode { COMPONENT_MODE_INHERIT }; + uint32_t _bloomMode { COMPONENT_MODE_INHERIT }; SkyboxPropertyGroup _skyboxProperties; HazePropertyGroup _hazeProperties; + BloomPropertyGroup _bloomProperties; bool _flyingAllowed { DEFAULT_FLYING_ALLOWED }; bool _ghostingAllowed { DEFAULT_GHOSTING_ALLOWED }; @@ -146,6 +151,7 @@ protected: bool _ambientLightPropertiesChanged { false }; bool _skyboxPropertiesChanged { false }; bool _hazePropertiesChanged{ false }; + bool _bloomPropertiesChanged { false }; bool _stagePropertiesChanged { false }; static bool _drawZoneBoundaries; diff --git a/libraries/graphics/src/graphics/Bloom.cpp b/libraries/graphics/src/graphics/Bloom.cpp new file mode 100644 index 0000000000..f8dcda3292 --- /dev/null +++ b/libraries/graphics/src/graphics/Bloom.cpp @@ -0,0 +1,18 @@ +// +// Bloom.cpp +// libraries/graphics/src/graphics +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "Bloom.h" + +using namespace graphics; + +const float Bloom::INITIAL_BLOOM_INTENSITY { 0.25f }; +const float Bloom::INITIAL_BLOOM_THRESHOLD { 0.7f }; +const float Bloom::INITIAL_BLOOM_SIZE { 0.9f }; \ No newline at end of file diff --git a/libraries/graphics/src/graphics/Bloom.h b/libraries/graphics/src/graphics/Bloom.h new file mode 100644 index 0000000000..02362ce25d --- /dev/null +++ b/libraries/graphics/src/graphics/Bloom.h @@ -0,0 +1,44 @@ +// +// Bloom.h +// libraries/graphics/src/graphics +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_model_Bloom_h +#define hifi_model_Bloom_h + +#include + +namespace graphics { + class Bloom { + public: + // Initial values + static const float INITIAL_BLOOM_INTENSITY; + static const float INITIAL_BLOOM_THRESHOLD; + static const float INITIAL_BLOOM_SIZE; + + Bloom() {} + + void setBloomIntensity(const float bloomIntensity) { _bloomIntensity = bloomIntensity; } + void setBloomThreshold(const float bloomThreshold) { _bloomThreshold = bloomThreshold; } + void setBloomSize(const float bloomSize) { _bloomSize = bloomSize; } + void setBloomActive(const bool isBloomActive) { _isBloomActive = isBloomActive; } + + float getBloomIntensity() { return _bloomIntensity; } + float getBloomThreshold() { return _bloomThreshold; } + float getBloomSize() { return _bloomSize; } + bool getBloomActive() { return _isBloomActive; } + + private: + bool _isBloomActive { false }; + float _bloomIntensity { INITIAL_BLOOM_INTENSITY }; + float _bloomThreshold {INITIAL_BLOOM_THRESHOLD }; + float _bloomSize { INITIAL_BLOOM_SIZE }; + }; + using BloomPointer = std::shared_ptr; +} +#endif // hifi_model_Bloom_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 82e9820509..ae165af5d1 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -33,7 +33,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::EntityEdit: case PacketType::EntityData: case PacketType::EntityPhysics: - return static_cast(EntityVersion::ParticleSpin); + return static_cast(EntityVersion::BloomEffect); case PacketType::EntityQuery: return static_cast(EntityQueryPacketVersion::ConicalFrustums); case PacketType::AvatarIdentity: diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 64c5bfe534..bfc92b9bde 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -240,7 +240,8 @@ enum class EntityVersion : PacketVersion { CollisionMask16Bytes, YieldSimulationOwnership, ParticleEntityFix, - ParticleSpin + ParticleSpin, + BloomEffect }; enum class EntityScriptCallMethodVersion : PacketVersion { diff --git a/libraries/render-utils/src/BloomEffect.cpp b/libraries/render-utils/src/BloomEffect.cpp index 2a9a9b3850..bd1f0fa9d2 100644 --- a/libraries/render-utils/src/BloomEffect.cpp +++ b/libraries/render-utils/src/BloomEffect.cpp @@ -25,11 +25,7 @@ BloomThreshold::BloomThreshold(unsigned int downsamplingFactor) { _parameters.edit()._sampleCount = downsamplingFactor; } -void BloomThreshold::configure(const Config& config) { - if (_parameters.get()._threshold != config.threshold) { - _parameters.edit()._threshold = config.threshold; - } -} +void BloomThreshold::configure(const Config& config) {} void BloomThreshold::run(const render::RenderContextPointer& renderContext, const Inputs& inputs, Outputs& outputs) { assert(renderContext->args); @@ -39,6 +35,7 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons const auto frameTransform = inputs.get0(); const auto inputFrameBuffer = inputs.get1(); + const auto bloom = inputs.get2(); assert(inputFrameBuffer->hasColor()); @@ -68,6 +65,28 @@ void BloomThreshold::run(const render::RenderContextPointer& renderContext, cons glm::ivec4 viewport{ 0, 0, bufferSize.x, bufferSize.y }; + if (!bloom || !bloom->getBloomActive()) { + outputs = _outputBuffer; + return; + } + + _parameters.edit()._threshold = bloom->getBloomThreshold(); + auto sigma = 0.5f + bloom->getBloomSize() * 3.5f; + + //{ + // std::string blurName { "BloomBlurN" }; + // auto task = static_cast(this); + + // for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { + // blurName.back() = '0' + i; + // auto blurJobIt = task->editJob(blurName); + // assert(blurJobIt != task->_jobs.end()); + // auto& gaussianBlur = blurJobIt->edit(); + // auto gaussianBlurParams = gaussianBlur.getParameters(); + // gaussianBlurParams->setFilterGaussianTaps(9, sigma); + // } + //} + gpu::doInBatch("BloomThreshold::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -90,16 +109,7 @@ BloomApply::BloomApply() { } -void BloomApply::configure(const Config& config) { - const auto newIntensity = config.intensity / 3.0f; - - if (_parameters.get()._intensities.x != newIntensity) { - auto& parameters = _parameters.edit(); - parameters._intensities.x = newIntensity; - parameters._intensities.y = newIntensity; - parameters._intensities.z = newIntensity; - } -} +void BloomApply::configure(const Config& config) {} void BloomApply::run(const render::RenderContextPointer& renderContext, const Inputs& inputs) { assert(renderContext->args); @@ -123,8 +133,19 @@ void BloomApply::run(const render::RenderContextPointer& renderContext, const In const auto blur0FB = inputs.get1(); const auto blur1FB = inputs.get2(); const auto blur2FB = inputs.get3(); + const auto bloom = inputs.get4(); const glm::ivec4 viewport{ 0, 0, framebufferSize.x, framebufferSize.y }; + if (!bloom || !bloom->getBloomActive()) { + return; + } + + const auto newIntensity = bloom->getBloomIntensity() / 3.0f; + auto& parameters = _parameters.edit(); + parameters._intensities.x = newIntensity; + parameters._intensities.y = newIntensity; + parameters._intensities.z = newIntensity; + gpu::doInBatch("BloomApply::run", args->_context, [&](gpu::Batch& batch) { batch.enableStereo(false); @@ -266,43 +287,9 @@ void DebugBloom::run(const render::RenderContextPointer& renderContext, const In }); } -void BloomConfig::setIntensity(float value) { - auto task = static_cast(_task); - auto blurJobIt = task->editJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - blurJobIt->getConfiguration()->setProperty("intensity", value); -} +BloomEffect::BloomEffect() {} -float BloomConfig::getIntensity() const { - auto task = static_cast(_task); - auto blurJobIt = task->getJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - return blurJobIt->getConfiguration()->property("intensity").toFloat(); -} - -void BloomConfig::setSize(float value) { - std::string blurName{ "BloomBlurN" }; - auto sigma = 0.5f+value*3.5f; - auto task = static_cast(_task); - - for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { - blurName.back() = '0' + i; - auto blurJobIt = task->editJob(blurName); - assert(blurJobIt != task->_jobs.end()); - auto& gaussianBlur = blurJobIt->edit(); - auto gaussianBlurParams = gaussianBlur.getParameters(); - gaussianBlurParams->setFilterGaussianTaps(9, sigma); - } - auto blurJobIt = task->getJob("BloomApply"); - assert(blurJobIt != task->_jobs.end()); - blurJobIt->getConfiguration()->setProperty("sigma", sigma); -} - -Bloom::Bloom() { - -} - -void Bloom::configure(const Config& config) { +void BloomEffect::configure(const Config& config) { std::string blurName{ "BloomBlurN" }; for (auto i = 0; i < BLOOM_BLUR_LEVEL_COUNT; i++) { @@ -312,7 +299,7 @@ void Bloom::configure(const Config& config) { } } -void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { +void BloomEffect::build(JobModel& task, const render::Varying& inputs, render::Varying& outputs) { // Start by computing threshold of color buffer input at quarter resolution const auto bloomInputBuffer = task.addJob("BloomThreshold", inputs, 4U); @@ -325,7 +312,7 @@ void Bloom::build(JobModel& task, const render::Varying& inputs, render::Varying const auto& frameBuffer = input[1]; // Mix all blur levels at quarter resolution - const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2).asVarying(); + const auto applyInput = BloomApply::Inputs(bloomInputBuffer, blurFB0, blurFB1, blurFB2, input[2]).asVarying(); task.addJob("BloomApply", applyInput); // And then blend result in additive manner on top of final color buffer const auto drawInput = BloomDraw::Inputs(frameBuffer, bloomInputBuffer).asVarying(); diff --git a/libraries/render-utils/src/BloomEffect.h b/libraries/render-utils/src/BloomEffect.h index 04cb4a9474..150a134dac 100644 --- a/libraries/render-utils/src/BloomEffect.h +++ b/libraries/render-utils/src/BloomEffect.h @@ -14,42 +14,21 @@ #include +#include "graphics/Bloom.h" + #include "DeferredFrameTransform.h" class BloomConfig : public render::Task::Config { Q_OBJECT - Q_PROPERTY(float intensity READ getIntensity WRITE setIntensity NOTIFY dirty) - Q_PROPERTY(float size MEMBER size WRITE setSize NOTIFY dirty) - -public: - - BloomConfig() : render::Task::Config(false) {} - - float size{ 0.7f }; - - void setIntensity(float value); - float getIntensity() const; - void setSize(float value); - -signals: - void dirty(); }; class BloomThresholdConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float threshold MEMBER threshold NOTIFY dirty) - -public: - - float threshold{ 0.9f }; - -signals: - void dirty(); }; class BloomThreshold { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using Outputs = gpu::FramebufferPointer; using Config = BloomThresholdConfig; using JobModel = render::Job::ModelIO; @@ -71,21 +50,11 @@ private: class BloomApplyConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(float intensity MEMBER intensity NOTIFY dirty) - Q_PROPERTY(float sigma MEMBER sigma NOTIFY dirty) - -public: - - float intensity{ 0.25f }; - float sigma{ 1.0f }; - -signals: - void dirty(); }; class BloomApply { public: - using Inputs = render::VaryingSet4; + using Inputs = render::VaryingSet5; using Config = BloomApplyConfig; using JobModel = render::Job::ModelI; @@ -118,7 +87,7 @@ private: class DebugBloomConfig : public render::Job::Config { Q_OBJECT - Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) + Q_PROPERTY(int mode MEMBER mode NOTIFY dirty) public: @@ -155,13 +124,13 @@ private: DebugBloomConfig::Mode _mode; }; -class Bloom { +class BloomEffect { public: - using Inputs = render::VaryingSet2; + using Inputs = render::VaryingSet3; using Config = BloomConfig; - using JobModel = render::Task::ModelI; + using JobModel = render::Task::ModelI; - Bloom(); + BloomEffect(); void configure(const Config& config); void build(JobModel& task, const render::Varying& inputs, render::Varying& outputs); diff --git a/libraries/render-utils/src/BloomStage.cpp b/libraries/render-utils/src/BloomStage.cpp new file mode 100644 index 0000000000..9a1c8ee183 --- /dev/null +++ b/libraries/render-utils/src/BloomStage.cpp @@ -0,0 +1,80 @@ +// +// BloomStage.cpp +// +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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 "BloomStage.h" + +#include "DeferredLightingEffect.h" + +#include + +std::string BloomStage::_stageName { "BLOOM_STAGE"}; +const BloomStage::Index BloomStage::INVALID_INDEX { render::indexed_container::INVALID_INDEX }; + +FetchBloomStage::FetchBloomStage() { + _bloom = std::make_shared(); +} + +void FetchBloomStage::configure(const Config& config) { + _bloom->setBloomIntensity(config.bloomIntensity); + _bloom->setBloomThreshold(config.bloomThreshold); + _bloom->setBloomSize(config.bloomSize); + _bloom->setBloomActive(config.isBloomActive); +} + +BloomStage::Index BloomStage::findBloom(const BloomPointer& bloom) const { + auto found = _bloomMap.find(bloom); + if (found != _bloomMap.end()) { + return INVALID_INDEX; + } else { + return (*found).second; + } +} + +BloomStage::Index BloomStage::addBloom(const BloomPointer& bloom) { + auto found = _bloomMap.find(bloom); + if (found == _bloomMap.end()) { + auto bloomId = _blooms.newElement(bloom); + // Avoid failing to allocate a bloom, just pass + if (bloomId != INVALID_INDEX) { + // Insert the bloom and its index in the reverse map + _bloomMap.insert(BloomMap::value_type(bloom, bloomId)); + } + return bloomId; + } else { + return (*found).second; + } +} + +BloomStage::BloomPointer BloomStage::removeBloom(Index index) { + BloomPointer removed = _blooms.freeElement(index); + if (removed) { + _bloomMap.erase(removed); + } + return removed; +} + +BloomStageSetup::BloomStageSetup() {} + +void BloomStageSetup::run(const render::RenderContextPointer& renderContext) { + auto stage = renderContext->_scene->getStage(BloomStage::getName()); + if (!stage) { + renderContext->_scene->resetStage(BloomStage::getName(), std::make_shared()); + } +} + +void FetchBloomStage::run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom) { + auto bloomStage = renderContext->_scene->getStage(); + assert(bloomStage); + + bloom = nullptr; + if (bloomStage->_currentFrame._blooms.size() != 0) { + auto bloomId = bloomStage->_currentFrame._blooms.front(); + bloom = bloomStage->getBloom(bloomId); + } +} diff --git a/libraries/render-utils/src/BloomStage.h b/libraries/render-utils/src/BloomStage.h new file mode 100644 index 0000000000..76829a19a6 --- /dev/null +++ b/libraries/render-utils/src/BloomStage.h @@ -0,0 +1,122 @@ +// +// BloomStage.h + +// Created by Sam Gondelman on 8/7/2018 +// Copyright 2018 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_render_utils_BloomStage_h +#define hifi_render_utils_BloomStage_h + +#include +#include +#include +#include +#include + +#include +#include +#include + +// Bloom stage to set up bloom-related rendering tasks +class BloomStage : public render::Stage { +public: + static std::string _stageName; + static const std::string& getName() { return _stageName; } + + using Index = render::indexed_container::Index; + static const Index INVALID_INDEX; + static bool isIndexInvalid(Index index) { return index == INVALID_INDEX; } + + using BloomPointer = graphics::BloomPointer; + using Blooms = render::indexed_container::IndexedPointerVector; + using BloomMap = std::unordered_map; + + using BloomIndices = std::vector; + + Index findBloom(const BloomPointer& bloom) const; + Index addBloom(const BloomPointer& bloom); + + BloomPointer removeBloom(Index index); + + bool checkBloomId(Index index) const { return _blooms.checkIndex(index); } + + Index getNumBlooms() const { return _blooms.getNumElements(); } + Index getNumFreeBlooms() const { return _blooms.getNumFreeIndices(); } + Index getNumAllocatedBlooms() const { return _blooms.getNumAllocatedIndices(); } + + BloomPointer getBloom(Index bloomId) const { + return _blooms.get(bloomId); + } + + Blooms _blooms; + BloomMap _bloomMap; + + class Frame { + public: + Frame() {} + + void clear() { _blooms.clear(); } + + void pushBloom(BloomStage::Index index) { _blooms.emplace_back(index); } + + BloomStage::BloomIndices _blooms; + }; + + Frame _currentFrame; +}; +using BloomStagePointer = std::shared_ptr; + +class BloomStageSetup { +public: + using JobModel = render::Job::Model; + + BloomStageSetup(); + void run(const render::RenderContextPointer& renderContext); + +protected: +}; + +class FetchBloomConfig : public render::Job::Config { + Q_OBJECT + Q_PROPERTY(float bloomIntensity MEMBER bloomIntensity WRITE setBloomIntensity NOTIFY dirty); + Q_PROPERTY(float bloomThreshold MEMBER bloomThreshold WRITE setBloomThreshold NOTIFY dirty); + Q_PROPERTY(float bloomSize MEMBER bloomSize WRITE setBloomSize NOTIFY dirty); + Q_PROPERTY(bool isBloomActive MEMBER isBloomActive WRITE setBloomActive NOTIFY dirty); + +public: + FetchBloomConfig() : render::Job::Config() {} + + float bloomIntensity { graphics::Bloom::INITIAL_BLOOM_INTENSITY }; + float bloomThreshold { graphics::Bloom::INITIAL_BLOOM_THRESHOLD }; + float bloomSize { graphics::Bloom::INITIAL_BLOOM_SIZE }; + + bool isBloomActive { false }; + +public slots: + void setBloomIntensity(const float value) { bloomIntensity = value; emit dirty(); } + void setBloomThreshold(const float value) { bloomThreshold = value; emit dirty(); } + void setBloomSize(const float value) { bloomSize = value; emit dirty(); } + void setBloomActive(const bool active) { isBloomActive = active; emit dirty(); } + +signals: + void dirty(); +}; + +class FetchBloomStage { +public: + using Config = FetchBloomConfig; + using JobModel = render::Job::ModelO; + + FetchBloomStage(); + + void configure(const Config& config); + void run(const render::RenderContextPointer& renderContext, graphics::BloomPointer& bloom); + +private: + graphics::BloomPointer _bloom; +}; +#endif diff --git a/libraries/render-utils/src/HazeStage.h b/libraries/render-utils/src/HazeStage.h index 8f137cb280..b48168e376 100644 --- a/libraries/render-utils/src/HazeStage.h +++ b/libraries/render-utils/src/HazeStage.h @@ -163,6 +163,5 @@ public: private: graphics::HazePointer _haze; - gpu::PipelinePointer _hazePipeline; }; #endif diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 59c86acd19..49851524cd 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -45,6 +45,7 @@ #include "TextureCache.h" #include "ZoneRenderer.h" #include "FadeEffect.h" +#include "BloomStage.h" #include "RenderUtilsLogging.h" #include "AmbientOcclusionEffect.h" @@ -171,7 +172,7 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto velocityBufferOutputs = task.addJob("VelocityBuffer", velocityBufferInputs); const auto velocityBuffer = velocityBufferOutputs.getN(0); - // Clear Light, Haze and Skybox Stages and render zones from the general metas bucket + // Clear Light, Haze, Bloom, and Skybox Stages and render zones from the general metas bucket const auto zones = task.addJob("ZoneRenderer", metas); // Draw Lights just add the lights to the current list of lights to deal with. NOt really gpu job for now. @@ -184,6 +185,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren // Add haze model const auto hazeModel = task.addJob("HazeModel"); + // Add Bloom model + const auto bloomModel = task.addJob("BloomModel"); // DeferredBuffer is complete, now let's shade it into the LightingBuffer const auto deferredLightingInputs = RenderDeferred::Inputs(deferredFrameTransform, deferredFramebuffer, lightingModel, @@ -240,8 +243,8 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren task.addJob("Antialiasing", antialiasingInputs); // Add bloom - const auto bloomInputs = Bloom::Inputs(deferredFrameTransform, lightingFramebuffer).asVarying(); - task.addJob("Bloom", bloomInputs); + const auto bloomInputs = BloomEffect::Inputs(deferredFrameTransform, lightingFramebuffer, bloomModel).asVarying(); + task.addJob("Bloom", bloomInputs); // Lighting Buffer ready for tone mapping const auto toneMappingInputs = ToneMappingDeferred::Inputs(lightingFramebuffer, scaledPrimaryFramebuffer).asVarying(); diff --git a/libraries/render-utils/src/UpdateSceneTask.cpp b/libraries/render-utils/src/UpdateSceneTask.cpp index e05f28ef0d..be61953073 100644 --- a/libraries/render-utils/src/UpdateSceneTask.cpp +++ b/libraries/render-utils/src/UpdateSceneTask.cpp @@ -14,6 +14,7 @@ #include "LightStage.h" #include "BackgroundStage.h" #include "HazeStage.h" +#include "BloomStage.h" #include #include #include "DeferredLightingEffect.h" @@ -22,6 +23,7 @@ void UpdateSceneTask::build(JobModel& task, const render::Varying& input, render task.addJob("LightStageSetup"); task.addJob("BackgroundStageSetup"); task.addJob("HazeStageSetup"); + task.addJob("BloomStageSetup"); task.addJob("TransitionStageSetup"); task.addJob("HighlightStageSetup"); diff --git a/libraries/render-utils/src/ZoneRenderer.cpp b/libraries/render-utils/src/ZoneRenderer.cpp index 3299b0c41c..d85818a21a 100644 --- a/libraries/render-utils/src/ZoneRenderer.cpp +++ b/libraries/render-utils/src/ZoneRenderer.cpp @@ -21,11 +21,12 @@ #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" - #include "render-utils/ShaderConstants.h" #include "StencilMaskPass.h" #include "DeferredLightingEffect.h" +#include "BloomStage.h" + namespace ru { using render_utils::slot::texture::Texture; using render_utils::slot::buffer::Buffer; @@ -63,7 +64,7 @@ void ZoneRendererTask::build(JobModel& task, const Varying& input, Varying& oupu } void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) { - // Grab light, background and haze stages and clear them + // Grab light, background, haze, and bloom stages and clear them auto lightStage = context->_scene->getStage(); assert(lightStage); lightStage->_currentFrame.clear(); @@ -76,6 +77,10 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) assert(hazeStage); hazeStage->_currentFrame.clear(); + auto bloomStage = context->_scene->getStage(); + assert(bloomStage); + bloomStage->_currentFrame.clear(); + // call render over the zones to grab their components in the correct order first... render::renderItems(context, inputs); @@ -84,6 +89,7 @@ void SetupZones::run(const RenderContextPointer& context, const Inputs& inputs) lightStage->_currentFrame.pushAmbientLight(lightStage->getDefaultLight()); backgroundStage->_currentFrame.pushBackground(0); hazeStage->_currentFrame.pushHaze(0); + bloomStage->_currentFrame.pushBloom(0); } const gpu::PipelinePointer& DebugZoneLighting::getKeyLightPipeline() { diff --git a/scripts/system/html/entityProperties.html b/scripts/system/html/entityProperties.html index 9614f8b8fe..744150253d 100644 --- a/scripts/system/html/entityProperties.html +++ b/scripts/system/html/entityProperties.html @@ -678,6 +678,52 @@
+
+ + Bloom + +
+ Inherit + Off + On +
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
+
+
+
+ + + +
+
+
+
+
+
diff --git a/scripts/system/html/js/entityProperties.js b/scripts/system/html/js/entityProperties.js index d2cea2d394..43ea6bf22f 100644 --- a/scripts/system/html/js/entityProperties.js +++ b/scripts/system/html/js/entityProperties.js @@ -839,7 +839,7 @@ function loaded() { var elZoneHazeModeInherit = document.getElementById("property-zone-haze-mode-inherit"); var elZoneHazeModeDisabled = document.getElementById("property-zone-haze-mode-disabled"); var elZoneHazeModeEnabled = document.getElementById("property-zone-haze-mode-enabled"); - + var elZoneHazeRange = document.getElementById("property-zone-haze-range"); var elZoneHazeColor = document.getElementById("property-zone-haze-color"); var elZoneHazeColorRed = document.getElementById("property-zone-haze-color-red"); @@ -858,6 +858,15 @@ function loaded() { var elZoneHazeBackgroundBlend = document.getElementById("property-zone-haze-background-blend"); + // Bloom + var elZoneBloomModeInherit = document.getElementById("property-zone-bloom-mode-inherit"); + var elZoneBloomModeDisabled = document.getElementById("property-zone-bloom-mode-disabled"); + var elZoneBloomModeEnabled = document.getElementById("property-zone-bloom-mode-enabled"); + + var elZoneBloomIntensity = document.getElementById("property-zone-bloom-intensity"); + var elZoneBloomThreshold = document.getElementById("property-zone-bloom-threshold"); + var elZoneBloomSize = document.getElementById("property-zone-bloom-size"); + var elZoneSkyboxColor = document.getElementById("property-zone-skybox-color"); var elZoneSkyboxColorRed = document.getElementById("property-zone-skybox-color-red"); var elZoneSkyboxColorGreen = document.getElementById("property-zone-skybox-color-green"); @@ -914,19 +923,19 @@ function loaded() { deleteJSONMaterialEditor(); } } - + elTypeIcon.style.display = "none"; elType.innerHTML = "No selection"; elPropertiesList.className = ''; - + elID.value = ""; elName.value = ""; elLocked.checked = false; elVisible.checked = false; - + elParentID.value = ""; elParentJointIndex.value = ""; - + elColorRed.value = ""; elColorGreen.value = ""; elColorBlue.value = ""; @@ -935,15 +944,15 @@ function loaded() { elPositionX.value = ""; elPositionY.value = ""; elPositionZ.value = ""; - + elRotationX.value = ""; elRotationY.value = ""; elRotationZ.value = ""; - + elDimensionsX.value = ""; elDimensionsY.value = ""; elDimensionsZ.value = ""; - + elRegistrationX.value = ""; elRegistrationY.value = ""; elRegistrationZ.value = ""; @@ -965,14 +974,14 @@ function loaded() { elAccelerationX.value = ""; elAccelerationY.value = ""; elAccelerationZ.value = ""; - + elRestitution.value = ""; elFriction.value = ""; elDensity.value = ""; - + elCollisionless.checked = false; elDynamic.checked = false; - + elCollideStatic.checked = false; elCollideKinematic.checked = false; elCollideDynamic.checked = false; @@ -989,27 +998,27 @@ function loaded() { elCloneableGroup.style.display = "none"; elCloneableLimit.value = ""; elCloneableLifetime.value = ""; - - showElements(document.getElementsByClassName('can-cast-shadow-section'), true); + + showElements(document.getElementsByClassName('can-cast-shadow-section'), true); elCanCastShadow.checked = false; - + elCollisionSoundURL.value = ""; elLifetime.value = ""; elScriptURL.value = ""; elServerScripts.value = ""; elHyperlinkHref.value = ""; elDescription.value = ""; - + deleteJSONEditor(); elUserData.value = ""; showUserDataTextArea(); showSaveUserDataButton(); showNewJSONEditorButton(); - + // Shape Properties elShape.value = "Cube"; setDropdownText(elShape); - + // Light Properties elLightSpotLight.checked = false; elLightColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; @@ -1020,7 +1029,7 @@ function loaded() { elLightFalloffRadius.value = ""; elLightExponent.value = ""; elLightCutoff.value = ""; - + // Model Properties elModelURL.value = ""; elCompoundShapeURL.value = ""; @@ -1037,7 +1046,7 @@ function loaded() { elModelAnimationAllowTranslation.checked = false; elModelTextures.value = ""; elModelOriginalTextures.value = ""; - + // Zone Properties elZoneFlyingAllowed.checked = false; elZoneGhostingAllowed.checked = false; @@ -1067,6 +1076,9 @@ function loaded() { elZoneHazeAltitudeEffect.checked = false; elZoneHazeBaseRef.value = ""; elZoneHazeCeiling.value = ""; + elZoneBloomIntensity.value = ""; + elZoneBloomThreshold.value = ""; + elZoneBloomSize.value = ""; elZoneSkyboxColor.style.backgroundColor = "rgb(" + 0 + "," + 0 + "," + 0 + ")"; elZoneSkyboxColorRed.value = ""; elZoneSkyboxColorGreen.value = ""; @@ -1076,7 +1088,8 @@ function loaded() { showElements(document.getElementsByClassName('skybox-section'), true); showElements(document.getElementsByClassName('ambient-section'), true); showElements(document.getElementsByClassName('haze-section'), true); - + showElements(document.getElementsByClassName('bloom-section'), true); + // Text Properties elTextText.value = ""; elTextLineHeight.value = ""; @@ -1089,14 +1102,14 @@ function loaded() { elTextBackgroundColorRed.value = ""; elTextBackgroundColorGreen.value = ""; elTextBackgroundColorBlue.value = ""; - + // Image Properties elImageURL.value = ""; - + // Web Properties elWebSourceURL.value = ""; elWebDPI.value = ""; - + // Material Properties elMaterialURL.value = ""; elParentMaterialNameNumber.value = ""; @@ -1107,13 +1120,13 @@ function loaded() { elMaterialMappingScaleX.value = ""; elMaterialMappingScaleY.value = ""; elMaterialMappingRot.value = ""; - + deleteJSONMaterialEditor(); elMaterialData.value = ""; showMaterialDataTextArea(); showSaveMaterialDataButton(); showNewJSONMaterialEditorButton(); - + disableProperties(); } else if (data.selections.length > 1) { deleteJSONEditor(); @@ -1250,7 +1263,7 @@ function loaded() { elCloneableGroup.style.display = elCloneable.checked ? "block": "none"; elCloneableLimit.value = properties.cloneLimit; elCloneableLifetime.value = properties.cloneLifetime; - + var grabbablesSet = false; var parsedUserData = {}; try { @@ -1476,6 +1489,14 @@ function loaded() { elZoneHazeBaseRef.value = properties.haze.hazeBaseRef.toFixed(0); elZoneHazeCeiling.value = properties.haze.hazeCeiling.toFixed(0); + elZoneBloomModeInherit.checked = (properties.bloomMode === 'inherit'); + elZoneBloomModeDisabled.checked = (properties.bloomMode === 'disabled'); + elZoneBloomModeEnabled.checked = (properties.bloomMode === 'enabled'); + + elZoneBloomIntensity.value = properties.bloom.bloomIntensity.toFixed(2); + elZoneBloomThreshold.value = properties.bloom.bloomThreshold.toFixed(2); + elZoneBloomSize.value = properties.bloom.bloomSize.toFixed(2); + elShapeType.value = properties.shapeType; elCompoundShapeURL.value = properties.compoundShapeURL; @@ -1502,6 +1523,9 @@ function loaded() { showElements(document.getElementsByClassName('haze-section'), elZoneHazeModeEnabled.checked); + + showElements(document.getElementsByClassName('bloom-section'), + elZoneBloomModeEnabled.checked); } else if (properties.type === "PolyVox") { elVoxelVolumeSizeX.value = properties.voxelVolumeSize.x.toFixed(2); elVoxelVolumeSizeY.value = properties.voxelVolumeSize.y.toFixed(2); @@ -2067,6 +2091,18 @@ function loaded() { elZoneHazeBackgroundBlend.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('haze', 'hazeBackgroundBlend')); + // Bloom + var bloomModeChanged = createZoneComponentModeChangedFunction('bloomMode', + elZoneBloomModeInherit, elZoneBloomModeDisabled, elZoneBloomModeEnabled); + + elZoneBloomModeInherit.addEventListener('change', bloomModeChanged); + elZoneBloomModeDisabled.addEventListener('change', bloomModeChanged); + elZoneBloomModeEnabled.addEventListener('change', bloomModeChanged); + + elZoneBloomIntensity.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomIntensity')); + elZoneBloomThreshold.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomThreshold')); + elZoneBloomSize.addEventListener('change', createEmitGroupNumberPropertyUpdateFunction('bloom', 'bloomSize')); + var zoneSkyboxColorChangeFunction = createEmitGroupColorPropertyUpdateFunction('skybox', 'color', elZoneSkyboxColorRed, elZoneSkyboxColorGreen, elZoneSkyboxColorBlue); elZoneSkyboxColorRed.addEventListener('change', zoneSkyboxColorChangeFunction); From 297e5b2e2f04102eb743c7b9d6391b4651cbc5cb Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 8 Aug 2018 09:18:28 -0700 Subject: [PATCH 011/207] removed print statements --- interface/src/Application.cpp | 1 + interface/src/avatar/MyAvatar.cpp | 6 ------ interface/src/avatar/MySkeletonModel.cpp | 2 -- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 1 - 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d1871c7692..0bbd2b1c17 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8133,6 +8133,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { // reset the avatar, to set head and hand palms back to a reasonable default pose. getMyAvatar()->reset(false); + // switch to first person if entering hmd and setting is checked if (menu) { QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fdd7edb7a..3f738ea4cb 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -806,7 +806,6 @@ void MyAvatar::updateSensorToWorldMatrix() { // update the sensor mat so that the body position will end up in the desired // position when driven from the head. float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); - qCDebug(interfaceapp) << sensorToWorldScale; glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); @@ -1743,10 +1742,6 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); - glm::mat4 printout = getSensorToWorldMatrix(); - if (action == controller::Action::HEAD) { - //qCDebug(interfaceapp) << "matrix 4 sensor to world" << printout; - } if (pose.valid) { return pose.transform(getSensorToWorldMatrix()); } else { @@ -1756,7 +1751,6 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); - // qCDebug(interfaceapp) << " the head position in world frame is " << pose.getTranslation(); if (pose.valid) { glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6bc0c8fefa..0fc5e7521e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -116,9 +116,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); - if (avatarHeadPose.isValid()) { - //qCDebug(interfaceapp) << " the head position in avatar frame is " << avatarHeadPose.getTranslation(); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0fda5be77b..69356cdfaa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1761,7 +1761,6 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& // thread-safe float Avatar::getEyeHeight() const { - qCDebug(animation) << "modelscale "< Date: Wed, 8 Aug 2018 09:22:31 -0700 Subject: [PATCH 012/207] fixing the ui script and trying to get shadow --- interface/src/workload/GameWorkloadRenderer.cpp | 2 +- libraries/render-utils/src/WorkloadResource.slh | 8 ++++---- scripts/developer/utilities/workload/workload.js | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index ae0e0abbeb..bbbc24c2c1 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -97,7 +97,7 @@ namespace render { } } -GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) { +GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withShadowCaster().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) { } render::ItemKey GameWorkloadRenderItem::getKey() const { diff --git a/libraries/render-utils/src/WorkloadResource.slh b/libraries/render-utils/src/WorkloadResource.slh index c1d0d21fa1..81b6ed78ce 100644 --- a/libraries/render-utils/src/WorkloadResource.slh +++ b/libraries/render-utils/src/WorkloadResource.slh @@ -26,7 +26,7 @@ struct WorkloadProxy { }; #if defined(GPU_GL410) -uniform samplerBuffer workloadProxiesBuffer; +layout(binding=0) uniform samplerBuffer workloadProxiesBuffer; WorkloadProxy getWorkloadProxy(int i) { int offset = 2 * i; WorkloadProxy proxy; @@ -35,7 +35,7 @@ WorkloadProxy getWorkloadProxy(int i) { return proxy; } #else -layout(std140) buffer workloadProxiesBuffer { +layout(std140, binding=0) buffer workloadProxiesBuffer { WorkloadProxy _proxies[]; }; WorkloadProxy getWorkloadProxy(int i) { @@ -58,7 +58,7 @@ struct WorkloadView { }; #if defined(GPU_GL410) -uniform samplerBuffer workloadViewsBuffer; +layout(binding=1) uniform samplerBuffer workloadViewsBuffer; WorkloadView getWorkloadView(int i) { int offset = 2 * i; WorkloadView view; @@ -67,7 +67,7 @@ WorkloadView getWorkloadView(int i) { return view; } #else -layout(std140) buffer workloadViewsBuffer { +layout(std140, binding=1) buffer workloadViewsBuffer { WorkloadView _views[]; }; WorkloadView getWorkloadView(int i) { diff --git a/scripts/developer/utilities/workload/workload.js b/scripts/developer/utilities/workload/workload.js index ce41e0ecb8..bdd33dcc5a 100644 --- a/scripts/developer/utilities/workload/workload.js +++ b/scripts/developer/utilities/workload/workload.js @@ -56,7 +56,7 @@ presentationMode: Desktop.PresentationMode.NATIVE, size: {x: 400, y: 600} }); - window.setPosition(200, 50); + // window.setPosition(200, 50); window.closed.connect(killWindow); window.fromQml.connect(fromQml); onScreen = true @@ -107,9 +107,7 @@ } } - function fromQml(message) { - } - + button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); @@ -127,6 +125,7 @@ Script.include("./test_physics_scene.js") function fromQml(message) { + print("fromQml: " + JSON.stringify(message)) switch (message.method) { case "createScene": createScene(); From 88bd4e3d9a3022341436586975592fb3aec4d537 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 8 Aug 2018 12:32:45 -0700 Subject: [PATCH 013/207] WIP - safe landing infrastructure --- interface/src/Application.cpp | 1 + .../src/octree/OctreePacketProcessor.cpp | 9 +- interface/src/octree/OctreePacketProcessor.h | 1 + interface/src/octree/SafeLanding.cpp | 96 +++++++++++++++++++ interface/src/octree/SafeLanding.h | 40 +++++++- 5 files changed, 143 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bbd2b1c17..b2beb5d551 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6166,6 +6166,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { _octreeQuery.setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE); static constexpr float MIN_LOD_ADJUST = -20.0f; _octreeQuery.setBoundaryLevelAdjust(MIN_LOD_ADJUST); + _octreeProcessor.startEntitySequence(); } else { _octreeQuery.setConicalViews(_conicalViews); auto lodManager = DependencyManager::get(); diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 52885e8902..e6a1857189 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -18,7 +18,9 @@ #include "SceneScriptingInterface.h" #include "SafeLanding.h" -OctreePacketProcessor::OctreePacketProcessor() { +OctreePacketProcessor::OctreePacketProcessor(): + _safeLanding(new SafeLanding()) +{ setObjectName("Octree Packet Processor"); auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); @@ -120,6 +122,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag message->readPrimitive(&completionNumber); _completionSequenceNumber = completionNumber; + _safeLanding->setCompletionSequenceNumbers(0, completionNumber); } break; default: { @@ -147,3 +150,7 @@ bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { return _completionSequenceNumber != INVALID_SEQUENCE && !lessThanWraparound(sequenceNumber, _completionSequenceNumber); } + +void OctreePacketProcessor::startEntitySequence() { + _safeLanding->startEntitySequence(qApp->getEntities()); +} diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index 866a59eaa6..e0a4429da2 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -25,6 +25,7 @@ public: OctreePacketProcessor(); ~OctreePacketProcessor(); + void startEntitySequence(); bool octreeSequenceIsComplete(int sequenceNumber) const; signals: diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index c4a2fa13e9..85040e5333 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -10,8 +10,104 @@ // #include "SafeLanding.h" +#include "EntityTreeRenderer.h" + +const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits::max() + 1; + +namespace { + template bool lessThanWraparound(int a, int b) { + constexpr int MAX_T_VALUE = std::numeric_limits::max(); + if (b <= a) { + b += MAX_T_VALUE; + } + return (b - a) < (MAX_T_VALUE / 2); + } +} + +bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const { + return lessThanWraparound(a, b); +} SafeLanding::SafeLanding() { } SafeLanding::~SafeLanding() { } + +void SafeLanding::startEntitySequence(QSharedPointer entityTreeRenderer) { + if (!_trackingEntities) { + auto entityTree = entityTreeRenderer->getTree(); + + if (entityTree) { + _entityTree = entityTree; + connect(std::const_pointer_cast(_entityTree).get(), + &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity); + connect(std::const_pointer_cast(_entityTree).get(), + &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity); + _trackingEntities = true; + _sequenceNumbers.clear(); + _isSequenceComplete = false; + } + } +} + +void SafeLanding::stopEntitySequence() { + _trackingEntities = false; + _initialStart = INVALID_SEQUENCE; + _initialEnd = INVALID_SEQUENCE; + _trackedEntities.clear(); + _sequenceNumbers.clear(); +} + +void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { + volatile EntityItemID id = entityID; + if (_trackingEntities) { + EntityItemPointer entity = _entityTree->findEntityByID(entityID); + if (entity) { + const auto& entityType = entity->getType(); + // Entity types of interest: + static const std::set solidTypes + { EntityTypes::Box, EntityTypes::Sphere, EntityTypes::Shape, EntityTypes::Model }; + + if (solidTypes.count(entity->getType()) && !entity->getCollisionless()) { + _trackedEntities.emplace(entityID, entity); + } + + } + + } +} + +void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) { + _trackedEntities.erase(entityID); +} + +void SafeLanding::setCompletionSequenceNumbers(int first, int last) { + _initialStart = first; + _initialEnd = (last + 1) % SEQUENCE_MODULO; + auto firstIter = _sequenceNumbers.find(_initialStart); + if (firstIter != _sequenceNumbers.end()) { + _sequenceNumbers.erase(_sequenceNumbers.begin(), firstIter); + } + _sequenceNumbers.erase(_sequenceNumbers.find(_initialEnd), _sequenceNumbers.end()); +} + +void SafeLanding::sequenceNumberReceived(int sequenceNumber) { + if (_trackingEntities) { + _sequenceNumbers.insert(sequenceNumber); + } +} + +bool SafeLanding::sequenceNumbersComplete() { + if (_initialStart != INVALID_SEQUENCE) { + auto startIter = _sequenceNumbers.find(_initialStart); + if (startIter != _sequenceNumbers.end()) { + _sequenceNumbers.erase(_sequenceNumbers.begin(), startIter); + _sequenceNumbers.erase(_sequenceNumbers.find(_initialEnd), _sequenceNumbers.end()); + int sequenceSize = _initialStart < _initialEnd ? _initialEnd - _initialStart : + _initialEnd + SEQUENCE_MODULO - _initialStart; + // First no. exists, nothing outside of first, last exists, so complete iff set size is sequence size. + return (int) _sequenceNumbers.size() == sequenceSize; + } + } + return false; +} diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index af13ef7539..ba343ad5eb 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -14,13 +14,47 @@ #ifndef hifi_SafeLanding_h #define hifi_SafeLanding_h -class SafeLanding { +#include +#include + +#include "EntityItem.h" + +class EntityTreeRenderer; +class EntityItemID; + +class SafeLanding : public QObject { public: - SafeLanding(); - ~SafeLanding(); + SafeLanding(); + ~SafeLanding(); + void startEntitySequence(QSharedPointer entityTreeRenderer); + void stopEntitySequence(); + void setCompletionSequenceNumbers(int first, int last); + void sequenceNumberReceived(int sequenceNumber); + bool isSequenceComplete() const { return _isSequenceComplete; } + +private slots: + void addTrackedEntity(const EntityItemID& entityID); + void deleteTrackedEntity(const EntityItemID& entityID); private: + bool sequenceNumbersComplete(); + bool _trackingEntities { false }; + EntityTreePointer _entityTree; + using EntityMap = std::map; + EntityMap _trackedEntities; + static constexpr int INVALID_SEQUENCE = -1; + bool _isSequenceComplete { true }; + int _initialStart { INVALID_SEQUENCE }; + int _initialEnd { INVALID_SEQUENCE }; // final sequence, exclusive. + + struct SequenceLessThan { + bool operator()(const int& a, const int& b) const; + }; + + std::set _sequenceNumbers; + + static const int SEQUENCE_MODULO; }; #endif // hifi_SafeLanding_h From c774dd7f5bf0734b771fc0616a3fff47cfa9081e Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 8 Aug 2018 17:52:22 -0700 Subject: [PATCH 014/207] debugging to solve the no-neck crazy scale problem --- interface/src/Application.cpp | 5 +++++ interface/src/avatar/MyAvatar.cpp | 2 ++ interface/src/avatar/MySkeletonModel.cpp | 5 +++++ .../src/avatars-renderer/Avatar.cpp | 3 +++ .../src/avatars-renderer/SkeletonModel.cpp | 19 +++++++++++++++++++ libraries/shared/src/shared/Camera.cpp | 1 + 6 files changed, 35 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bbd2b1c17..8a7a5c610a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3064,6 +3064,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { auto myAvatar = getMyAvatar(); boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; + //qCDebug(interfaceapp) << "boom offset" << boomOffset; // The render mode is default or mirror if the camera is in mirror mode, assigned further below renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; @@ -3074,10 +3075,12 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); + //qCDebug(interfaceapp) << "camera 1rst translation hmd " << extractTranslation(camMat); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); } else { + //qCDebug(interfaceapp) << "camera 1rst translation desktop " << myAvatar->getDefaultEyePosition(); _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); } @@ -3099,6 +3102,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); _myCamera.setPosition(extractTranslation(worldCameraMat)); + //qCDebug(interfaceapp) << "camera translation 3rd hmd " << extractTranslation(worldCameraMat); } else { _thirdPersonHMDCameraBoomValid = false; @@ -3112,6 +3116,7 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() + myAvatar->getWorldOrientation() * boomOffset); } + qCDebug(interfaceapp) << "camera translation 3rd desktop " << (myAvatar->getDefaultEyePosition() + _myCamera.getOrientation() * boomOffset); } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3f738ea4cb..9971f90bd6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1704,8 +1704,10 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); + //qCDebug(interfaceapp) << "position third person " << getWorldPosition(); return getWorldPosition() + getWorldOrientation() * FLIP * _skeletonOffset; } + //qCDebug(interfaceapp) << "position first person " << Avatar::getWorldPosition(); return Avatar::getWorldPosition(); } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0fc5e7521e..86085798ab 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -95,7 +95,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // make sure lookAt is not too close to face (avoid crosseyes) glm::vec3 lookAt = head->getLookAtPosition(); + //qCDebug(interfaceapp) << "look at " << lookAt; glm::vec3 focusOffset = lookAt - _owningAvatar->getHead()->getEyePosition(); + //qCDebug(interfaceapp) << "focus offset " << focusOffset; float focusDistance = glm::length(focusOffset); const float MIN_LOOK_AT_FOCUS_DISTANCE = 1.0f; if (focusDistance < MIN_LOOK_AT_FOCUS_DISTANCE && focusDistance > EPSILON) { @@ -113,6 +115,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()); glm::mat4 sensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); params.rigToSensorMatrix = glm::inverse(sensorToWorldMatrix) * avatarToWorldMatrix * rigToAvatarMatrix; + //qCDebug(interfaceapp) << "AVATAR: " << avatarToWorldMatrix; + //qCDebug(interfaceapp) << "SENSOR " << sensorToWorldMatrix; // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); @@ -282,6 +286,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; + //qCDebug(interfaceapp) << "right eye index is " << geometry.leftEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); updateFingers(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 69356cdfaa..07d1edcabb 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -169,6 +169,8 @@ AABox Avatar::getBounds() const { // approximately 2m tall, scaled to user request. return AABox(getWorldPosition() - glm::vec3(getModelScale()), getModelScale() * 2.0f); } + //float temp = _skeletonModel->getRenderableMeshBound().getLargestDimension(); + //qCDebug(animation) << " largest bounding box dimension " << temp; return _skeletonModel->getRenderableMeshBound(); } @@ -1775,6 +1777,7 @@ void Avatar::buildUnscaledEyeHeightCache() { // Sanity check by looking at the model extents. Extents meshExtents = _skeletonModel->getUnscaledMeshExtents(); float meshHeight = meshExtents.size().y; + //qCDebug(animation) << "mesh height " << meshHeight << " skeleton height " << skeletonHeight; // if we determine the mesh is much larger then the skeleton, then we use the mesh height instead. // This helps prevent absurdly large avatars from exceeding the domain height limit. diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index de8c02f10e..83caaede8d 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -71,14 +71,23 @@ void SkeletonModel::initJointStates() { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; + qCDebug(avatars_renderer) << "initial left and right eyes " << leftEyePosition << " " << rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); + qCDebug(avatars_renderer) << "after setting the left and right eyes " << leftEyePosition << " " << rightEyePosition; glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; int rootJointIndex = geometry.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); + qCDebug(avatars_renderer) << "root joint index " << rootJointIndex << " root position: " << rootModelPosition; _defaultEyeModelPosition = midEyePosition - rootModelPosition; + if (headJointIndex > -1) { + glm::vec3 headModelPosition; + getJointPosition(headJointIndex, headModelPosition); + qCDebug(avatars_renderer) << "we have a head joint " << headJointIndex << " and " << headModelPosition; + } + qCDebug(avatars_renderer) << "the default eye pos " << _defaultEyeModelPosition << " and scale " << _scale; // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; @@ -294,6 +303,16 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD) * headHeight; return true; + } else if (getJointPosition(geometry.headJointIndex, headPosition)) { + glm::vec3 baseEyePosition = headPosition; + glm::quat headRotation; + getJointRotation(geometry.headJointIndex, headRotation); + const float EYES_FORWARD = 0.25f; + const float EYE_SEPARATION = 0.1f; + float headHeight = glm::distance(neckPosition, headPosition); + firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD); + secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD); + return true; } return false; } diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index 787b7bfb1a..c80e14d87c 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -9,6 +9,7 @@ // #include "Camera.h" +//#include "../SharedLogging.h" /**jsdoc *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the From 4395e1fa2693472751cb1d01f64a61887629d30e Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 9 Aug 2018 11:11:58 -0700 Subject: [PATCH 015/207] Safe Landing checkpoint --- interface/src/Application.cpp | 4 +- .../src/octree/OctreePacketProcessor.cpp | 15 ++-- interface/src/octree/OctreePacketProcessor.h | 2 +- interface/src/octree/SafeLanding.cpp | 74 +++++++++++++++++-- interface/src/octree/SafeLanding.h | 11 ++- 5 files changed, 90 insertions(+), 16 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index b2beb5d551..14ee3befc5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5495,6 +5495,7 @@ void Application::update(float deltaTime) { if (!_physicsEnabled) { if (!domainLoadingInProgress) { PROFILE_ASYNC_BEGIN(app, "Scene Loading", ""); + _octreeProcessor.startEntitySequence(); domainLoadingInProgress = true; } @@ -5504,7 +5505,7 @@ void Application::update(float deltaTime) { // Check for flagged EntityData having arrived. auto entityTreeRenderer = getEntities(); if (isServerlessMode() || - (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) { + (_octreeProcessor.isLoadSequenceComplete() )) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; @@ -6166,7 +6167,6 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) { _octreeQuery.setOctreeSizeScale(DEFAULT_OCTREE_SIZE_SCALE); static constexpr float MIN_LOD_ADJUST = -20.0f; _octreeQuery.setBoundaryLevelAdjust(MIN_LOD_ADJUST); - _octreeProcessor.startEntitySequence(); } else { _octreeQuery.setConicalViews(_conicalViews); auto lodManager = DependencyManager::get(); diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index e6a1857189..3f06e1579a 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -112,6 +112,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag auto renderer = qApp->getEntities(); if (renderer) { renderer->processDatagram(*message, sendingNode); + _safeLanding->sequenceNumberReceived(renderer->getLastOctreeMessageSequence()); } } } break; @@ -145,12 +146,16 @@ namespace { } } -bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { - // If we've received the flagged seq # and the current one is >= it. - return _completionSequenceNumber != INVALID_SEQUENCE && - !lessThanWraparound(sequenceNumber, _completionSequenceNumber); -} +//bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { +// // If we've received the flagged seq # and the current one is >= it. +// return _completionSequenceNumber != INVALID_SEQUENCE && +// !lessThanWraparound(sequenceNumber, _completionSequenceNumber); +//} void OctreePacketProcessor::startEntitySequence() { _safeLanding->startEntitySequence(qApp->getEntities()); } + +bool OctreePacketProcessor::isLoadSequenceComplete() const { + return _safeLanding->isLoadSequenceComplete(); +} diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index e0a4429da2..0a1fecaf96 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -26,7 +26,7 @@ public: ~OctreePacketProcessor(); void startEntitySequence(); - bool octreeSequenceIsComplete(int sequenceNumber) const; + bool isLoadSequenceComplete() const; signals: void packetVersionMismatch(); diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 85040e5333..c0f8b0130d 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -11,6 +11,9 @@ #include "SafeLanding.h" #include "EntityTreeRenderer.h" +#include "ModelEntityItem.h" +#include "model-networking/ModelCache.h" +#include "InterfaceLogging.h" const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits::max() + 1; @@ -31,8 +34,6 @@ bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const SafeLanding::SafeLanding() { } -SafeLanding::~SafeLanding() { } - void SafeLanding::startEntitySequence(QSharedPointer entityTreeRenderer) { if (!_trackingEntities) { auto entityTree = entityTreeRenderer->getTree(); @@ -45,7 +46,6 @@ void SafeLanding::startEntitySequence(QSharedPointer entityT &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity); _trackingEntities = true; _sequenceNumbers.clear(); - _isSequenceComplete = false; } } } @@ -70,10 +70,10 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { if (solidTypes.count(entity->getType()) && !entity->getCollisionless()) { _trackedEntities.emplace(entityID, entity); + trackResources(entity); + qCDebug(interfaceapp) << "Tracking entity " << entity->getItemName(); } - } - } } @@ -111,3 +111,67 @@ bool SafeLanding::sequenceNumbersComplete() { } return false; } + +void SafeLanding::trackResources(EntityItemPointer entity) { + if (entity->getType() == EntityTypes::Model) { + ModelEntityItem * modelEntity = std::dynamic_pointer_cast(entity).get(); + QString resourceURL; + QSharedPointer resource; + auto shapeType = modelEntity->getShapeType(); + + if (shapeType == SHAPE_TYPE_COMPOUND) { + resourceURL = modelEntity->getCompoundShapeURL(); + resource = DependencyManager::get()->getCollisionGeometryResource(resourceURL); + } else if (shapeType == SHAPE_TYPE_STATIC_MESH || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) { + resourceURL = modelEntity->getModelURL(); + resource = DependencyManager::get()->getGeometryResource(resourceURL); + } + + if (resource) { + connect(resource.data(), &Resource::loaded, this, &SafeLanding::resourceLoaded); + // Remove it either way: + connect(resource.data(), &Resource::failed, this, &SafeLanding::resourceLoaded); + if (!resource->isLoaded()) { + _trackedURLs.insert(resourceURL); + } + } + } +} + +void SafeLanding::resourceLoaded() { + QObject * sender = QObject::sender(); + if (sender) { + Resource * resource = dynamic_cast(sender); + const QString resourceURL = resource->getURL().toString(); + _trackedURLs.erase(resourceURL); + qCDebug(interfaceapp) << "Removed tracked URL" << resourceURL; + } +} + +bool SafeLanding::isLoadSequenceComplete() { + if (sequenceNumbersComplete() && _trackedURLs.empty()) { + _trackingEntities = false; + _trackedEntities.clear(); + } + + return !_trackingEntities; +} + +void SafeLanding::DebugDumpSequenceIDs() const { + int p = -1; + qCDebug(interfaceapp) << "Sequence set size:" << _sequenceNumbers.size(); + for (auto s: _sequenceNumbers) { + if (p == -1) { + p = s; + qCDebug(interfaceapp) << "First: " << s; + } else { + if (s != p + 1) { + qCDebug(interfaceapp) << "Gap from" << p << "to" << s << "(exclusive)"; + p = s; + } + } + } + if (p != -1) { + qCDebug(interfaceapp) << "Last:" << p; + } +} diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index ba343ad5eb..e32a444784 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -25,26 +25,30 @@ class EntityItemID; class SafeLanding : public QObject { public: SafeLanding(); - ~SafeLanding(); + ~SafeLanding() = default; void startEntitySequence(QSharedPointer entityTreeRenderer); void stopEntitySequence(); void setCompletionSequenceNumbers(int first, int last); void sequenceNumberReceived(int sequenceNumber); - bool isSequenceComplete() const { return _isSequenceComplete; } + bool isLoadSequenceComplete(); private slots: void addTrackedEntity(const EntityItemID& entityID); void deleteTrackedEntity(const EntityItemID& entityID); + void resourceLoaded(); + private: bool sequenceNumbersComplete(); + void trackResources(EntityItemPointer entity); + void DebugDumpSequenceIDs() const; + bool _trackingEntities { false }; EntityTreePointer _entityTree; using EntityMap = std::map; EntityMap _trackedEntities; static constexpr int INVALID_SEQUENCE = -1; - bool _isSequenceComplete { true }; int _initialStart { INVALID_SEQUENCE }; int _initialEnd { INVALID_SEQUENCE }; // final sequence, exclusive. @@ -53,6 +57,7 @@ private: }; std::set _sequenceNumbers; + std::set _trackedURLs; static const int SEQUENCE_MODULO; }; From a3f2fd1f235b014fd672e3b2f61a153103b84312 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Aug 2018 11:33:27 -0700 Subject: [PATCH 016/207] removed some print statements and added the code to use the head parent when the neck is missing, this allows for cauterization --- interface/src/Application.cpp | 5 ----- interface/src/avatar/MyAvatar.cpp | 8 +++++--- interface/src/avatar/MySkeletonModel.cpp | 5 ----- .../avatars-renderer/src/avatars-renderer/Avatar.cpp | 3 --- libraries/render-utils/src/CauterizedModel.cpp | 2 ++ libraries/shared/src/shared/Camera.cpp | 1 - 6 files changed, 7 insertions(+), 17 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8a7a5c610a..0bbd2b1c17 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3064,7 +3064,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { auto myAvatar = getMyAvatar(); boomOffset = myAvatar->getModelScale() * myAvatar->getBoomLength() * -IDENTITY_FORWARD; - //qCDebug(interfaceapp) << "boom offset" << boomOffset; // The render mode is default or mirror if the camera is in mirror mode, assigned further below renderArgs._renderMode = RenderArgs::DEFAULT_RENDER_MODE; @@ -3075,12 +3074,10 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _thirdPersonHMDCameraBoomValid= false; if (isHMDMode()) { mat4 camMat = myAvatar->getSensorToWorldMatrix() * myAvatar->getHMDSensorMatrix(); - //qCDebug(interfaceapp) << "camera 1rst translation hmd " << extractTranslation(camMat); _myCamera.setPosition(extractTranslation(camMat)); _myCamera.setOrientation(glmExtractRotation(camMat)); } else { - //qCDebug(interfaceapp) << "camera 1rst translation desktop " << myAvatar->getDefaultEyePosition(); _myCamera.setPosition(myAvatar->getDefaultEyePosition()); _myCamera.setOrientation(myAvatar->getMyHead()->getHeadOrientation()); } @@ -3102,7 +3099,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setOrientation(glm::normalize(glmExtractRotation(worldCameraMat))); _myCamera.setPosition(extractTranslation(worldCameraMat)); - //qCDebug(interfaceapp) << "camera translation 3rd hmd " << extractTranslation(worldCameraMat); } else { _thirdPersonHMDCameraBoomValid = false; @@ -3116,7 +3112,6 @@ void Application::updateCamera(RenderArgs& renderArgs, float deltaTime) { _myCamera.setPosition(myAvatar->getDefaultEyePosition() + myAvatar->getWorldOrientation() * boomOffset); } - qCDebug(interfaceapp) << "camera translation 3rd desktop " << (myAvatar->getDefaultEyePosition() + _myCamera.getOrientation() * boomOffset); } } else if (_myCamera.getMode() == CAMERA_MODE_MIRROR) { diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 9971f90bd6..8ba0841196 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1704,10 +1704,8 @@ glm::vec3 MyAvatar::getSkeletonPosition() const { // The avatar is rotated PI about the yAxis, so we have to correct for it // to get the skeleton offset contribution in the world-frame. const glm::quat FLIP = glm::angleAxis(PI, glm::vec3(0.0f, 1.0f, 0.0f)); - //qCDebug(interfaceapp) << "position third person " << getWorldPosition(); return getWorldPosition() + getWorldOrientation() * FLIP * _skeletonOffset; } - //qCDebug(interfaceapp) << "position first person " << Avatar::getWorldPosition(); return Avatar::getWorldPosition(); } @@ -1992,7 +1990,11 @@ void MyAvatar::initHeadBones() { neckJointIndex = _skeletonModel->getFBXGeometry().neckJointIndex; } if (neckJointIndex == -1) { - return; + neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); + if (neckJointIndex < 0) { + //return if the head is not even there. can't cauterize!! + return; + } } _headBoneSet.clear(); std::queue q; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 86085798ab..0fc5e7521e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -95,9 +95,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // make sure lookAt is not too close to face (avoid crosseyes) glm::vec3 lookAt = head->getLookAtPosition(); - //qCDebug(interfaceapp) << "look at " << lookAt; glm::vec3 focusOffset = lookAt - _owningAvatar->getHead()->getEyePosition(); - //qCDebug(interfaceapp) << "focus offset " << focusOffset; float focusDistance = glm::length(focusOffset); const float MIN_LOOK_AT_FOCUS_DISTANCE = 1.0f; if (focusDistance < MIN_LOOK_AT_FOCUS_DISTANCE && focusDistance > EPSILON) { @@ -115,8 +113,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { glm::mat4 avatarToWorldMatrix = createMatFromQuatAndPos(myAvatar->getWorldOrientation(), myAvatar->getWorldPosition()); glm::mat4 sensorToWorldMatrix = myAvatar->getSensorToWorldMatrix(); params.rigToSensorMatrix = glm::inverse(sensorToWorldMatrix) * avatarToWorldMatrix * rigToAvatarMatrix; - //qCDebug(interfaceapp) << "AVATAR: " << avatarToWorldMatrix; - //qCDebug(interfaceapp) << "SENSOR " << sensorToWorldMatrix; // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); @@ -286,7 +282,6 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { eyeParams.leftEyeJointIndex = geometry.leftEyeJointIndex; eyeParams.rightEyeJointIndex = geometry.rightEyeJointIndex; - //qCDebug(interfaceapp) << "right eye index is " << geometry.leftEyeJointIndex; _rig.updateFromEyeParameters(eyeParams); updateFingers(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 07d1edcabb..69356cdfaa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -169,8 +169,6 @@ AABox Avatar::getBounds() const { // approximately 2m tall, scaled to user request. return AABox(getWorldPosition() - glm::vec3(getModelScale()), getModelScale() * 2.0f); } - //float temp = _skeletonModel->getRenderableMeshBound().getLargestDimension(); - //qCDebug(animation) << " largest bounding box dimension " << temp; return _skeletonModel->getRenderableMeshBound(); } @@ -1777,7 +1775,6 @@ void Avatar::buildUnscaledEyeHeightCache() { // Sanity check by looking at the model extents. Extents meshExtents = _skeletonModel->getUnscaledMeshExtents(); float meshHeight = meshExtents.size().y; - //qCDebug(animation) << "mesh height " << meshHeight << " skeleton height " << skeletonHeight; // if we determine the mesh is much larger then the skeleton, then we use the mesh height instead. // This helps prevent absurdly large avatars from exceeding the domain height limit. diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 80a9c5ccae..b5b8c039a1 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -139,6 +139,8 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; + // qCDebug(renderutils) << "cauterize matrix: " << cauterizeMatrix; + for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); diff --git a/libraries/shared/src/shared/Camera.cpp b/libraries/shared/src/shared/Camera.cpp index c80e14d87c..787b7bfb1a 100644 --- a/libraries/shared/src/shared/Camera.cpp +++ b/libraries/shared/src/shared/Camera.cpp @@ -9,7 +9,6 @@ // #include "Camera.h" -//#include "../SharedLogging.h" /**jsdoc *

Camera modes affect the position of the camera and the controls for camera movement. The camera can be in one of the From f26d6b8c3dc30957477271987647e4f60c96a708 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Aug 2018 12:15:35 -0700 Subject: [PATCH 017/207] cleaned up code and changed the forward offset for the default eyes when there is no neck bone --- interface/src/avatar/MyAvatar.cpp | 2 +- .../src/avatars-renderer/SkeletonModel.cpp | 15 +++------------ libraries/render-utils/src/CauterizedModel.cpp | 2 -- 3 files changed, 4 insertions(+), 15 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8ba0841196..d456028bbe 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1992,7 +1992,7 @@ void MyAvatar::initHeadBones() { if (neckJointIndex == -1) { neckJointIndex = (_skeletonModel->getFBXGeometry().headJointIndex - 1); if (neckJointIndex < 0) { - //return if the head is not even there. can't cauterize!! + // return if the head is not even there. can't cauterize!! return; } } diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 83caaede8d..11bad6c1b8 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -71,23 +71,14 @@ void SkeletonModel::initJointStates() { qCWarning(avatars_renderer) << "Bad head joint! Got:" << headJointIndex << "jointCount:" << _rig.getJointStateCount(); } glm::vec3 leftEyePosition, rightEyePosition; - qCDebug(avatars_renderer) << "initial left and right eyes " << leftEyePosition << " " << rightEyePosition; getEyeModelPositions(leftEyePosition, rightEyePosition); - qCDebug(avatars_renderer) << "after setting the left and right eyes " << leftEyePosition << " " << rightEyePosition; glm::vec3 midEyePosition = (leftEyePosition + rightEyePosition) / 2.0f; int rootJointIndex = geometry.rootJointIndex; glm::vec3 rootModelPosition; getJointPosition(rootJointIndex, rootModelPosition); - qCDebug(avatars_renderer) << "root joint index " << rootJointIndex << " root position: " << rootModelPosition; _defaultEyeModelPosition = midEyePosition - rootModelPosition; - if (headJointIndex > -1) { - glm::vec3 headModelPosition; - getJointPosition(headJointIndex, headModelPosition); - qCDebug(avatars_renderer) << "we have a head joint " << headJointIndex << " and " << headModelPosition; - } - qCDebug(avatars_renderer) << "the default eye pos " << _defaultEyeModelPosition << " and scale " << _scale; // Skeleton may have already been scaled so unscale it _defaultEyeModelPosition = _defaultEyeModelPosition / _scale; @@ -307,11 +298,11 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& glm::vec3 baseEyePosition = headPosition; glm::quat headRotation; getJointRotation(geometry.headJointIndex, headRotation); - const float EYES_FORWARD = 0.25f; + const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; float headHeight = glm::distance(neckPosition, headPosition); - firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD); - secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD); + firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); + secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); return true; } return false; diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index b5b8c039a1..80a9c5ccae 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -139,8 +139,6 @@ void CauterizedModel::updateClusterMatrices() { glm::vec4(0.0f, 0.0f, 0.0f, 1.0f)); auto cauterizeMatrix = _rig.getJointTransform(geometry.neckJointIndex) * zeroScale; - // qCDebug(renderutils) << "cauterize matrix: " << cauterizeMatrix; - for (int i = 0; i < _cauterizeMeshStates.size(); i++) { Model::MeshState& state = _cauterizeMeshStates[i]; const FBXMesh& mesh = geometry.meshes.at(i); From 93baad73873eb1de6fd0745dd6d430464563293d Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 9 Aug 2018 14:35:44 -0700 Subject: [PATCH 018/207] removed unnecessary computation of head height from eye position code --- .../avatars-renderer/src/avatars-renderer/SkeletonModel.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp index 11bad6c1b8..1ec58fd704 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/SkeletonModel.cpp @@ -300,7 +300,6 @@ bool SkeletonModel::getEyeModelPositions(glm::vec3& firstEyePosition, glm::vec3& getJointRotation(geometry.headJointIndex, headRotation); const float EYES_FORWARD_HEAD_ONLY = 0.30f; const float EYE_SEPARATION = 0.1f; - float headHeight = glm::distance(neckPosition, headPosition); firstEyePosition = baseEyePosition + headRotation * glm::vec3(EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); secondEyePosition = baseEyePosition + headRotation * glm::vec3(-EYE_SEPARATION, 0.0f, EYES_FORWARD_HEAD_ONLY); return true; From 875407b3fef3a85b3525abd0bd0f8acc2bc271cc Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Thu, 9 Aug 2018 16:24:04 -0700 Subject: [PATCH 019/207] New approach to physics readiness - now (mostly) working --- interface/src/Application.cpp | 3 +- .../src/octree/OctreePacketProcessor.cpp | 22 +----- interface/src/octree/OctreePacketProcessor.h | 5 -- interface/src/octree/SafeLanding.cpp | 73 ++++++++----------- interface/src/octree/SafeLanding.h | 7 +- 5 files changed, 34 insertions(+), 76 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 14ee3befc5..cc2c7f601a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6322,10 +6322,11 @@ void Application::clearDomainOctreeDetails() { _octreeServerSceneStats.clear(); }); - _octreeProcessor.resetCompletionSequenceNumber(); // reset the model renderer getEntities()->clear(); + _octreeProcessor.startEntitySequence(); + auto skyStage = DependencyManager::get()->getSkyStage(); skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_DEFAULT); diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 3f06e1579a..6dab37b1a1 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -122,7 +122,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag OCTREE_PACKET_SEQUENCE completionNumber; message->readPrimitive(&completionNumber); - _completionSequenceNumber = completionNumber; + completionNumber; _safeLanding->setCompletionSequenceNumbers(0, completionNumber); } break; @@ -132,26 +132,6 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag } } -void OctreePacketProcessor::resetCompletionSequenceNumber() { - _completionSequenceNumber = INVALID_SEQUENCE; -} - -namespace { - template bool lessThanWraparound(int a, int b) { - constexpr int MAX_T_VALUE = std::numeric_limits::max(); - if (b <= a) { - b += MAX_T_VALUE; - } - return (b - a) < (MAX_T_VALUE / 2); - } -} - -//bool OctreePacketProcessor::octreeSequenceIsComplete(int sequenceNumber) const { -// // If we've received the flagged seq # and the current one is >= it. -// return _completionSequenceNumber != INVALID_SEQUENCE && -// !lessThanWraparound(sequenceNumber, _completionSequenceNumber); -//} - void OctreePacketProcessor::startEntitySequence() { _safeLanding->startEntitySequence(qApp->getEntities()); } diff --git a/interface/src/octree/OctreePacketProcessor.h b/interface/src/octree/OctreePacketProcessor.h index 0a1fecaf96..f9c24ddc51 100644 --- a/interface/src/octree/OctreePacketProcessor.h +++ b/interface/src/octree/OctreePacketProcessor.h @@ -31,9 +31,6 @@ public: signals: void packetVersionMismatch(); -public slots: - void resetCompletionSequenceNumber(); - protected: virtual void processPacket(QSharedPointer message, SharedNodePointer sendingNode) override; @@ -41,8 +38,6 @@ private slots: void handleOctreePacket(QSharedPointer message, SharedNodePointer senderNode); private: - static constexpr int INVALID_SEQUENCE = -1; - std::atomic _completionSequenceNumber { INVALID_SEQUENCE }; std::unique_ptr _safeLanding; }; #endif // hifi_OctreePacketProcessor_h diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index c0f8b0130d..352c83dad0 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -12,7 +12,6 @@ #include "SafeLanding.h" #include "EntityTreeRenderer.h" #include "ModelEntityItem.h" -#include "model-networking/ModelCache.h" #include "InterfaceLogging.h" const int SafeLanding::SEQUENCE_MODULO = std::numeric_limits::max() + 1; @@ -31,9 +30,6 @@ bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const return lessThanWraparound(a, b); } -SafeLanding::SafeLanding() { -} - void SafeLanding::startEntitySequence(QSharedPointer entityTreeRenderer) { if (!_trackingEntities) { auto entityTree = entityTreeRenderer->getTree(); @@ -59,19 +55,20 @@ void SafeLanding::stopEntitySequence() { } void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { - volatile EntityItemID id = entityID; if (_trackingEntities) { EntityItemPointer entity = _entityTree->findEntityByID(entityID); - if (entity) { - const auto& entityType = entity->getType(); - // Entity types of interest: - static const std::set solidTypes - { EntityTypes::Box, EntityTypes::Sphere, EntityTypes::Shape, EntityTypes::Model }; - if (solidTypes.count(entity->getType()) && !entity->getCollisionless()) { - _trackedEntities.emplace(entityID, entity); - trackResources(entity); - qCDebug(interfaceapp) << "Tracking entity " << entity->getItemName(); + if (entity && !entity->getCollisionless()) { + const auto& entityType = entity->getType(); + if (entityType == EntityTypes::Model) { + ModelEntityItem * modelEntity = std::dynamic_pointer_cast(entity).get(); + auto shapeType = modelEntity->getShapeType(); + if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_STATIC_MESH || + shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) { + // Only track entities with downloaded collision bodies. + _trackedEntities.emplace(entityID, entity); + qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); + } } } } @@ -112,58 +109,46 @@ bool SafeLanding::sequenceNumbersComplete() { return false; } -void SafeLanding::trackResources(EntityItemPointer entity) { - if (entity->getType() == EntityTypes::Model) { - ModelEntityItem * modelEntity = std::dynamic_pointer_cast(entity).get(); - QString resourceURL; - QSharedPointer resource; - auto shapeType = modelEntity->getShapeType(); - - if (shapeType == SHAPE_TYPE_COMPOUND) { - resourceURL = modelEntity->getCompoundShapeURL(); - resource = DependencyManager::get()->getCollisionGeometryResource(resourceURL); - } else if (shapeType == SHAPE_TYPE_STATIC_MESH || shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) { - resourceURL = modelEntity->getModelURL(); - resource = DependencyManager::get()->getGeometryResource(resourceURL); - } - - if (resource) { - connect(resource.data(), &Resource::loaded, this, &SafeLanding::resourceLoaded); - // Remove it either way: - connect(resource.data(), &Resource::failed, this, &SafeLanding::resourceLoaded); - if (!resource->isLoaded()) { - _trackedURLs.insert(resourceURL); - } - } - } -} - void SafeLanding::resourceLoaded() { QObject * sender = QObject::sender(); if (sender) { Resource * resource = dynamic_cast(sender); const QString resourceURL = resource->getURL().toString(); _trackedURLs.erase(resourceURL); - qCDebug(interfaceapp) << "Removed tracked URL" << resourceURL; + qCDebug(interfaceapp) << "Safe Landing: Removed tracked URL" << resourceURL; } } bool SafeLanding::isLoadSequenceComplete() { - if (sequenceNumbersComplete() && _trackedURLs.empty()) { + if (sequenceNumbersComplete() && entityPhysicsComplete()) { _trackingEntities = false; _trackedEntities.clear(); + qCDebug(interfaceapp) << "Safe Landing: load sequence complete"; } return !_trackingEntities; } -void SafeLanding::DebugDumpSequenceIDs() const { +bool SafeLanding::entityPhysicsComplete() { + for (auto entityMapIter = _trackedEntities.begin(); entityMapIter != _trackedEntities.end(); ++entityMapIter) { + auto entity = entityMapIter->second; + if (!entity->shouldBePhysical() || entity->isReadyToComputeShape()) { + entityMapIter = _trackedEntities.erase(entityMapIter); + if (entityMapIter == _trackedEntities.end()) { + break; + } + } + } + return _trackedEntities.empty(); +} + +void SafeLanding::debugDumpSequenceIDs() const { int p = -1; qCDebug(interfaceapp) << "Sequence set size:" << _sequenceNumbers.size(); for (auto s: _sequenceNumbers) { if (p == -1) { p = s; - qCDebug(interfaceapp) << "First: " << s; + qCDebug(interfaceapp) << "First:" << s; } else { if (s != p + 1) { qCDebug(interfaceapp) << "Gap from" << p << "to" << s << "(exclusive)"; diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index e32a444784..e81746e077 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -24,9 +24,6 @@ class EntityItemID; class SafeLanding : public QObject { public: - SafeLanding(); - ~SafeLanding() = default; - void startEntitySequence(QSharedPointer entityTreeRenderer); void stopEntitySequence(); void setCompletionSequenceNumbers(int first, int last); @@ -40,8 +37,8 @@ private slots: private: bool sequenceNumbersComplete(); - void trackResources(EntityItemPointer entity); - void DebugDumpSequenceIDs() const; + void debugDumpSequenceIDs() const; + bool entityPhysicsComplete(); bool _trackingEntities { false }; EntityTreePointer _entityTree; From a85ed2a2f55e1edcaa352f1d91f196261291dcd8 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Mon, 6 Aug 2018 22:35:20 +0300 Subject: [PATCH 020/207] show 'edit wearables' button even if avatar doesn't have any wearables --- interface/resources/qml/hifi/AvatarApp.qml | 23 --------------- .../qml/hifi/avatarapp/MessageBoxes.qml | 29 ------------------- 2 files changed, 52 deletions(-) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index b7e1adda70..bd3f8fd1cf 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -491,33 +491,10 @@ Rectangle { anchors.verticalCenter: wearablesLabel.verticalCenter glyphText: "\ue02e" - visible: avatarWearablesCount !== 0 onClicked: { adjustWearables.open(currentAvatar); } } - - // TextStyle3 - RalewayRegular { - size: 22; - anchors.right: parent.right - anchors.verticalCenter: wearablesLabel.verticalCenter - font.underline: true - text: "Add" - color: 'black' - visible: avatarWearablesCount === 0 - - MouseArea { - anchors.fill: parent - onClicked: { - popup.showGetWearables(function() { - emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'}) - }, function(link) { - emitSendToScript({'method' : 'navigate', 'url' : link}) - }); - } - } - } } Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 90f55fd8bb..c9b57abe66 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -33,35 +33,6 @@ MessageBox { popup.inputText.forceActiveFocus(); } - property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' - - function showGetWearables(callback, linkCallback) { - popup.button2text = 'AvatarIsland' - popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; - popup.button1text = 'CANCEL' - popup.titleText = 'Get Wearables' - popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearables from My Purchases.' + '
' + '
' + - 'Visit “AvatarIsland” to get wearables' - - popup.imageSource = getWearablesUrl; - popup.onButton2Clicked = function() { - popup.close(); - - if(callback) - callback(); - } - - popup.onLinkClicked = function(link) { - popup.close(); - - if(linkCallback) - linkCallback(link); - } - - popup.open(); - } - function showDeleteFavorite(favoriteName, callback) { popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' From f8c1ba48ad19d4938a128e281e1e8dbe28adc806 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Mon, 6 Aug 2018 23:55:06 +0300 Subject: [PATCH 021/207] update UI accordingly to the updated mockup --- .../qml/hifi/avatarapp/AdjustWearables.qml | 176 ++++++++++++------ 1 file changed, 119 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 39344b04bc..909f7c886f 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -115,64 +115,111 @@ Rectangle { Column { anchors.top: parent.top - anchors.topMargin: 15 + anchors.topMargin: 12 anchors.horizontalCenter: parent.horizontalCenter spacing: 20 - width: parent.width - 30 * 2 + width: parent.width - 22 * 2 - HifiControlsUit.ComboBox { - id: wearablesCombobox - anchors.left: parent.left - anchors.right: parent.right - comboBox.textRole: "text" + Column { + width: parent.width - model: ListModel { - function findIndexById(id) { + Row { + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Wearable" + anchors.verticalCenter: parent.verticalCenter + } - for(var i = 0; i < count; ++i) { - var wearable = get(i); - if(wearable.id === id) { - return i; - } - } + spacing: 10 - return -1; + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Add custom" + linkColor: hifi.colors.blueHighlight + anchors.verticalCenter: parent.verticalCenter } } - comboBox.onCurrentIndexChanged: { - var currentWearable = getCurrentWearable(); + HifiControlsUit.ComboBox { + id: wearablesCombobox + anchors.left: parent.left + anchors.right: parent.right + comboBox.textRole: "text" - if(currentWearable) { - position.set(currentWearable.localPosition); - rotation.set(currentWearable.localRotationAngles); - scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) + model: ListModel { + function findIndexById(id) { - wearableSelected(currentWearable.id); + for(var i = 0; i < count; ++i) { + var wearable = get(i); + if(wearable.id === id) { + return i; + } + } + + return -1; + } + } + + comboBox.onCurrentIndexChanged: { + var currentWearable = getCurrentWearable(); + + if(currentWearable) { + position.set(currentWearable.localPosition); + rotation.set(currentWearable.localRotationAngles); + scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) + + wearableSelected(currentWearable.id); + } + } + } + } + + Column { + width: parent.width + + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Joint" + } + + HifiControlsUit.ComboBox { + id: jointsCombobox + anchors.left: parent.left + anchors.right: parent.right + comboBox.textRole: "text" + + model: ListModel { } } } Column { width: parent.width - spacing: 5 Row { spacing: 20 // TextStyle5 - FiraSansSemiBold { + RalewayBold { id: positionLabel - size: 22; + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; text: "Position" } // TextStyle7 - FiraSansRegular { - size: 18; + RalewayBold { + size: 15; lineHeightMode: Text.FixedHeight - lineHeight: 16.9; + lineHeight: 18; text: "m" anchors.verticalCenter: positionLabel.verticalCenter } @@ -214,23 +261,24 @@ Rectangle { Column { width: parent.width - spacing: 5 Row { spacing: 20 // TextStyle5 - FiraSansSemiBold { + RalewayBold { id: rotationLabel - size: 22; + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; text: "Rotation" } // TextStyle7 - FiraSansRegular { - size: 18; + RalewayBold { + size: 15; lineHeightMode: Text.FixedHeight - lineHeight: 16.9; + lineHeight: 18; text: "deg" anchors.verticalCenter: rotationLabel.verticalCenter } @@ -270,19 +318,30 @@ Rectangle { } } - Column { + Item { width: parent.width - spacing: 5 + height: childrenRect.height - // TextStyle5 - FiraSansSemiBold { - size: 22; - text: "Scale" + HifiControlsUit.CheckBox { + text: "Is soft" + labelFontSize: 15 + labelFontWeight: Font.Bold + color: Qt.black + y: scalespinner.y } - Item { - width: parent.width - height: childrenRect.height + Column { + id: scalesColumn + anchors.right: parent.right + + // TextStyle5 + RalewayBold { + id: scaleLabel + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Scale" + } HifiControlsUit.SpinBox { id: scalespinner @@ -320,26 +379,29 @@ Rectangle { wearableUpdated(currentWearable.id, wearablesCombobox.currentIndex, properties); } } - - HifiControlsUit.Button { - fontSize: 18 - height: 40 - anchors.right: parent.right - color: hifi.buttons.red; - colorScheme: hifi.colorSchemes.light; - text: "TAKE IT OFF" - onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id); - enabled: wearablesCombobox.model.count !== 0 - anchors.verticalCenter: scalespinner.verticalCenter - } } + } + Column { + width: parent.width + + HifiControlsUit.Button { + fontSize: 18 + height: 40 + width: scalespinner.width + anchors.right: parent.right + color: hifi.buttons.red; + colorScheme: hifi.colorSchemes.light; + text: "TAKE IT OFF" + onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id); + enabled: wearablesCombobox.model.count !== 0 + } } } DialogButtons { anchors.bottom: parent.bottom - anchors.bottomMargin: 30 + anchors.bottomMargin: 57 anchors.left: parent.left anchors.leftMargin: 30 anchors.right: parent.right From 550e39ac8498a79a6d6cf9f49ece488bddb179e0 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 8 Aug 2018 21:41:10 +0300 Subject: [PATCH 022/207] add possibility to change joints using combobox --- .../qml/hifi/avatarapp/AdjustWearables.qml | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 909f7c886f..8ea3827f9c 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -42,7 +42,7 @@ Rectangle { var wearable = avatar.wearables.get(i).properties; for(var j = (wearable.modelURL.length - 1); j >= 0; --j) { if(wearable.modelURL[j] === '/') { - wearable.text = wearable.modelURL.substring(j + 1) + ' [%jointIndex%]'.replace('%jointIndex%', jointNames[wearable.parentJointIndex]); + wearable.text = wearable.modelURL.substring(j + 1); break; } } @@ -50,6 +50,9 @@ Rectangle { } wearablesCombobox.currentIndex = 0; + if(wearablesCombobox.model.count !== 0) { + jointsCombobox.set(getCurrentWearable().parentJointIndex) + } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { @@ -172,6 +175,7 @@ Rectangle { position.set(currentWearable.localPosition); rotation.set(currentWearable.localRotationAngles); scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) + jointsCombobox.set(currentWearable.parentJointIndex) wearableSelected(currentWearable.id); } @@ -193,9 +197,32 @@ Rectangle { id: jointsCombobox anchors.left: parent.left anchors.right: parent.right - comboBox.textRole: "text" - model: ListModel { + model: jointNames + property bool notify: false + + function set(jointIndex) { + notify = false; + currentIndex = jointIndex; + notify = true; + } + + function notifyJointChanged() { + modified = true; + var properties = { + parentJointIndex: currentIndex, + localPosition: { + x: position.xvalue, + y: position.yvalue, + z: position.zvalue + } + }; + + wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties); + } + + onCurrentIndexChanged: { + if(notify) notifyJointChanged(); } } } From da68a8906eb9bde610058015439cf0ef46a0a809 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 8 Aug 2018 23:56:01 +0300 Subject: [PATCH 023/207] show displayText if specified (to allow override combobox current value) --- interface/resources/qml/controls-uit/ComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index be8c9f6740..245b565a62 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -103,7 +103,7 @@ FocusScope { verticalCenter: parent.verticalCenter } size: hifi.fontSizes.textFieldInput - text: comboBox.currentText + text: comboBox.displayText ? comboBox.displayText : comboBox.currentText elide: Text.ElideRight color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) } From 56194bba8e69d8946543f2fb42af52fc303928f6 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 8 Aug 2018 23:57:08 +0300 Subject: [PATCH 024/207] bind 'is soft' to backend API, always show 'Hips' for soften entity --- .../qml/hifi/avatarapp/AdjustWearables.qml | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 8ea3827f9c..158757770c 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -48,11 +48,7 @@ Rectangle { } wearablesCombobox.model.append(wearable); } - wearablesCombobox.currentIndex = 0; - if(wearablesCombobox.model.count !== 0) { - jointsCombobox.set(getCurrentWearable().parentJointIndex) - } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { @@ -176,6 +172,7 @@ Rectangle { rotation.set(currentWearable.localRotationAngles); scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) jointsCombobox.set(currentWearable.parentJointIndex) + isSoft.set(currentWearable.relayParentJoints) wearableSelected(currentWearable.id); } @@ -197,6 +194,8 @@ Rectangle { id: jointsCombobox anchors.left: parent.left anchors.right: parent.right + enabled: !isSoft.checked + comboBox.displayText: isSoft.checked ? 'Hips' : comboBox.currentText model: jointNames property bool notify: false @@ -350,11 +349,31 @@ Rectangle { height: childrenRect.height HifiControlsUit.CheckBox { + id: isSoft text: "Is soft" labelFontSize: 15 labelFontWeight: Font.Bold color: Qt.black y: scalespinner.y + + function set(value) { + notify = false; + checked = value + notify = true; + } + + function notifyIsSoftChanged() { + modified = true; + var properties = { + relayParentJoints: checked + }; + + wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties); + } + + property bool notify: false; + + onCheckedChanged: if(notify) notifyIsSoftChanged(); } Column { From 0a28361dc8cb866fbdd2dfd0f5acef4240a85f93 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 9 Aug 2018 01:22:11 +0300 Subject: [PATCH 025/207] treat ModelEntityItem-s with relayParentJoints as wearables --- interface/src/AvatarBookmarks.cpp | 7 ++++--- scripts/system/avatarapp.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 3e0e643bd8..0400beb39c 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ #include "QVariantGLM.h" #include - +#include void addAvatarEntities(const QVariantList& avatarEntities) { auto nodeList = DependencyManager::get(); @@ -145,8 +146,8 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { } bool isWearableEntity(const EntityItemPointer& entity) { - return entity->isVisible() && entity->getParentJointIndex() != INVALID_JOINT_INDEX && - (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); + return entity->isVisible() && (entity->getParentJointIndex() != INVALID_JOINT_INDEX || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) + && (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); } void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 03b7b3969d..4bc4d1a762 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -31,7 +31,7 @@ function executeLater(callback) { var INVALID_JOINT_INDEX = -1 function isWearable(avatarEntity) { - return avatarEntity.properties.visible === true && avatarEntity.properties.parentJointIndex !== INVALID_JOINT_INDEX && + return avatarEntity.properties.visible === true && (avatarEntity.properties.parentJointIndex !== INVALID_JOINT_INDEX || avatarEntity.properties.relayParentJoints === true) && (avatarEntity.properties.parentID === MyAvatar.sessionUUID || avatarEntity.properties.parentID === MyAvatar.SELF_ID); } From e900e034a36fd1d52b1db6827dc92be00c41cd6b Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 9 Aug 2018 02:21:27 +0300 Subject: [PATCH 026/207] couple Spinbox fixes: - stop resetting value in 'valueFromText'. The issue with such approach is that is is not possible to understand whether value was really changed or, for example, focus did change - instead, propagate validator to TextInput which makes not possible to type bad text (which eliminate the reason for original fix) - specify correct inputMethodHints to guide onscreen keyboards --- interface/resources/qml/controls-uit/SpinBox.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index f3ae3c5d6e..3d3ea7a75e 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -21,6 +21,7 @@ SpinBox { id: hifi } + inputMethodHints: Qt.ImhFormattedNumbersOnly property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light property string label: "" @@ -91,7 +92,6 @@ SpinBox { } valueFromText: function(text, locale) { - spinBox.value = 0; // Force valueChanged signal to be emitted so that validator fires. return Number.fromLocaleString(locale, text)*factor; } @@ -104,6 +104,8 @@ SpinBox { selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix + inputMethodHints: spinBox.inputMethodHints + validator: spinBox.validator verticalAlignment: Qt.AlignVCenter leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding //rightPadding: hifi.dimensions.spinnerSize From 1771e4c74232f3ca3b93f45c42c95cf398e6f188 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 9 Aug 2018 02:22:35 +0300 Subject: [PATCH 027/207] better handling of 'modified' inside AdjustWearables dialog --- .../resources/qml/hifi/avatarapp/AdjustWearables.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 158757770c..5f42bb4556 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -30,6 +30,7 @@ Rectangle { function open(avatar) { adjustWearablesOpened(avatar.name); + modified = false; visible = true; avatarName = avatar.name; wearablesModel = avatar.wearables; @@ -401,7 +402,7 @@ Rectangle { colorScheme: hifi.colorSchemes.light property bool notify: false; - onValueChanged: if(notify) notifyScaleChanged(); + onRealValueChanged: if(notify) notifyScaleChanged(); function set(value) { notify = false; @@ -439,13 +440,18 @@ Rectangle { color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; text: "TAKE IT OFF" - onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id); + onClicked: { + modified = true; + wearableDeleted(root.avatarName, getCurrentWearable().id); + } enabled: wearablesCombobox.model.count !== 0 } } } DialogButtons { + yesButton.enabled: modified + anchors.bottom: parent.bottom anchors.bottomMargin: 57 anchors.left: parent.left From 9234813e0835c04dd47f153205a742193d4318a7 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 14:54:15 +0300 Subject: [PATCH 028/207] bring back 'get more' wearables button --- .../qml/hifi/avatarapp/AdjustWearables.qml | 30 +++++++++++++++++-- .../qml/hifi/avatarapp/MessageBoxes.qml | 29 ++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 5f42bb4556..e99fdfd243 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -1,5 +1,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 +import QtQuick.Layouts 1.3 import "../../styles-uit" import "../../controls-uit" as HifiControlsUit import "../../controls" as HifiControls @@ -124,7 +125,10 @@ Rectangle { Column { width: parent.width - Row { + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + RalewayBold { size: 15; lineHeightMode: Text.FixedHeight @@ -139,9 +143,31 @@ Rectangle { size: 15; lineHeightMode: Text.FixedHeight lineHeight: 18; - text: "Add custom" + text: "Get more" linkColor: hifi.colors.blueHighlight anchors.verticalCenter: parent.verticalCenter + onLinkActivated: { + popup.showGetWearables(function() { + emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'}) + }, function(link) { + emitSendToScript({'method' : 'navigate', 'url' : link}) + }); + } + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Add custom" + linkColor: hifi.colors.blueHighlight + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + } } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index c9b57abe66..a381da7d0b 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -33,6 +33,35 @@ MessageBox { popup.inputText.forceActiveFocus(); } + property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' + + function showGetWearables(callback, linkCallback) { + popup.button2text = 'AvatarIsland' + popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; + popup.button1text = 'CANCEL' + popup.titleText = 'Get Wearables' + popup.bodyText = 'Buy wearables from Marketplace.' + '
' + + 'Wear wearables from My Purchases.' + '
' + '
' + + 'Visit “AvatarIsland” to get wearables' + + popup.imageSource = getWearablesUrl; + popup.onButton2Clicked = function() { + popup.close(); + + if(callback) + callback(); + } + + popup.onLinkClicked = function(link) { + popup.close(); + + if(linkCallback) + linkCallback(link); + } + + popup.open(); + } + function showDeleteFavorite(favoriteName, callback) { popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' From 90fb0d7be6a40ef3fe36f5c2f62cc56f62c68150 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 17:59:11 +0300 Subject: [PATCH 029/207] add possibility to add wearable from external url --- interface/resources/qml/hifi/AvatarApp.qml | 3 +++ .../qml/hifi/avatarapp/AdjustWearables.qml | 9 +++++++ .../qml/hifi/avatarapp/MessageBoxes.qml | 20 ++++++++++++++ scripts/system/avatarapp.js | 27 +++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index bd3f8fd1cf..217525498d 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -285,6 +285,9 @@ Rectangle { onWearableSelected: { emitSendToScript({'method' : 'selectWearable', 'entityID' : id}); } + onAddWearable: { + emitSendToScript({'method' : 'addWearable', 'avatarName' : avatarName, 'url' : url}); + } z: 3 } diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index e99fdfd243..27ae590fc5 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -18,6 +18,7 @@ Rectangle { signal adjustWearablesOpened(var avatarName); signal adjustWearablesClosed(bool status, var avatarName); + signal addWearable(string avatarName, string url); property bool modified: false; Component.onCompleted: { @@ -40,6 +41,8 @@ Rectangle { function refresh(avatar) { wearablesCombobox.model.clear(); + wearablesCombobox.currentIndex = -1; + for(var i = 0; i < avatar.wearables.count; ++i) { var wearable = avatar.wearables.get(i).properties; for(var j = (wearable.modelURL.length - 1); j >= 0; --j) { @@ -167,6 +170,12 @@ Rectangle { linkColor: hifi.colors.blueHighlight anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right + onLinkActivated: { + popup.showSpecifyWearableUrl(function(url) { + console.debug('popup.showSpecifyWearableUrl: ', url); + addWearable(root.avatarName, url); + }); + } } } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index a381da7d0b..64c2d478c0 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -33,6 +33,26 @@ MessageBox { popup.inputText.forceActiveFocus(); } + function showSpecifyWearableUrl(callback) { + popup.button2text = 'CONFIRM' + popup.button1text = 'CANCEL' + popup.titleText = 'Specify Wearable URL' + popup.bodyText = 'If you want to add a custom wearable, you can specify the URL of the wearable file here.' + + popup.inputText.visible = true; + popup.inputText.placeholderText = 'Enter Wearable URL'; + + popup.onButton2Clicked = function() { + if(callback) + callback(popup.inputText.text); + + popup.close(); + } + + popup.open(); + popup.inputText.forceActiveFocus(); + } + property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' function showGetWearables(callback, linkCallback) { diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 4bc4d1a762..38b6d2447c 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -235,6 +235,33 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See Messages.messageReceived.disconnect(handleWearableMessages); Messages.unsubscribe('Hifi-Object-Manipulation'); break; + case 'addWearable': + + var joints = MyAvatar.getJointNames(); + var hipsIndex = -1; + + for(var i = 0; i < joints.length; ++i) { + if(joints[i] === 'Hips') { + hipsIndex = i; + break; + } + } + + var properties = { + name: "Custom wearable", + type: "Model", + modelURL: message.url, + parentID: MyAvatar.sessionUUID, + relayParentJoints: false, + parentJointIndex: hipsIndex + }; + + var entityID = Entities.addEntity(properties, true); + updateAvatarWearables(currentAvatar, message.avatarName); + executeLater(function() { + onSelectedEntity(entityID); + }); + break; case 'selectWearable': ensureWearableSelected(message.entityID); break; From 9304d6c5c4f85185653c93b275427d1c8bf006a6 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 18:42:08 +0300 Subject: [PATCH 030/207] reset UI to default state & disable controls on removing last wearable --- .../qml/hifi/avatarapp/AdjustWearables.qml | 47 ++++++++++++------- .../resources/qml/hifi/avatarapp/Vector3.qml | 4 ++ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 27ae590fc5..bf3c62410e 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -53,7 +53,10 @@ Rectangle { } wearablesCombobox.model.append(wearable); } - wearablesCombobox.currentIndex = 0; + + if(wearablesCombobox.model.count !== 0) { + wearablesCombobox.currentIndex = 0; + } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { @@ -75,9 +78,9 @@ Rectangle { if(updateUI) { if(prop === 'localPosition') { - position.set(wearable[prop]); + positionVector.set(wearable[prop]); } else if(prop === 'localRotationAngles') { - rotation.set(wearable[prop]); + rotationVector.set(wearable[prop]); } else if(prop === 'dimensions') { scalespinner.set(wearable[prop].x / wearable.naturalDimensions.x); } @@ -88,7 +91,7 @@ Rectangle { } function getCurrentWearable() { - return wearablesCombobox.model.get(wearablesCombobox.currentIndex) + return wearablesCombobox.currentIndex !== -1 ? wearablesCombobox.model.get(wearablesCombobox.currentIndex) : null; } function selectWearableByID(entityID) { @@ -184,6 +187,7 @@ Rectangle { id: wearablesCombobox anchors.left: parent.left anchors.right: parent.right + enabled: getCurrentWearable() !== null comboBox.textRole: "text" model: ListModel { @@ -202,14 +206,19 @@ Rectangle { comboBox.onCurrentIndexChanged: { var currentWearable = getCurrentWearable(); + var position = currentWearable ? currentWearable.localPosition : { x : 0, y : 0, z : 0 }; + var rotation = currentWearable ? currentWearable.localRotationAngles : { x : 0, y : 0, z : 0 }; + var scale = currentWearable ? currentWearable.dimensions.x / currentWearable.naturalDimensions.x : 1.0; + var joint = currentWearable ? currentWearable.parentJointIndex : -1; + var soft = currentWearable ? currentWearable.relayParentJoints : false; + + positionVector.set(position); + rotationVector.set(rotation); + scalespinner.set(scale); + jointsCombobox.set(joint); + isSoft.set(soft); if(currentWearable) { - position.set(currentWearable.localPosition); - rotation.set(currentWearable.localRotationAngles); - scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) - jointsCombobox.set(currentWearable.parentJointIndex) - isSoft.set(currentWearable.relayParentJoints) - wearableSelected(currentWearable.id); } } @@ -230,7 +239,7 @@ Rectangle { id: jointsCombobox anchors.left: parent.left anchors.right: parent.right - enabled: !isSoft.checked + enabled: getCurrentWearable() !== null && !isSoft.checked comboBox.displayText: isSoft.checked ? 'Hips' : comboBox.currentText model: jointNames @@ -247,9 +256,9 @@ Rectangle { var properties = { parentJointIndex: currentIndex, localPosition: { - x: position.xvalue, - y: position.yvalue, - z: position.zvalue + x: positionVector.xvalue, + y: positionVector.yvalue, + z: positionVector.zvalue } }; @@ -288,8 +297,9 @@ Rectangle { } Vector3 { - id: position + id: positionVector backgroundColor: "lightgray" + enabled: getCurrentWearable() !== null function set(localPosition) { notify = false; @@ -347,8 +357,9 @@ Rectangle { } Vector3 { - id: rotation + id: rotationVector backgroundColor: "lightgray" + enabled: getCurrentWearable() !== null function set(localRotationAngles) { notify = false; @@ -386,6 +397,7 @@ Rectangle { HifiControlsUit.CheckBox { id: isSoft + enabled: getCurrentWearable() !== null text: "Is soft" labelFontSize: 15 labelFontWeight: Font.Bold @@ -427,13 +439,14 @@ Rectangle { HifiControlsUit.SpinBox { id: scalespinner + enabled: getCurrentWearable() !== null decimals: 2 realStepSize: 0.1 realFrom: 0.1 realTo: 3.0 realValue: 1.0 backgroundColor: "lightgray" - width: position.spinboxWidth + width: positionVector.spinboxWidth colorScheme: hifi.colorSchemes.light property bool notify: false; diff --git a/interface/resources/qml/hifi/avatarapp/Vector3.qml b/interface/resources/qml/hifi/avatarapp/Vector3.qml index 33e82fe0ee..d77665f992 100644 --- a/interface/resources/qml/hifi/avatarapp/Vector3.qml +++ b/interface/resources/qml/hifi/avatarapp/Vector3.qml @@ -20,6 +20,7 @@ Row { spacing: spinboxSpace + property bool enabled: false; property alias xvalue: xspinner.realValue property alias yvalue: yspinner.realValue property alias zvalue: zspinner.realValue @@ -35,6 +36,7 @@ Row { realFrom: root.realFrom realTo: root.realTo realStepSize: root.realStepSize + enabled: root.enabled } HifiControlsUit.SpinBox { @@ -48,6 +50,7 @@ Row { realFrom: root.realFrom realTo: root.realTo realStepSize: root.realStepSize + enabled: root.enabled } HifiControlsUit.SpinBox { @@ -61,5 +64,6 @@ Row { realFrom: root.realFrom realTo: root.realTo realStepSize: root.realStepSize + enabled: root.enabled } } From ce6c8c72f528e24cf73254f5708d74dab5f6ade9 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 19:09:47 +0300 Subject: [PATCH 031/207] ensure selection of newly-added wearable always happens after pushing updated wearables list to UI --- scripts/system/avatarapp.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 38b6d2447c..85946c644a 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -72,12 +72,15 @@ function getMyAvatarSettings() { } } -function updateAvatarWearables(avatar, bookmarkAvatarName) { +function updateAvatarWearables(avatar, bookmarkAvatarName, callback) { executeLater(function() { var wearables = getMyAvatarWearables(); avatar[ENTRY_AVATAR_ENTITIES] = wearables; sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables, 'avatarName' : bookmarkAvatarName}) + + if(callback) + callback(); }); } @@ -257,8 +260,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See }; var entityID = Entities.addEntity(properties, true); - updateAvatarWearables(currentAvatar, message.avatarName); - executeLater(function() { + updateAvatarWearables(currentAvatar, message.avatarName, function() { onSelectedEntity(entityID); }); break; From 3bfe173371c73446377e065ff692c5e2b1901b0b Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 19:13:17 +0300 Subject: [PATCH 032/207] allow saving wearables right after adding new one --- interface/resources/qml/hifi/avatarapp/AdjustWearables.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index bf3c62410e..5e88836070 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -177,6 +177,7 @@ Rectangle { popup.showSpecifyWearableUrl(function(url) { console.debug('popup.showSpecifyWearableUrl: ', url); addWearable(root.avatarName, url); + modified = true; }); } } From a8b9568c926301f1cac887da65dd0b3ec3294e93 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 10 Aug 2018 10:11:46 -0700 Subject: [PATCH 033/207] Add some locking --- interface/src/octree/SafeLanding.cpp | 50 ++++++++++++---------------- interface/src/octree/SafeLanding.h | 3 +- 2 files changed, 24 insertions(+), 29 deletions(-) diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 352c83dad0..ab0ae784f8 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -35,6 +35,7 @@ void SafeLanding::startEntitySequence(QSharedPointer entityT auto entityTree = entityTreeRenderer->getTree(); if (entityTree) { + Locker lock(_lock); _entityTree = entityTree; connect(std::const_pointer_cast(_entityTree).get(), &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity); @@ -47,6 +48,7 @@ void SafeLanding::startEntitySequence(QSharedPointer entityT } void SafeLanding::stopEntitySequence() { + Locker lock(_lock); _trackingEntities = false; _initialStart = INVALID_SEQUENCE; _initialEnd = INVALID_SEQUENCE; @@ -62,10 +64,11 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { const auto& entityType = entity->getType(); if (entityType == EntityTypes::Model) { ModelEntityItem * modelEntity = std::dynamic_pointer_cast(entity).get(); - auto shapeType = modelEntity->getShapeType(); - if (shapeType == SHAPE_TYPE_COMPOUND || shapeType == SHAPE_TYPE_STATIC_MESH || - shapeType == SHAPE_TYPE_SIMPLE_COMPOUND) { + static const std::set downloadedCollisionTypes + { SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL }; + if (downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { // Only track entities with downloaded collision bodies. + Locker lock(_lock); _trackedEntities.emplace(entityID, entity); qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); } @@ -75,27 +78,37 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { } void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) { + Locker lock(_lock); _trackedEntities.erase(entityID); } void SafeLanding::setCompletionSequenceNumbers(int first, int last) { + Locker lock(_lock); _initialStart = first; _initialEnd = (last + 1) % SEQUENCE_MODULO; - auto firstIter = _sequenceNumbers.find(_initialStart); - if (firstIter != _sequenceNumbers.end()) { - _sequenceNumbers.erase(_sequenceNumbers.begin(), firstIter); - } - _sequenceNumbers.erase(_sequenceNumbers.find(_initialEnd), _sequenceNumbers.end()); } void SafeLanding::sequenceNumberReceived(int sequenceNumber) { if (_trackingEntities) { + Locker lock(_lock); _sequenceNumbers.insert(sequenceNumber); } } +bool SafeLanding::isLoadSequenceComplete() { + if (sequenceNumbersComplete() && entityPhysicsComplete()) { + Locker lock(_lock); + _trackingEntities = false; + _trackedEntities.clear(); + qCDebug(interfaceapp) << "Safe Landing: load sequence complete"; + } + + return !_trackingEntities; +} + bool SafeLanding::sequenceNumbersComplete() { if (_initialStart != INVALID_SEQUENCE) { + Locker lock(_lock); auto startIter = _sequenceNumbers.find(_initialStart); if (startIter != _sequenceNumbers.end()) { _sequenceNumbers.erase(_sequenceNumbers.begin(), startIter); @@ -109,27 +122,8 @@ bool SafeLanding::sequenceNumbersComplete() { return false; } -void SafeLanding::resourceLoaded() { - QObject * sender = QObject::sender(); - if (sender) { - Resource * resource = dynamic_cast(sender); - const QString resourceURL = resource->getURL().toString(); - _trackedURLs.erase(resourceURL); - qCDebug(interfaceapp) << "Safe Landing: Removed tracked URL" << resourceURL; - } -} - -bool SafeLanding::isLoadSequenceComplete() { - if (sequenceNumbersComplete() && entityPhysicsComplete()) { - _trackingEntities = false; - _trackedEntities.clear(); - qCDebug(interfaceapp) << "Safe Landing: load sequence complete"; - } - - return !_trackingEntities; -} - bool SafeLanding::entityPhysicsComplete() { + Locker lock(_lock); for (auto entityMapIter = _trackedEntities.begin(); entityMapIter != _trackedEntities.end(); ++entityMapIter) { auto entity = entityMapIter->second; if (!entity->shouldBePhysical() || entity->isReadyToComputeShape()) { diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index e81746e077..a9146f58c7 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -33,13 +33,14 @@ public: private slots: void addTrackedEntity(const EntityItemID& entityID); void deleteTrackedEntity(const EntityItemID& entityID); - void resourceLoaded(); private: bool sequenceNumbersComplete(); void debugDumpSequenceIDs() const; bool entityPhysicsComplete(); + std::mutex _lock; + using Locker = std::lock_guard; bool _trackingEntities { false }; EntityTreePointer _entityTree; using EntityMap = std::map; From 9b9e30e829325e47b4408fb537415f9ee93f2282 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 20:14:15 +0300 Subject: [PATCH 034/207] add 'Tip: You can use hand controllers to grab and adjust your wearables' if in HMD --- .../qml/hifi/avatarapp/AdjustWearables.qml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 5e88836070..2b50fbdf34 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -131,6 +131,32 @@ Rectangle { Column { width: parent.width + Rectangle { + color: hifi.colors.orangeHighlight + anchors.left: parent.left + anchors.right: parent.right + height: 70 + visible: HMD.active + + RalewayRegular { + anchors.fill: parent + anchors.leftMargin: 18 + anchors.rightMargin: 18 + size: 20; + lineHeightMode: Text.FixedHeight + lineHeight: 23; + text: "Tip: You can use hand controllers to grab and adjust your wearables" + wrapMode: Text.WordWrap + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 12 // spacing + visible: HMD.active + } + RowLayout { anchors.left: parent.left anchors.right: parent.right From e6915e63f2fb777d31951b3c7f91c3b1c83f3c72 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 11 Aug 2018 00:39:43 +0300 Subject: [PATCH 035/207] put 'Fedora' on HEAD --- interface/resources/avatar/bookmarks/avatarbookmarks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/avatar/bookmarks/avatarbookmarks.json b/interface/resources/avatar/bookmarks/avatarbookmarks.json index 2ef59d53a3..065e97c842 100644 --- a/interface/resources/avatar/bookmarks/avatarbookmarks.json +++ b/interface/resources/avatar/bookmarks/avatarbookmarks.json @@ -507,7 +507,7 @@ "originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n", "owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}", "parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}", - "parentJointIndex": 64, + "parentJointIndex": 63, "position": { "x": -0.030000008642673492, "y": 0.12999820709228516, From 3c665b7de8c4d66f84a1b4976a6a8e4c432076d5 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 10 Aug 2018 14:40:08 -0700 Subject: [PATCH 036/207] Don't delete sequence numbers outside of range as range may change --- interface/src/Application.cpp | 3 +-- interface/src/octree/SafeLanding.cpp | 23 +++++++++++-------- interface/src/octree/SafeLanding.h | 2 +- .../src/EntityTreeRenderer.cpp | 8 +++++++ 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cc2c7f601a..cf5da63dc8 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5495,7 +5495,6 @@ void Application::update(float deltaTime) { if (!_physicsEnabled) { if (!domainLoadingInProgress) { PROFILE_ASYNC_BEGIN(app, "Scene Loading", ""); - _octreeProcessor.startEntitySequence(); domainLoadingInProgress = true; } @@ -5514,7 +5513,7 @@ void Application::update(float deltaTime) { // process octree stats packets are sent in between full sends of a scene (this isn't currently true). // We keep physics disabled until we've received a full scene and everything near the avatar in that // scene is ready to compute its collision shape. - if (nearbyEntitiesAreReadyForPhysics() && getMyAvatar()->isReadyForPhysics()) { + if (getMyAvatar()->isReadyForPhysics()) { _physicsEnabled = true; getMyAvatar()->updateMotionBehaviorFromMenu(); } diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index ab0ae784f8..c958527c4a 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -85,7 +85,7 @@ void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) { void SafeLanding::setCompletionSequenceNumbers(int first, int last) { Locker lock(_lock); _initialStart = first; - _initialEnd = (last + 1) % SEQUENCE_MODULO; + _initialEnd = last; } void SafeLanding::sequenceNumberReceived(int sequenceNumber) { @@ -96,7 +96,7 @@ void SafeLanding::sequenceNumberReceived(int sequenceNumber) { } bool SafeLanding::isLoadSequenceComplete() { - if (sequenceNumbersComplete() && entityPhysicsComplete()) { + if (entityPhysicsComplete() && sequenceNumbersComplete()) { Locker lock(_lock); _trackingEntities = false; _trackedEntities.clear(); @@ -109,14 +109,19 @@ bool SafeLanding::isLoadSequenceComplete() { bool SafeLanding::sequenceNumbersComplete() { if (_initialStart != INVALID_SEQUENCE) { Locker lock(_lock); - auto startIter = _sequenceNumbers.find(_initialStart); - if (startIter != _sequenceNumbers.end()) { - _sequenceNumbers.erase(_sequenceNumbers.begin(), startIter); - _sequenceNumbers.erase(_sequenceNumbers.find(_initialEnd), _sequenceNumbers.end()); - int sequenceSize = _initialStart < _initialEnd ? _initialEnd - _initialStart : + //auto startIter = _sequenceNumbers.find(_initialStart); + //if (startIter != _sequenceNumbers.end()) { + // _sequenceNumbers.erase(_sequenceNumbers.begin(), startIter); + // _sequenceNumbers.erase(_sequenceNumbers.find(_initialEnd), _sequenceNumbers.end()); + int sequenceSize = _initialStart < _initialEnd ? _initialEnd - _initialStart: _initialEnd + SEQUENCE_MODULO - _initialStart; - // First no. exists, nothing outside of first, last exists, so complete iff set size is sequence size. - return (int) _sequenceNumbers.size() == sequenceSize; + // // First no. exists, nothing outside of first, last exists, so complete iff set size is sequence size. + // return (int) _sequenceNumbers.size() == sequenceSize; + //} + auto startIter = _sequenceNumbers.find(_initialStart); + auto endIter = _sequenceNumbers.find(_initialEnd); + if (startIter != _sequenceNumbers.end() && endIter != _sequenceNumbers.end() && std::distance(startIter, endIter) == sequenceSize) { + return true; } } return false; diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index a9146f58c7..2a9413034c 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -48,7 +48,7 @@ private: static constexpr int INVALID_SEQUENCE = -1; int _initialStart { INVALID_SEQUENCE }; - int _initialEnd { INVALID_SEQUENCE }; // final sequence, exclusive. + int _initialEnd { INVALID_SEQUENCE }; struct SequenceLessThan { bool operator()(const int& a, const int& b) const; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index e330427350..6b8505eaf1 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -642,6 +642,14 @@ bool EntityTreeRenderer::applyLayeredZones() { } void EntityTreeRenderer::processEraseMessage(ReceivedMessage& message, const SharedNodePointer& sourceNode) { + OCTREE_PACKET_FLAGS flags; + message.readPrimitive(&flags); + + OCTREE_PACKET_SEQUENCE sequence; + message.readPrimitive(&sequence); + + _lastOctreeMessageSequence = sequence; + message.seek(0); std::static_pointer_cast(_tree)->processEraseMessage(message, sourceNode); } From c1ebadfdd808359aa614c36926663a206c2faeba Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 11 Aug 2018 00:43:39 +0300 Subject: [PATCH 037/207] adjust text in 'get wearables' popup --- interface/resources/qml/hifi/avatarapp/MessageBoxes.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 64c2d478c0..62203960e8 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -61,8 +61,8 @@ MessageBox { popup.button1text = 'CANCEL' popup.titleText = 'Get Wearables' popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearables from My Purchases.' + '
' + '
' + - 'Visit “AvatarIsland” to get wearables' + 'Wear wearable from My Purchases.' + '
' + '
' + + 'Visit “AvatarIsland” to get wearables' popup.imageSource = getWearablesUrl; popup.onButton2Clicked = function() { From 5ba68cbfac3d51c2c4dade2af391ddbee6b8b242 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 11 Aug 2018 00:50:32 +0300 Subject: [PATCH 038/207] remove attachments menu item / dialogs / related components --- .../qml/hifi/dialogs/AttachmentsDialog.qml | 48 --- .../hifi/dialogs/AvatarAttachmentsDialog.qml | 130 -------- .../hifi/dialogs/attachments/Attachment.qml | 230 -------------- .../qml/hifi/dialogs/attachments/Rotation.qml | 9 - .../hifi/dialogs/attachments/Translation.qml | 9 - .../qml/hifi/dialogs/attachments/Vector3.qml | 112 ------- .../dialogs/content/AttachmentsContent.qml | 282 ------------------ .../hifi/tablet/TabletAttachmentsDialog.qml | 103 ------- interface/src/Menu.cpp | 7 - interface/src/Menu.h | 1 - 10 files changed, 931 deletions(-) delete mode 100644 interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml delete mode 100644 interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Attachment.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Rotation.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Translation.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Vector3.qml delete mode 100644 interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml delete mode 100644 interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml deleted file mode 100644 index 006a4b7158..0000000000 --- a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml +++ /dev/null @@ -1,48 +0,0 @@ -// -// AttachmentsDialog.qml -// -// Created by David Rowe on 9 Mar 2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import Qt.labs.settings 1.0 - -import "../../styles-uit" -import "../../windows" -import "content" - -ScrollingWindow { - id: root - title: "Attachments" - objectName: "AttachmentsDialog" - width: 600 - height: 600 - resizable: true - destroyOnHidden: true - minSize: Qt.vector2d(400, 500) - - HifiConstants { id: hifi } - - // This is for JS/QML communication, which is unused in the AttachmentsDialog, - // but not having this here results in spurious warnings about a - // missing signal - signal sendToScript(var message); - - property var settings: Settings { - category: "AttachmentsDialog" - property alias x: root.x - property alias y: root.y - property alias width: root.width - property alias height: root.height - } - - function closeDialog() { - root.destroy(); - } - - AttachmentsContent { } -} diff --git a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml deleted file mode 100644 index b4357a0077..0000000000 --- a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml +++ /dev/null @@ -1,130 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 1.5 -import QtQuick.XmlListModel 2.0 -import QtQuick.Controls.Styles 1.4 - -import "../../windows" -import "../../js/Utils.js" as Utils -import "../models" - -ModalWindow { - id: root - resizable: true - width: 640 - height: 480 - - property var result; - - signal selected(var modelUrl); - signal canceled(); - - Rectangle { - anchors.fill: parent - color: "white" - - Item { - anchors { fill: parent; margins: 8 } - - TextField { - id: filterEdit - anchors { left: parent.left; right: parent.right; top: parent.top } - style: TextFieldStyle { renderType: Text.QtRendering } - placeholderText: "filter" - onTextChanged: tableView.model.filter = text - } - - TableView { - id: tableView - anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 } - model: S3Model{} - onCurrentRowChanged: { - if (currentRow == -1) { - root.result = null; - return; - } - result = model.baseUrl + "/" + model.get(tableView.currentRow).key; - } - itemDelegate: Component { - Item { - clip: true - Text { - x: 3 - anchors.verticalCenter: parent.verticalCenter - color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor - elide: styleData.elideMode - text: getText() - - function getText() { - switch(styleData.column) { - case 1: - return Utils.formatSize(styleData.value) - default: - return styleData.value; - } - } - - } - } - } - TableViewColumn { - role: "name" - title: "Name" - width: 200 - } - TableViewColumn { - role: "size" - title: "Size" - width: 100 - } - TableViewColumn { - role: "modified" - title: "Last Modified" - width: 200 - } - Rectangle { - anchors.fill: parent - visible: tableView.model.status !== XmlListModel.Ready - color: "#7fffffff" - BusyIndicator { - anchors.centerIn: parent - width: 48; height: 48 - running: true - } - } - } - - Row { - id: buttonRow - anchors { right: parent.right; bottom: parent.bottom } - Button { action: acceptAction } - Button { action: cancelAction } - } - - Action { - id: acceptAction - text: qsTr("OK") - enabled: root.result ? true : false - shortcut: "Return" - onTriggered: { - root.selected(root.result); - root.destroy(); - } - } - - Action { - id: cancelAction - text: qsTr("Cancel") - shortcut: "Esc" - onTriggered: { - root.canceled(); - root.destroy(); - } - } - } - - } -} - - - - diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml deleted file mode 100644 index 54270c2d06..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ /dev/null @@ -1,230 +0,0 @@ -import QtQuick 2.5 - -import "." -import ".." -import "../../tablet" -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls -import "../../../windows" - -Item { - height: column.height + 2 * 8 - - property var attachment; - - HifiConstants { id: hifi } - - signal selectAttachment(); - signal deleteAttachment(var attachment); - signal updateAttachment(); - property bool completed: false; - - function doSelectAttachment(control, focus) { - if (focus) { - selectAttachment(); - - // Refocus control after possibly changing focus to attachment. - if (control.setControlFocus !== undefined) { - control.setControlFocus(); - } else { - control.focus = true; - } - } - } - - Rectangle { color: hifi.colors.baseGray; anchors.fill: parent; radius: 4 } - - Component.onCompleted: { - jointChooser.model = MyAvatar.jointNames; - completed = true; - } - - Column { - y: 8 - id: column - anchors { left: parent.left; right: parent.right; margins: 20 } - spacing: 8 - - Item { - height: modelChooserButton.height + urlLabel.height + 4 - anchors { left: parent.left; right: parent.right;} - HifiControls.Label { id: urlLabel; color: hifi.colors.lightGrayText; text: "Model URL"; anchors.top: parent.top;} - HifiControls.TextField { - id: modelUrl; - height: jointChooser.height; - colorScheme: hifi.colorSchemes.dark - anchors { bottom: parent.bottom; left: parent.left; rightMargin: 8; right: modelChooserButton.left } - text: attachment ? attachment.modelUrl : "" - onTextChanged: { - if (completed && attachment && attachment.modelUrl !== text) { - attachment.modelUrl = text; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - HifiControls.Button { - id: modelChooserButton; - text: "Choose"; - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter } - Component { - id: modelBrowserBuilder; - ModelBrowserDialog {} - } - Component { - id: tabletModelBrowserBuilder; - TabletModelBrowserDialog {} - } - - onClicked: { - var browser; - if (typeof desktop !== "undefined") { - browser = modelBrowserBuilder.createObject(desktop); - browser.selected.connect(function(newModelUrl){ - modelUrl.text = newModelUrl; - }); - } else { - browser = tabletModelBrowserBuilder.createObject(tabletRoot); - browser.selected.connect(function(newModelUrl){ - modelUrl.text = newModelUrl; - tabletRoot.openModal = null; - }); - browser.canceled.connect(function() { - tabletRoot.openModal = null; - }); - - // Make dialog modal. - tabletRoot.openModal = browser; - } - } - } - } - - Item { - z: 1000 - height: jointChooser.height + jointLabel.height + 4 - anchors { left: parent.left; right: parent.right; } - HifiControls.Label { - id: jointLabel; - text: "Joint"; - color: hifi.colors.lightGrayText; - anchors.top: parent.top - } - HifiControls.ComboBox { - id: jointChooser; - dropdownHeight: (typeof desktop !== "undefined") ? 480 : 206 - anchors { bottom: parent.bottom; left: parent.left; right: parent.right } - colorScheme: hifi.colorSchemes.dark - currentIndex: attachment ? model.indexOf(attachment.jointName) : -1 - onCurrentIndexChanged: { - if (completed && attachment && currentIndex != -1 && attachment.jointName !== model[currentIndex]) { - attachment.jointName = model[currentIndex]; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - } - - Item { - height: translation.height + translationLabel.height + 4 - anchors { left: parent.left; right: parent.right; } - HifiControls.Label { id: translationLabel; color: hifi.colors.lightGrayText; text: "Translation"; anchors.top: parent.top; } - Translation { - id: translation; - anchors { left: parent.left; right: parent.right; bottom: parent.bottom} - vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0}; - onValueChanged: { - if (completed && attachment) { - attachment.translation = vector; - updateAttachment(); - } - } - onControlFocusChanged: doSelectAttachment(this, controlFocus); - } - } - - Item { - height: rotation.height + rotationLabel.height + 4 - anchors { left: parent.left; right: parent.right; } - HifiControls.Label { id: rotationLabel; color: hifi.colors.lightGrayText; text: "Rotation"; anchors.top: parent.top; } - Rotation { - id: rotation; - anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } - vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0}; - onValueChanged: { - if (completed && attachment) { - attachment.rotation = vector; - updateAttachment(); - } - } - onControlFocusChanged: doSelectAttachment(this, controlFocus); - } - } - - Item { - height: scaleItem.height - anchors { left: parent.left; right: parent.right; } - - Item { - id: scaleItem - height: scaleSpinner.height + scaleLabel.height + 4 - width: parent.width / 3 - 8 - anchors { right: parent.right; } - HifiControls.Label { id: scaleLabel; color: hifi.colors.lightGrayText; text: "Scale"; anchors.top: parent.top; } - HifiControls.SpinBox { - id: scaleSpinner; - anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } - decimals: 2; - minimumValue: 0.01 - maximumValue: 10 - realStepSize: 0.05; - realValue: attachment ? attachment.scale : 1.0 - colorScheme: hifi.colorSchemes.dark - onRealValueChanged: { - if (completed && attachment && attachment.scale !== realValue) { - attachment.scale = realValue; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - } - - Item { - id: isSoftItem - height: scaleSpinner.height - anchors { - left: parent.left - bottom: parent.bottom - } - HifiControls.CheckBox { - id: soft - text: "Is soft" - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - checked: attachment ? attachment.soft : false - colorScheme: hifi.colorSchemes.dark - onCheckedChanged: { - if (completed && attachment && attachment.soft !== checked) { - attachment.soft = checked; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - } - } - HifiControls.Button { - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors { left: parent.left; right: parent.right; } - text: "Delete" - onClicked: deleteAttachment(root.attachment); - } - } -} diff --git a/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml b/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml deleted file mode 100644 index 6061efc4c8..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml +++ /dev/null @@ -1,9 +0,0 @@ -import "." - -Vector3 { - decimals: 1; - stepSize: 1; - maximumValue: 180 - minimumValue: -180 -} - diff --git a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml deleted file mode 100644 index 39ac6da55a..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml +++ /dev/null @@ -1,9 +0,0 @@ -import "." - -Vector3 { - decimals: 3; - stepSize: 0.01; - maximumValue: 10 - minimumValue: -10 -} - diff --git a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml deleted file mode 100644 index 311492858b..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml +++ /dev/null @@ -1,112 +0,0 @@ -import QtQuick 2.5 - -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls -import "../../../windows" - -Item { - id: root - implicitHeight: xspinner.height - readonly property real spacing: 8 - property real spinboxWidth: (width / 3) - spacing - property var vector; - property real decimals: 0 - property real stepSize: 1 - property real maximumValue: 99 - property real minimumValue: 0 - property bool controlFocus: false; // True if one of the ordinate controls has focus. - property var controlFocusControl: undefined - - signal valueChanged(); - - function setControlFocus() { - if (controlFocusControl) { - controlFocusControl.focus = true; - // The controlFocus value is updated via onFocusChanged. - } - } - - function setFocus(control, focus) { - if (focus) { - controlFocusControl = control; - setControlFocusTrue.start(); // After any subsequent false from previous control. - } else { - controlFocus = false; - } - } - - Timer { - id: setControlFocusTrue - interval: 50 - repeat: false - running: false - onTriggered: { - controlFocus = true; - } - } - - HifiConstants { id: hifi } - - HifiControls.SpinBox { - id: xspinner - width: root.spinboxWidth - anchors { left: parent.left } - realValue: root.vector.x - labelInside: "X:" - colorScheme: hifi.colorSchemes.dark - colorLabelInside: hifi.colors.redHighlight - decimals: root.decimals - realStepSize: root.stepSize - maximumValue: root.maximumValue - minimumValue: root.minimumValue - onRealValueChanged: { - if (realValue !== vector.x) { - vector.x = realValue - root.valueChanged(); - } - } - onFocusChanged: setFocus(this, focus); - } - - HifiControls.SpinBox { - id: yspinner - width: root.spinboxWidth - anchors { horizontalCenter: parent.horizontalCenter } - realValue: root.vector.y - labelInside: "Y:" - colorLabelInside: hifi.colors.greenHighlight - colorScheme: hifi.colorSchemes.dark - decimals: root.decimals - realStepSize: root.stepSize - maximumValue: root.maximumValue - minimumValue: root.minimumValue - onRealValueChanged: { - if (realValue !== vector.y) { - vector.y = realValue - root.valueChanged(); - } - } - onFocusChanged: setFocus(this, focus); - } - - HifiControls.SpinBox { - id: zspinner - width: root.spinboxWidth - anchors { right: parent.right; } - realValue: root.vector.z - labelInside: "Z:" - colorLabelInside: hifi.colors.primaryHighlight - colorScheme: hifi.colorSchemes.dark - decimals: root.decimals - realStepSize: root.stepSize - maximumValue: root.maximumValue - minimumValue: root.minimumValue - onRealValueChanged: { - if (realValue !== vector.z) { - vector.z = realValue - root.valueChanged(); - } - } - onFocusChanged: setFocus(this, focus); - } -} diff --git a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml deleted file mode 100644 index fa71116574..0000000000 --- a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml +++ /dev/null @@ -1,282 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.3 -import QtQuick.Dialogs 1.2 as OriginalDialogs - -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls -import "../../../windows" -import "../attachments" - -Item { - id: content - - readonly property var originalAttachments: MyAvatar.getAttachmentsVariant(); - property var attachments: []; - - function reload(){ - content.attachments = []; - var currentAttachments = MyAvatar.getAttachmentsVariant(); - listView.model.clear(); - for (var i = 0; i < currentAttachments.length; ++i) { - var attachment = currentAttachments[i]; - content.attachments.push(attachment); - listView.model.append({}); - } - } - - Connections { - id: onAttachmentsChangedConnection - target: MyAvatar - onAttachmentsChanged: reload() - } - - Component.onCompleted: { - reload() - } - - function setAttachmentsVariant(attachments) { - onAttachmentsChangedConnection.enabled = false; - MyAvatar.setAttachmentsVariant(attachments); - onAttachmentsChangedConnection.enabled = true; - } - - Column { - width: pane.width - - Rectangle { - width: parent.width - height: root.height - (keyboardEnabled && keyboardRaised ? 200 : 0) - color: hifi.colors.baseGray - - Rectangle { - id: attachmentsBackground - anchors { - left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; - margins: hifi.dimensions.contentMargin.x - bottomMargin: hifi.dimensions.contentSpacing.y - } - color: hifi.colors.baseGrayShadow - radius: 4 - - ListView { - id: listView - anchors { - top: parent.top - left: parent.left - right: scrollBar.left - bottom: parent.bottom - margins: 4 - } - clip: true - cacheBuffer: 4000 - - model: ListModel {} - delegate: Item { - id: attachmentDelegate - implicitHeight: attachmentView.height + 8; - implicitWidth: attachmentView.width - - MouseArea { - // User can click on whitespace to select item. - anchors.fill: parent - propagateComposedEvents: true - onClicked: { - listView.currentIndex = index; - attachmentsBackground.forceActiveFocus(); // Unfocus from any control. - mouse.accepted = false; - } - } - - Attachment { - id: attachmentView - width: listView.width - attachment: content.attachments[index] - onSelectAttachment: { - listView.currentIndex = index; - } - onDeleteAttachment: { - attachments.splice(index, 1); - listView.model.remove(index, 1); - } - onUpdateAttachment: { - setAttachmentsVariant(attachments); - } - } - } - - onCountChanged: { - setAttachmentsVariant(attachments); - } - - /* - // DEBUG - highlight: Rectangle { color: "#40ffff00" } - highlightFollowsCurrentItem: true - */ - - onHeightChanged: { - // Keyboard has been raised / lowered. - positionViewAtIndex(listView.currentIndex, ListView.SnapPosition); - } - - onCurrentIndexChanged: { - if (!yScrollTimer.running) { - scrollSlider.y = currentIndex * (scrollBar.height - scrollSlider.height) / (listView.count - 1); - } - } - - onContentYChanged: { - // User may have dragged content up/down. - yScrollTimer.restart(); - } - - Timer { - id: yScrollTimer - interval: 200 - repeat: false - running: false - onTriggered: { - var index = (listView.count - 1) * listView.contentY / (listView.contentHeight - scrollBar.height); - index = Math.round(index); - listView.currentIndex = index; - scrollSlider.y = index * (scrollBar.height - scrollSlider.height) / (listView.count - 1); - } - } - } - - Rectangle { - id: scrollBar - - property bool scrolling: listView.contentHeight > listView.height - - anchors { - top: parent.top - right: parent.right - bottom: parent.bottom - topMargin: 4 - bottomMargin: 4 - } - width: scrolling ? 18 : 0 - radius: attachmentsBackground.radius - color: hifi.colors.baseGrayShadow - - MouseArea { - anchors.fill: parent - - onClicked: { - var index = listView.currentIndex; - index = index + (mouse.y <= scrollSlider.y ? -1 : 1); - if (index < 0) { - index = 0; - } - if (index > listView.count - 1) { - index = listView.count - 1; - } - listView.currentIndex = index; - } - } - - Rectangle { - id: scrollSlider - anchors { - right: parent.right - rightMargin: 3 - } - width: 16 - height: (listView.height / listView.contentHeight) * listView.height - radius: width / 2 - color: hifi.colors.lightGray - - visible: scrollBar.scrolling; - - onYChanged: { - var index = y * (listView.count - 1) / (scrollBar.height - scrollSlider.height); - index = Math.round(index); - listView.currentIndex = index; - } - - MouseArea { - anchors.fill: parent - drag.target: scrollSlider - drag.axis: Drag.YAxis - drag.minimumY: 0 - drag.maximumY: scrollBar.height - scrollSlider.height - } - } - } - } - - HifiControls.Button { - id: newAttachmentButton - anchors { - left: parent.left - right: parent.right - bottom: buttonRow.top - margins: hifi.dimensions.contentMargin.x; - topMargin: hifi.dimensions.contentSpacing.y - bottomMargin: hifi.dimensions.contentSpacing.y - } - text: "New Attachment" - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - onClicked: { - var template = { - modelUrl: "", - translation: { x: 0, y: 0, z: 0 }, - rotation: { x: 0, y: 0, z: 0 }, - scale: 1, - jointName: MyAvatar.jointNames[0], - soft: false - }; - attachments.push(template); - listView.model.append({}); - setAttachmentsVariant(attachments); - } - } - - Row { - id: buttonRow - spacing: 8 - anchors { - right: parent.right - bottom: parent.bottom - margins: hifi.dimensions.contentMargin.x - topMargin: hifi.dimensions.contentSpacing.y - bottomMargin: hifi.dimensions.contentSpacing.y - } - HifiControls.Button { - action: okAction - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - } - HifiControls.Button { - action: cancelAction - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - } - } - - Action { - id: cancelAction - text: "Cancel" - onTriggered: { - setAttachmentsVariant(originalAttachments); - closeDialog(); - } - } - - Action { - id: okAction - text: "OK" - onTriggered: { - for (var i = 0; i < attachments.length; ++i) { - console.log("Attachment " + i + ": " + attachments[i]); - } - - setAttachmentsVariant(attachments); - closeDialog(); - } - } - } - } -} diff --git a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml b/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml deleted file mode 100644 index 7e1cdea0db..0000000000 --- a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml +++ /dev/null @@ -1,103 +0,0 @@ -// -// TabletAttachmentsDialog.qml -// -// Created by David Rowe on 9 Mar 2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 - -import "../../controls-uit" as HifiControls -import "../../styles-uit" -import "../dialogs/content" - -Item { - id: root - objectName: "AttachmentsDialog" - - property string title: "Avatar Attachments" - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - signal sendToScript(var message); - - anchors.fill: parent - - HifiConstants { id: hifi } - - Rectangle { - id: pane // Surrogate for ScrollingWindow's pane. - anchors.fill: parent - } - - function closeDialog() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen(); - } - - anchors.topMargin: hifi.dimensions.tabletMenuHeader // Space for header. - - HifiControls.TabletHeader { - id: header - title: root.title - - anchors { - left: parent.left - right: parent.right - bottom: parent.top - } - } - - AttachmentsContent { - id: attachments - - anchors { - top: header.bottom - left: parent.left - right: parent.right - bottom: keyboard.top - } - - MouseArea { - // Defocuses any current control so that the keyboard gets hidden. - id: defocuser - anchors.fill: parent - propagateComposedEvents: true - acceptedButtons: Qt.AllButtons - onPressed: { - parent.forceActiveFocus(); - mouse.accepted = false; - } - } - } - - HifiControls.Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - MouseArea { - id: activator - anchors.fill: parent - propagateComposedEvents: true - enabled: true - acceptedButtons: Qt.AllButtons - onPressed: { - mouse.accepted = false; - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } -} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 130c2c0b89..8524c40262 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -275,13 +275,6 @@ Menu::Menu() { QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog"); }); - // Settings > Attachments... - action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Attachments); - connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"), - QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog"); - }); - // Settings > Developer Menu addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 1ab7faa82b..be8957cefd 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -36,7 +36,6 @@ namespace MenuOption { const QString AskToResetSettings = "Ask To Reset Settings on Start"; const QString AssetMigration = "ATP Asset Migration"; const QString AssetServer = "Asset Browser"; - const QString Attachments = "Attachments..."; const QString AudioScope = "Show Scope"; const QString AudioScopeFiftyFrames = "Fifty"; const QString AudioScopeFiveFrames = "Five"; From 05fa7fdf0e012fab69b26c1a77317536287ebe07 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 10 Aug 2018 15:43:51 -0700 Subject: [PATCH 039/207] Clean-up --- interface/src/octree/SafeLanding.cpp | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index c958527c4a..4cbdb815b2 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -37,12 +37,14 @@ void SafeLanding::startEntitySequence(QSharedPointer entityT if (entityTree) { Locker lock(_lock); _entityTree = entityTree; + _trackingEntities = true; connect(std::const_pointer_cast(_entityTree).get(), &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity); connect(std::const_pointer_cast(_entityTree).get(), &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity); - _trackingEntities = true; _sequenceNumbers.clear(); + _initialStart = INVALID_SEQUENCE; + _initialEnd = INVALID_SEQUENCE; } } } @@ -58,6 +60,7 @@ void SafeLanding::stopEntitySequence() { void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { if (_trackingEntities) { + Locker lock(_lock); EntityItemPointer entity = _entityTree->findEntityByID(entityID); if (entity && !entity->getCollisionless()) { @@ -68,7 +71,6 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { { SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL }; if (downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { // Only track entities with downloaded collision bodies. - Locker lock(_lock); _trackedEntities.emplace(entityID, entity); qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); } @@ -84,8 +86,10 @@ void SafeLanding::deleteTrackedEntity(const EntityItemID& entityID) { void SafeLanding::setCompletionSequenceNumbers(int first, int last) { Locker lock(_lock); - _initialStart = first; - _initialEnd = last; + if (_initialStart == INVALID_SEQUENCE) { + _initialStart = first; + _initialEnd = last; + } } void SafeLanding::sequenceNumberReceived(int sequenceNumber) { @@ -98,8 +102,10 @@ void SafeLanding::sequenceNumberReceived(int sequenceNumber) { bool SafeLanding::isLoadSequenceComplete() { if (entityPhysicsComplete() && sequenceNumbersComplete()) { Locker lock(_lock); - _trackingEntities = false; _trackedEntities.clear(); + _initialStart = INVALID_SEQUENCE; + _initialEnd = INVALID_SEQUENCE; + _entityTree = nullptr; qCDebug(interfaceapp) << "Safe Landing: load sequence complete"; } @@ -109,18 +115,12 @@ bool SafeLanding::isLoadSequenceComplete() { bool SafeLanding::sequenceNumbersComplete() { if (_initialStart != INVALID_SEQUENCE) { Locker lock(_lock); - //auto startIter = _sequenceNumbers.find(_initialStart); - //if (startIter != _sequenceNumbers.end()) { - // _sequenceNumbers.erase(_sequenceNumbers.begin(), startIter); - // _sequenceNumbers.erase(_sequenceNumbers.find(_initialEnd), _sequenceNumbers.end()); int sequenceSize = _initialStart < _initialEnd ? _initialEnd - _initialStart: _initialEnd + SEQUENCE_MODULO - _initialStart; - // // First no. exists, nothing outside of first, last exists, so complete iff set size is sequence size. - // return (int) _sequenceNumbers.size() == sequenceSize; - //} auto startIter = _sequenceNumbers.find(_initialStart); auto endIter = _sequenceNumbers.find(_initialEnd); - if (startIter != _sequenceNumbers.end() && endIter != _sequenceNumbers.end() && std::distance(startIter, endIter) == sequenceSize) { + if (startIter != _sequenceNumbers.end() && endIter != _sequenceNumbers.end() && distance(startIter, endIter) == sequenceSize) { + _trackingEntities = false; // Don't track anything else that comes in. return true; } } From 0ae86d5aa8ce241e132df8774abe6ad8cb989ca6 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 10 Aug 2018 16:16:58 -0700 Subject: [PATCH 040/207] Remove useless statement --- interface/src/octree/OctreePacketProcessor.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index 6dab37b1a1..f740995123 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -121,8 +121,6 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag // Read sequence # OCTREE_PACKET_SEQUENCE completionNumber; message->readPrimitive(&completionNumber); - - completionNumber; _safeLanding->setCompletionSequenceNumbers(0, completionNumber); } break; From 829b83c61ddcffa6153ba68406a1f2dfdf91edb9 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Fri, 10 Aug 2018 18:16:08 -0700 Subject: [PATCH 041/207] Use elevated model priority during initial load --- interface/src/octree/SafeLanding.cpp | 2 ++ interface/src/octree/SafeLanding.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 4cbdb815b2..16e357c9dd 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -45,6 +45,7 @@ void SafeLanding::startEntitySequence(QSharedPointer entityT _sequenceNumbers.clear(); _initialStart = INVALID_SEQUENCE; _initialEnd = INVALID_SEQUENCE; + EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority); } } } @@ -106,6 +107,7 @@ bool SafeLanding::isLoadSequenceComplete() { _initialStart = INVALID_SEQUENCE; _initialEnd = INVALID_SEQUENCE; _entityTree = nullptr; + EntityTreeRenderer::setEntityLoadingPriorityFunction(StandardPriority); qCDebug(interfaceapp) << "Safe Landing: load sequence complete"; } diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index 2a9413034c..9a846294bf 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -57,6 +57,9 @@ private: std::set _sequenceNumbers; std::set _trackedURLs; + static float ElevatedPriority(const EntityItem&) { return 10.0f; } + static float StandardPriority(const EntityItem&) { return 0.0f; } + static const int SEQUENCE_MODULO; }; From 83ededfd37961ef157b1dd63497b32a2a090cc7e Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 13 Aug 2018 14:51:13 -0700 Subject: [PATCH 042/207] Pass collision filter flags into AllContactsCallback for more efficient collision filtering --- libraries/physics/src/PhysicsEngine.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index ef8c28bdc6..efe1bddc97 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -863,6 +863,9 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) { } } +const int32_t CONTACT_CALLBACK_FLAG_ENTITY = BULLET_COLLISION_GROUP_STATIC | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_DYNAMIC; +const int32_t CONTACT_CALLBACK_FLAG_AVATAR = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR; + struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { AllContactsCallback(MotionStateType desiredObjectType, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) : btCollisionWorld::ContactResultCallback(), @@ -879,6 +882,14 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { bulletTransform.setRotation(glmToBullet(transform.getRotation())); collisionObject.setWorldTransform(bulletTransform); + + m_collisionFilterGroup = ~0; // Everything collidable should collide with our test object unless we set the filter mask otherwise + if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR) { + m_collisionFilterMask = CONTACT_CALLBACK_FLAG_AVATAR; + } + else { + m_collisionFilterMask = CONTACT_CALLBACK_FLAG_ENTITY; + } } ~AllContactsCallback() { @@ -890,10 +901,6 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { std::vector contacts; btCollisionObject* myAvatarCollisionObject; - bool needsCollision(btBroadphaseProxy* proxy) const override { - return true; - } - btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override { const btCollisionObject* otherBody; btVector3 penetrationPoint; From 61d12923ea794373ee6b951ea16125f9ebc2aff2 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 13 Aug 2018 16:49:51 -0700 Subject: [PATCH 043/207] Do not copy list of collision pick contact points when filtering them --- interface/src/raypick/CollisionPick.cpp | 20 ++++++++++---------- interface/src/raypick/CollisionPick.h | 2 +- libraries/physics/src/PhysicsEngine.cpp | 2 +- libraries/physics/src/PhysicsEngine.h | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 87c8c91e6d..a70882eed0 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -308,20 +308,18 @@ CollisionRegion CollisionPick::getMathematicalPick() const { return _mathPick; } -const std::vector CollisionPick::filterIntersections(const std::vector& intersections) const { - std::vector filteredIntersections; - +void CollisionPick::filterIntersections(std::vector& intersections) const { const QVector& ignoreItems = getIgnoreItems(); const QVector& includeItems = getIncludeItems(); bool isWhitelist = includeItems.size(); - for (const auto& intersection : intersections) { + for (int i = 0; i < intersections.size(); i++) { + auto& intersection = intersections[i]; const QUuid& id = intersection.foundID; - if (!ignoreItems.contains(id) && (!isWhitelist || includeItems.contains(id))) { - filteredIntersections.push_back(intersection); + if (ignoreItems.contains(id) || (isWhitelist && !includeItems.contains(id))) { + intersections[i] = intersections[intersections.size()-1]; + intersections.pop_back(); } } - - return filteredIntersections; } PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { @@ -330,7 +328,8 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } - const auto& entityIntersections = filterIntersections(_physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform)); + auto& entityIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform); + filterIntersections(entityIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); } @@ -344,7 +343,8 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } - const auto& avatarIntersections = filterIntersections(_physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform)); + auto& avatarIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform); + filterIntersections(avatarIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); } diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index b3a7186893..bfc3487a62 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -92,7 +92,7 @@ protected: // Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. bool isShapeInfoReady(); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); - const std::vector filterIntersections(const std::vector& intersections) const; + void filterIntersections(std::vector& intersections) const; CollisionRegion _mathPick; PhysicsEnginePointer _physicsEngine; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index efe1bddc97..cfe255e561 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -944,7 +944,7 @@ protected: } }; -const std::vector PhysicsEngine::getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const { +std::vector PhysicsEngine::getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const { // TODO: Give MyAvatar a motion state so we don't have to do this btCollisionObject* myAvatarCollisionObject = nullptr; if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && _myAvatarController) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index c5ab0cfdee..854d61844f 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -126,7 +126,7 @@ public: void setShowBulletConstraintLimits(bool value); // Function for getting colliding ObjectMotionStates in the world of specified type - const std::vector getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const; + std::vector getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const; private: QList removeDynamicsForBody(btRigidBody* body); From aa4a6b2eaedb8f578d7bcd2a2dcd7a4e112028f0 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 13 Aug 2018 17:26:20 -0700 Subject: [PATCH 044/207] Convert entityIntersections/avatarIntersections lists in CollisionPickResult to shared pointers --- interface/src/raypick/CollisionPick.cpp | 27 +++++++++++++------------ interface/src/raypick/CollisionPick.h | 26 ++++++++++++++---------- libraries/physics/src/PhysicsEngine.cpp | 9 ++++----- libraries/physics/src/PhysicsEngine.h | 2 +- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index a70882eed0..f05fb208f1 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -15,8 +15,8 @@ #include "ScriptEngineLogging.h" #include "UUIDHasher.h" -void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::vector& objectIntersections, std::unordered_map& intersections, std::unordered_map& collisionPointPairs) { - for (auto& objectIntersection : objectIntersections) { +void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::shared_ptr> objectIntersections, std::unordered_map& intersections, std::unordered_map& collisionPointPairs) { + for (auto& objectIntersection : *objectIntersections) { auto at = intersections.find(objectIntersection.foundID); if (at == intersections.end()) { QVariantMap intersectingObject; @@ -308,16 +308,17 @@ CollisionRegion CollisionPick::getMathematicalPick() const { return _mathPick; } -void CollisionPick::filterIntersections(std::vector& intersections) const { +void CollisionPick::filterIntersections(std::shared_ptr> intersections) const { const QVector& ignoreItems = getIgnoreItems(); const QVector& includeItems = getIncludeItems(); bool isWhitelist = includeItems.size(); - for (int i = 0; i < intersections.size(); i++) { - auto& intersection = intersections[i]; + int n = (int)intersections->size(); + for (int i = 0; i < n; i++) { + auto& intersection = (*intersections)[i]; const QUuid& id = intersection.foundID; if (ignoreItems.contains(id) || (isWhitelist && !includeItems.contains(id))) { - intersections[i] = intersections[intersections.size()-1]; - intersections.pop_back(); + intersection = (*intersections)[--n]; + intersections->pop_back(); } } } @@ -325,29 +326,29 @@ void CollisionPick::filterIntersections(std::vector& intersec PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { if (!isShapeInfoReady()) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } auto& entityIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform); filterIntersections(entityIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); + return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::make_shared>()); } PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { if (!isShapeInfoReady()) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } auto& avatarIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform); filterIntersections(avatarIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); + return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::make_shared>(), avatarIntersections); } PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); + return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } \ No newline at end of file diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index bfc3487a62..2f383f3a58 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -24,10 +24,14 @@ public: CollisionPickResult() {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} - CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector& entityIntersections, const std::vector& avatarIntersections) : + CollisionPickResult(const CollisionRegion& searchRegion, + LoadState loadState, + std::shared_ptr> entityIntersections = std::make_shared>(), + std::shared_ptr> avatarIntersections = std::make_shared>() + ) : PickResult(searchRegion.toVariantMap()), loadState(loadState), - intersects(entityIntersections.size() || avatarIntersections.size()), + intersects(entityIntersections->size() || avatarIntersections->size()), entityIntersections(entityIntersections), avatarIntersections(avatarIntersections) { } @@ -41,8 +45,8 @@ public: LoadState loadState { LOAD_STATE_UNKNOWN }; bool intersects { false }; - std::vector entityIntersections; - std::vector avatarIntersections; + std::shared_ptr> entityIntersections { std::make_shared>() }; + std::shared_ptr> avatarIntersections { std::make_shared>() }; QVariantMap toVariantMap() const override; @@ -52,14 +56,14 @@ public: PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override { const std::shared_ptr newCollisionResult = std::static_pointer_cast(newRes); - for (ContactTestResult& entityIntersection : newCollisionResult->entityIntersections) { - entityIntersections.push_back(entityIntersection); + for (ContactTestResult& entityIntersection : *(newCollisionResult->entityIntersections)) { + entityIntersections->push_back(entityIntersection); } - for (ContactTestResult& avatarIntersection : newCollisionResult->avatarIntersections) { - avatarIntersections.push_back(avatarIntersection); + for (ContactTestResult& avatarIntersection : *(newCollisionResult->avatarIntersections)) { + avatarIntersections->push_back(avatarIntersection); } - intersects = entityIntersections.size() || avatarIntersections.size(); + intersects = entityIntersections->size() || avatarIntersections->size(); if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) { loadState = (LoadState)newCollisionResult->loadState; } @@ -81,7 +85,7 @@ public: CollisionRegion getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { - return std::make_shared(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector(), std::vector()); + return std::make_shared(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::make_shared>(), std::make_shared>()); } PickResultPointer getEntityIntersection(const CollisionRegion& pick) override; PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; @@ -92,7 +96,7 @@ protected: // Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. bool isShapeInfoReady(); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); - void filterIntersections(std::vector& intersections) const; + void filterIntersections(std::shared_ptr> intersections) const; CollisionRegion _mathPick; PhysicsEnginePointer _physicsEngine; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index cfe255e561..83ad0dc24a 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -871,7 +871,6 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { btCollisionWorld::ContactResultCallback(), desiredObjectType(desiredObjectType), collisionObject(), - contacts(), myAvatarCollisionObject(myAvatarCollisionObject) { const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); @@ -898,7 +897,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { MotionStateType desiredObjectType; btCollisionObject collisionObject; - std::vector contacts; + std::shared_ptr> contacts = std::make_shared>(); btCollisionObject* myAvatarCollisionObject; btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override { @@ -917,7 +916,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { // TODO: Give MyAvatar a motion state so we don't have to do this if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && myAvatarCollisionObject && myAvatarCollisionObject == otherBody) { - contacts.emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); + contacts->emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); return 0; } @@ -933,7 +932,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { } // This is the correct object type. Add it to the list. - contacts.emplace_back(candidate->getObjectID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); + contacts->emplace_back(candidate->getObjectID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); return 0; } @@ -944,7 +943,7 @@ protected: } }; -std::vector PhysicsEngine::getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const { +std::shared_ptr> PhysicsEngine::getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const { // TODO: Give MyAvatar a motion state so we don't have to do this btCollisionObject* myAvatarCollisionObject = nullptr; if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && _myAvatarController) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 854d61844f..64109199ed 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -126,7 +126,7 @@ public: void setShowBulletConstraintLimits(bool value); // Function for getting colliding ObjectMotionStates in the world of specified type - std::vector getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const; + std::shared_ptr> getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const; private: QList removeDynamicsForBody(btRigidBody* body); From 67ff05739a2c7d4593081d954391f40e76a60d59 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Wed, 15 Aug 2018 11:05:52 -0700 Subject: [PATCH 045/207] Take advantage of CollisionPickResult intersections being shared pointers to avoid copying on result comparison --- interface/src/raypick/CollisionPick.cpp | 27 +++++++++++++++++++++++++ interface/src/raypick/CollisionPick.h | 18 +---------------- 2 files changed, 28 insertions(+), 17 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index f05fb208f1..d763fc5c27 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -15,6 +15,33 @@ #include "ScriptEngineLogging.h" #include "UUIDHasher.h" +PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResultPointer& newRes) { + const std::shared_ptr newCollisionResult = std::static_pointer_cast(newRes); + + if (entityIntersections->size()) { + for (ContactTestResult& entityIntersection : *(newCollisionResult->entityIntersections)) { + entityIntersections->push_back(entityIntersection); + } + } else { + entityIntersections = newCollisionResult->entityIntersections; + } + + if (avatarIntersections->size()) { + for (ContactTestResult& avatarIntersection : *(newCollisionResult->avatarIntersections)) { + avatarIntersections->push_back(avatarIntersection); + } + } else { + avatarIntersections = newCollisionResult->avatarIntersections; + } + + intersects = entityIntersections->size() || avatarIntersections->size(); + if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) { + loadState = (LoadState)newCollisionResult->loadState; + } + + return std::make_shared(*this); +} + void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::shared_ptr> objectIntersections, std::unordered_map& intersections, std::unordered_map& collisionPointPairs) { for (auto& objectIntersection : *objectIntersections) { auto at = intersections.find(objectIntersection.foundID); diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 2f383f3a58..40f2450776 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -53,23 +53,7 @@ public: bool doesIntersect() const override { return intersects; } bool checkOrFilterAgainstMaxDistance(float maxDistance) override { return true; } - PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override { - const std::shared_ptr newCollisionResult = std::static_pointer_cast(newRes); - - for (ContactTestResult& entityIntersection : *(newCollisionResult->entityIntersections)) { - entityIntersections->push_back(entityIntersection); - } - for (ContactTestResult& avatarIntersection : *(newCollisionResult->avatarIntersections)) { - avatarIntersections->push_back(avatarIntersection); - } - - intersects = entityIntersections->size() || avatarIntersections->size(); - if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) { - loadState = (LoadState)newCollisionResult->loadState; - } - - return std::make_shared(*this); - } + PickResultPointer compareAndProcessNewResult(const PickResultPointer& newRes) override; }; class CollisionPick : public Pick { From 130cb70a52d55c9d04c97da5780d56f213f0b333 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Thu, 16 Aug 2018 10:39:13 -0700 Subject: [PATCH 046/207] Do not create a reference to a shared pointer in CollisionPick.cpp --- interface/src/raypick/CollisionPick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index d763fc5c27..d065d6fabe 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -356,7 +356,7 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } - auto& entityIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform); + auto entityIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform); filterIntersections(entityIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::make_shared>()); } @@ -371,7 +371,7 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } - auto& avatarIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform); + auto avatarIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform); filterIntersections(avatarIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::make_shared>(), avatarIntersections); } From 2933038d04ee0a4a55d46a2ea7110790df4d381b Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 17 Aug 2018 12:50:50 -0700 Subject: [PATCH 047/207] Rename PhysicsEngine::getCollidingInRegion to contactTest and change it to accept collision flags instead of a MotionState enum --- interface/src/raypick/CollisionPick.cpp | 5 ++-- libraries/physics/src/PhysicsEngine.cpp | 26 ++++++------------- libraries/physics/src/PhysicsEngine.h | 5 ++-- libraries/shared/src/PhysicsCollisionGroups.h | 1 + 4 files changed, 15 insertions(+), 22 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index d065d6fabe..59fe6504ca 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -12,6 +12,7 @@ #include +#include "PhysicsCollisionGroups.h" #include "ScriptEngineLogging.h" #include "UUIDHasher.h" @@ -356,7 +357,7 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } - auto entityIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_ENTITY, *pick.shapeInfo, pick.transform); + auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); filterIntersections(entityIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::make_shared>()); } @@ -371,7 +372,7 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } - auto avatarIntersections = _physicsEngine->getCollidingInRegion(MOTIONSTATE_TYPE_AVATAR, *pick.shapeInfo, pick.transform); + auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); filterIntersections(avatarIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::make_shared>(), avatarIntersections); } diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 83ad0dc24a..37afadc21b 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -863,13 +863,9 @@ void PhysicsEngine::setShowBulletConstraintLimits(bool value) { } } -const int32_t CONTACT_CALLBACK_FLAG_ENTITY = BULLET_COLLISION_GROUP_STATIC | BULLET_COLLISION_GROUP_KINEMATIC | BULLET_COLLISION_GROUP_DYNAMIC; -const int32_t CONTACT_CALLBACK_FLAG_AVATAR = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR; - struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { - AllContactsCallback(MotionStateType desiredObjectType, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) : + AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) : btCollisionWorld::ContactResultCallback(), - desiredObjectType(desiredObjectType), collisionObject(), myAvatarCollisionObject(myAvatarCollisionObject) { const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); @@ -882,20 +878,14 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { collisionObject.setWorldTransform(bulletTransform); - m_collisionFilterGroup = ~0; // Everything collidable should collide with our test object unless we set the filter mask otherwise - if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR) { - m_collisionFilterMask = CONTACT_CALLBACK_FLAG_AVATAR; - } - else { - m_collisionFilterMask = CONTACT_CALLBACK_FLAG_ENTITY; - } + m_collisionFilterMask = mask; + m_collisionFilterGroup = group; } ~AllContactsCallback() { ObjectMotionState::getShapeManager()->releaseShape(collisionObject.getCollisionShape()); } - MotionStateType desiredObjectType; btCollisionObject collisionObject; std::shared_ptr> contacts = std::make_shared>(); btCollisionObject* myAvatarCollisionObject; @@ -915,7 +905,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { } // TODO: Give MyAvatar a motion state so we don't have to do this - if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && myAvatarCollisionObject && myAvatarCollisionObject == otherBody) { + if ((m_collisionFilterMask & BULLET_COLLISION_GROUP_MY_AVATAR) && myAvatarCollisionObject && myAvatarCollisionObject == otherBody) { contacts->emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); return 0; } @@ -927,7 +917,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { const btMotionState* motionStateCandidate = collisionCandidate->getMotionState(); const ObjectMotionState* candidate = dynamic_cast(motionStateCandidate); - if (!candidate || candidate->getType() != desiredObjectType) { + if (!candidate) { return 0; } @@ -943,14 +933,14 @@ protected: } }; -std::shared_ptr> PhysicsEngine::getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const { +std::shared_ptr> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const { // TODO: Give MyAvatar a motion state so we don't have to do this btCollisionObject* myAvatarCollisionObject = nullptr; - if (desiredObjectType == MOTIONSTATE_TYPE_AVATAR && _myAvatarController) { + if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) { myAvatarCollisionObject = _myAvatarController->getCollisionObject(); } - auto contactCallback = AllContactsCallback(desiredObjectType, regionShapeInfo, regionTransform, myAvatarCollisionObject); + auto contactCallback = AllContactsCallback((int32_t)mask, (int32_t)group, regionShapeInfo, regionTransform, myAvatarCollisionObject); _dynamicsWorld->contactTest(&contactCallback.collisionObject, contactCallback); return contactCallback.contacts; diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 64109199ed..7c4d5392d6 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -125,8 +125,9 @@ public: void setShowBulletConstraints(bool value); void setShowBulletConstraintLimits(bool value); - // Function for getting colliding ObjectMotionStates in the world of specified type - std::shared_ptr> getCollidingInRegion(MotionStateType desiredObjectType, const ShapeInfo& regionShapeInfo, const Transform& regionTransform) const; + // Function for getting colliding objects in the world of specified type + // See PhysicsCollisionGroups.h for mask flags. + std::shared_ptr> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; private: QList removeDynamicsForBody(btRigidBody* body); diff --git a/libraries/shared/src/PhysicsCollisionGroups.h b/libraries/shared/src/PhysicsCollisionGroups.h index ef18cb0b0e..cae3918a3f 100644 --- a/libraries/shared/src/PhysicsCollisionGroups.h +++ b/libraries/shared/src/PhysicsCollisionGroups.h @@ -104,6 +104,7 @@ const uint16_t ENTITY_COLLISION_MASK_DEFAULT = USER_COLLISION_GROUP_OTHER_AVATAR; const uint16_t USER_COLLISION_MASK_AVATARS = USER_COLLISION_GROUP_MY_AVATAR | USER_COLLISION_GROUP_OTHER_AVATAR; +const uint16_t USER_COLLISION_MASK_ENTITIES = USER_COLLISION_GROUP_STATIC | USER_COLLISION_GROUP_DYNAMIC | USER_COLLISION_GROUP_KINEMATIC; const int32_t NUM_USER_COLLISION_GROUPS = 5; From e3c39020dcd04da661ad87c833d411ce7f5f4327 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 17 Aug 2018 12:54:47 -0700 Subject: [PATCH 048/207] Exit early from CollisionPick::filterIntersections on empty blacklist --- interface/src/raypick/CollisionPick.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 59fe6504ca..52650966d3 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -340,6 +340,11 @@ void CollisionPick::filterIntersections(std::shared_ptr& ignoreItems = getIgnoreItems(); const QVector& includeItems = getIncludeItems(); bool isWhitelist = includeItems.size(); + + if (!isWhitelist && !ignoreItems.size()) { + return; + } + int n = (int)intersections->size(); for (int i = 0; i < n; i++) { auto& intersection = (*intersections)[i]; From 6081d63f5317cc0bab726029dde9b727d4a7bfb1 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 17 Aug 2018 13:17:35 -0700 Subject: [PATCH 049/207] Use std::vector::insert for merging CollisionPickResults --- interface/src/raypick/CollisionPick.cpp | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 52650966d3..a7ee534528 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -20,17 +20,13 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu const std::shared_ptr newCollisionResult = std::static_pointer_cast(newRes); if (entityIntersections->size()) { - for (ContactTestResult& entityIntersection : *(newCollisionResult->entityIntersections)) { - entityIntersections->push_back(entityIntersection); - } + entityIntersections->insert(entityIntersections->cend(), newCollisionResult->entityIntersections->begin(), newCollisionResult->entityIntersections->end()); } else { entityIntersections = newCollisionResult->entityIntersections; } if (avatarIntersections->size()) { - for (ContactTestResult& avatarIntersection : *(newCollisionResult->avatarIntersections)) { - avatarIntersections->push_back(avatarIntersection); - } + avatarIntersections->insert(avatarIntersections->cend(), newCollisionResult->avatarIntersections->begin(), newCollisionResult->avatarIntersections->end()); } else { avatarIntersections = newCollisionResult->avatarIntersections; } From 51940898d9a7027a1b20e6fcb4bf78ac3d654b73 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Fri, 17 Aug 2018 16:23:31 -0700 Subject: [PATCH 050/207] Do not implicitly convert size of vector to bool in CollisionPick::filterIntersections --- interface/src/raypick/CollisionPick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index a7ee534528..01264a252d 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -335,9 +335,9 @@ CollisionRegion CollisionPick::getMathematicalPick() const { void CollisionPick::filterIntersections(std::shared_ptr> intersections) const { const QVector& ignoreItems = getIgnoreItems(); const QVector& includeItems = getIncludeItems(); - bool isWhitelist = includeItems.size(); + bool isWhitelist = !includeItems.empty(); - if (!isWhitelist && !ignoreItems.size()) { + if (!isWhitelist && ignoreItems.empty()) { return; } From 877bce23d2936a2dbbd73ab9423e583ff6d47eb8 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 18 Aug 2018 16:32:31 +0300 Subject: [PATCH 051/207] explicitely specify localRotations on joint change, otherwise it will be updated by engine --- interface/resources/qml/hifi/avatarapp/AdjustWearables.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 2b50fbdf34..bec037fdf2 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -286,6 +286,11 @@ Rectangle { x: positionVector.xvalue, y: positionVector.yvalue, z: positionVector.zvalue + }, + localRotationAngles: { + x: rotationVector.xvalue, + y: rotationVector.yvalue, + z: rotationVector.zvalue, } }; From 3bcd6f4a25fb46df70a4e49697a348f6bd942972 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 18 Aug 2018 16:33:30 +0300 Subject: [PATCH 052/207] take 'relayParentJoints' into account during wearables comparison --- interface/resources/qml/hifi/avatarapp/AvatarsModel.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml index 931a041747..9b78dee5bc 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml @@ -164,6 +164,7 @@ ListModel { {'propertyName' : 'marketplaceID'}, {'propertyName' : 'itemName'}, {'propertyName' : 'script'}, + {'propertyName' : 'relayParentJoints'}, {'propertyName' : 'localPosition', 'comparer' : compareNumericObjects}, {'propertyName' : 'localRotationAngles', 'comparer' : compareNumericObjects}, {'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties') From 9f92ff5de9b49582466cc1c3a31958590baa891e Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 18 Aug 2018 16:38:35 +0300 Subject: [PATCH 053/207] fix for duplicating wearables on saving note: the issue was caused by newly-introduced code converting between attachments & entities fix removes attachments-related code because attachments are deprecated and converted into entities on the fly --- .../resources/qml/hifi/avatarapp/AvatarsModel.qml | 12 ------------ interface/src/AvatarBookmarks.cpp | 1 - scripts/system/avatarapp.js | 2 -- 3 files changed, 15 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml index 9b78dee5bc..f9a231ec48 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml @@ -42,7 +42,6 @@ ListModel { 'thumbnailUrl' : avatarThumbnailUrl, 'avatarUrl' : avatar.avatarUrl, 'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [], - 'attachments' : avatar.attachments ? avatar.attachments : [], 'entry' : avatar, 'getMoreAvatars' : false }; @@ -170,13 +169,6 @@ ListModel { {'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties') } - function compareAttachments(a1, a2) { - return compareObjects(a1, a2, [{'propertyName' : 'position', 'comparer' : compareNumericObjects}, - {'propertyName' : 'orientation'}, - {'propertyName' : 'parentJointIndex'}, - {'propertyName' : 'modelurl'}]) - } - function findAvatarIndexByValue(avatar) { var index = -1; @@ -192,10 +184,6 @@ ListModel { if(bookmarkedAvatar.avatarScale !== avatar.avatarScale) continue; - if(!modelsAreEqual(bookmarkedAvatar.attachments, avatar.attachments, compareAttachments)) { - continue; - } - if(!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) { continue; } diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index d8fc30963b..6ebd7b0d9e 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -255,7 +255,6 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale); - bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); QScriptEngine scriptEngine; QVariantList wearableEntities; diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 85946c644a..10ccb66d96 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -20,7 +20,6 @@ Script.include("/~/system/libraries/controllers.js"); // constants from AvatarBookmarks.h var ENTRY_AVATAR_URL = "avatarUrl"; -var ENTRY_AVATAR_ATTACHMENTS = "attachments"; var ENTRY_AVATAR_ENTITIES = "avatarEntites"; var ENTRY_AVATAR_SCALE = "avatarScale"; var ENTRY_VERSION = "version"; @@ -57,7 +56,6 @@ function getMyAvatar() { var avatar = {} avatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL; avatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale(); - avatar[ENTRY_AVATAR_ATTACHMENTS] = MyAvatar.getAttachmentsVariant(); avatar[ENTRY_AVATAR_ENTITIES] = getMyAvatarWearables(); return avatar; } From 50cc68c63eec993e9a30ce43e3770c589c2f8a05 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Sun, 19 Aug 2018 12:43:06 -0700 Subject: [PATCH 054/207] Recording app doesn't record old attachments --- libraries/avatars/src/AvatarData.cpp | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 62c7a7053f..bc504b4496 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2255,7 +2255,6 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); // It isn't meaningful to persist sessionDisplayName. -static const QString JSON_AVATAR_ATTACHMENTS = QStringLiteral("attachments"); static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); static const QString JSON_AVATAR_VERSION = QStringLiteral("version"); @@ -2303,13 +2302,6 @@ QJsonObject AvatarData::toJson() const { if (!getDisplayName().isEmpty()) { root[JSON_AVATAR_DISPLAY_NAME] = getDisplayName(); } - if (!getAttachmentData().isEmpty()) { - QJsonArray attachmentsJson; - for (auto attachment : getAttachmentData()) { - attachmentsJson.push_back(attachment.toJson()); - } - root[JSON_AVATAR_ATTACHMENTS] = attachmentsJson; - } _avatarEntitiesLock.withReadLock([&] { if (!_avatarEntityData.empty()) { @@ -2426,26 +2418,6 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble()); } - QVector attachments; - if (json.contains(JSON_AVATAR_ATTACHMENTS) && json[JSON_AVATAR_ATTACHMENTS].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); - for (auto attachmentJson : attachmentsJson) { - AttachmentData attachment; - attachment.fromJson(attachmentJson.toObject()); - attachments.push_back(attachment); - } - } - if (attachments != getAttachmentData()) { - setAttachmentData(attachments); - } - - // if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { - // QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); - // for (auto attachmentJson : attachmentsJson) { - // // TODO -- something - // } - // } - if (json.contains(JSON_AVATAR_JOINT_ARRAY)) { if (version == (int)JsonAvatarFrameVersion::JointRotationsInRelativeFrame) { // because we don't have the full joint hierarchy skeleton of the model, From 2a182a00337ae57edc0109c77db8aca6cd4506fe Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 20 Aug 2018 09:24:07 -0700 Subject: [PATCH 055/207] Revert "Do not create a reference to a shared pointer in CollisionPick.cpp" This reverts commit 130cb70a52d55c9d04c97da5780d56f213f0b333. --- interface/src/raypick/CollisionPick.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 01264a252d..004017ca15 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -358,7 +358,7 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } - auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); + auto& entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); filterIntersections(entityIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::make_shared>()); } @@ -372,8 +372,8 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi // Cannot compute result return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); } - - auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); + + auto &avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); filterIntersections(avatarIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::make_shared>(), avatarIntersections); } From 74f482b36133640d52725e6149e6af64c48ae28f Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 20 Aug 2018 10:15:28 -0700 Subject: [PATCH 056/207] Revert "Convert entityIntersections/avatarIntersections lists in" This reverts commit aa4a6b2eaedb8f578d7bcd2a2dcd7a4e112028f0. --- interface/src/raypick/CollisionPick.cpp | 36 ++++++++++++------------- interface/src/raypick/CollisionPick.h | 16 +++++------ libraries/physics/src/PhysicsEngine.cpp | 9 ++++--- libraries/physics/src/PhysicsEngine.h | 2 +- 4 files changed, 30 insertions(+), 33 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 004017ca15..b93c5b6dbb 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -19,19 +19,19 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResultPointer& newRes) { const std::shared_ptr newCollisionResult = std::static_pointer_cast(newRes); - if (entityIntersections->size()) { - entityIntersections->insert(entityIntersections->cend(), newCollisionResult->entityIntersections->begin(), newCollisionResult->entityIntersections->end()); + if (entityIntersections.size()) { + entityIntersections.insert(entityIntersections.cend(), newCollisionResult->entityIntersections.begin(), newCollisionResult->entityIntersections.end()); } else { entityIntersections = newCollisionResult->entityIntersections; } - if (avatarIntersections->size()) { - avatarIntersections->insert(avatarIntersections->cend(), newCollisionResult->avatarIntersections->begin(), newCollisionResult->avatarIntersections->end()); + if (avatarIntersections.size()) { + avatarIntersections.insert(avatarIntersections.cend(), newCollisionResult->avatarIntersections.begin(), newCollisionResult->avatarIntersections.end()); } else { avatarIntersections = newCollisionResult->avatarIntersections; } - intersects = entityIntersections->size() || avatarIntersections->size(); + intersects = entityIntersections.size() || avatarIntersections.size(); if (newCollisionResult->loadState == LOAD_STATE_NOT_LOADED || loadState == LOAD_STATE_UNKNOWN) { loadState = (LoadState)newCollisionResult->loadState; } @@ -39,8 +39,8 @@ PickResultPointer CollisionPickResult::compareAndProcessNewResult(const PickResu return std::make_shared(*this); } -void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::shared_ptr> objectIntersections, std::unordered_map& intersections, std::unordered_map& collisionPointPairs) { - for (auto& objectIntersection : *objectIntersections) { +void buildObjectIntersectionsMap(IntersectionType intersectionType, const std::vector& objectIntersections, std::unordered_map& intersections, std::unordered_map& collisionPointPairs) { + for (auto& objectIntersection : objectIntersections) { auto at = intersections.find(objectIntersection.foundID); if (at == intersections.end()) { QVariantMap intersectingObject; @@ -332,7 +332,7 @@ CollisionRegion CollisionPick::getMathematicalPick() const { return _mathPick; } -void CollisionPick::filterIntersections(std::shared_ptr> intersections) const { +void CollisionPick::filterIntersections(std::vector& intersections) const { const QVector& ignoreItems = getIgnoreItems(); const QVector& includeItems = getIncludeItems(); bool isWhitelist = !includeItems.empty(); @@ -341,13 +341,13 @@ void CollisionPick::filterIntersections(std::shared_ptrsize(); + int n = (int)intersections.size(); for (int i = 0; i < n; i++) { - auto& intersection = (*intersections)[i]; + auto& intersection = intersections[i]; const QUuid& id = intersection.foundID; if (ignoreItems.contains(id) || (isWhitelist && !includeItems.contains(id))) { - intersection = (*intersections)[--n]; - intersections->pop_back(); + intersections[i] = intersections[--n]; + intersections.pop_back(); } } } @@ -355,29 +355,29 @@ void CollisionPick::filterIntersections(std::shared_ptr(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); + return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } auto& entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); filterIntersections(entityIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::make_shared>()); + return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); } PickResultPointer CollisionPick::getOverlayIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); + return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pick) { if (!isShapeInfoReady()) { // Cannot compute result - return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); + return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } auto &avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); filterIntersections(avatarIntersections); - return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::make_shared>(), avatarIntersections); + return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); } PickResultPointer CollisionPick::getHUDIntersection(const CollisionRegion& pick) { - return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::make_shared>(), std::make_shared>()); + return std::make_shared(pick.toVariantMap(), isShapeInfoReady() ? CollisionPickResult::LOAD_STATE_LOADED : CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } \ No newline at end of file diff --git a/interface/src/raypick/CollisionPick.h b/interface/src/raypick/CollisionPick.h index 40f2450776..6631238737 100644 --- a/interface/src/raypick/CollisionPick.h +++ b/interface/src/raypick/CollisionPick.h @@ -24,14 +24,10 @@ public: CollisionPickResult() {} CollisionPickResult(const QVariantMap& pickVariant) : PickResult(pickVariant) {} - CollisionPickResult(const CollisionRegion& searchRegion, - LoadState loadState, - std::shared_ptr> entityIntersections = std::make_shared>(), - std::shared_ptr> avatarIntersections = std::make_shared>() - ) : + CollisionPickResult(const CollisionRegion& searchRegion, LoadState loadState, const std::vector& entityIntersections, const std::vector& avatarIntersections) : PickResult(searchRegion.toVariantMap()), loadState(loadState), - intersects(entityIntersections->size() || avatarIntersections->size()), + intersects(entityIntersections.size() || avatarIntersections.size()), entityIntersections(entityIntersections), avatarIntersections(avatarIntersections) { } @@ -45,8 +41,8 @@ public: LoadState loadState { LOAD_STATE_UNKNOWN }; bool intersects { false }; - std::shared_ptr> entityIntersections { std::make_shared>() }; - std::shared_ptr> avatarIntersections { std::make_shared>() }; + std::vector entityIntersections; + std::vector avatarIntersections; QVariantMap toVariantMap() const override; @@ -69,7 +65,7 @@ public: CollisionRegion getMathematicalPick() const override; PickResultPointer getDefaultResult(const QVariantMap& pickVariant) const override { - return std::make_shared(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::make_shared>(), std::make_shared>()); + return std::make_shared(pickVariant, CollisionPickResult::LOAD_STATE_UNKNOWN, std::vector(), std::vector()); } PickResultPointer getEntityIntersection(const CollisionRegion& pick) override; PickResultPointer getOverlayIntersection(const CollisionRegion& pick) override; @@ -80,7 +76,7 @@ protected: // Returns true if pick.shapeInfo is valid. Otherwise, attempts to get the shapeInfo ready for use. bool isShapeInfoReady(); void computeShapeInfo(CollisionRegion& pick, ShapeInfo& shapeInfo, QSharedPointer resource); - void filterIntersections(std::shared_ptr> intersections) const; + void filterIntersections(std::vector& intersections) const; CollisionRegion _mathPick; PhysicsEnginePointer _physicsEngine; diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 37afadc21b..5685e8a6f3 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -867,6 +867,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { AllContactsCallback(int32_t mask, int32_t group, const ShapeInfo& shapeInfo, const Transform& transform, btCollisionObject* myAvatarCollisionObject) : btCollisionWorld::ContactResultCallback(), collisionObject(), + contacts(), myAvatarCollisionObject(myAvatarCollisionObject) { const btCollisionShape* collisionShape = ObjectMotionState::getShapeManager()->getShape(shapeInfo); @@ -887,7 +888,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { } btCollisionObject collisionObject; - std::shared_ptr> contacts = std::make_shared>(); + std::vector contacts; btCollisionObject* myAvatarCollisionObject; btScalar addSingleResult(btManifoldPoint& cp, const btCollisionObjectWrapper* colObj0, int partId0, int index0, const btCollisionObjectWrapper* colObj1, int partId1, int index1) override { @@ -906,7 +907,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { // TODO: Give MyAvatar a motion state so we don't have to do this if ((m_collisionFilterMask & BULLET_COLLISION_GROUP_MY_AVATAR) && myAvatarCollisionObject && myAvatarCollisionObject == otherBody) { - contacts->emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); + contacts.emplace_back(Physics::getSessionUUID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); return 0; } @@ -922,7 +923,7 @@ struct AllContactsCallback : public btCollisionWorld::ContactResultCallback { } // This is the correct object type. Add it to the list. - contacts->emplace_back(candidate->getObjectID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); + contacts.emplace_back(candidate->getObjectID(), bulletToGLM(penetrationPoint), bulletToGLM(otherPenetrationPoint)); return 0; } @@ -933,7 +934,7 @@ protected: } }; -std::shared_ptr> PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const { +std::vector PhysicsEngine::contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group) const { // TODO: Give MyAvatar a motion state so we don't have to do this btCollisionObject* myAvatarCollisionObject = nullptr; if ((mask & USER_COLLISION_GROUP_MY_AVATAR) && _myAvatarController) { diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 7c4d5392d6..c6e165632b 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -127,7 +127,7 @@ public: // Function for getting colliding objects in the world of specified type // See PhysicsCollisionGroups.h for mask flags. - std::shared_ptr> contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; + std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; private: QList removeDynamicsForBody(btRigidBody* body); From d9b136cf5d3aa2bfb60058fc42175465bac7c317 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 20 Aug 2018 11:06:51 -0700 Subject: [PATCH 057/207] Read resources from other than the main directory --- libraries/fbx/src/GLTFReader.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libraries/fbx/src/GLTFReader.cpp b/libraries/fbx/src/GLTFReader.cpp index 300e6f4846..317342b886 100644 --- a/libraries/fbx/src/GLTFReader.cpp +++ b/libraries/fbx/src/GLTFReader.cpp @@ -935,7 +935,7 @@ FBXGeometry* GLTFReader::readGLTF(QByteArray& model, const QVariantHash& mapping } bool GLTFReader::readBinary(const QString& url, QByteArray& outdata) { - QUrl binaryUrl = _url.resolved(QUrl(url).fileName()); + QUrl binaryUrl = _url.resolved(url); qCDebug(modelformat) << "binaryUrl: " << binaryUrl << " OriginalUrl: " << _url; bool success; @@ -948,7 +948,7 @@ bool GLTFReader::doesResourceExist(const QString& url) { if (_url.isEmpty()) { return false; } - QUrl candidateUrl = _url.resolved(QUrl(url).fileName()); + QUrl candidateUrl = _url.resolved(url); return DependencyManager::get()->resourceExists(candidateUrl); } @@ -1001,8 +1001,9 @@ FBXTexture GLTFReader::getFBXTexture(const GLTFTexture& texture) { fbxtex.texcoordSet = 0; if (texture.defined["source"]) { - QString fname = QUrl(_file.images[texture.source].uri).fileName(); - QUrl textureUrl = _url.resolved(fname); + QString url = _file.images[texture.source].uri; + QString fname = QUrl(url).fileName(); + QUrl textureUrl = _url.resolved(url); qCDebug(modelformat) << "fname: " << fname; qCDebug(modelformat) << "textureUrl: " << textureUrl; qCDebug(modelformat) << "Url: " << _url; From 444224a11a558fe4b6a856af70a8be4d122c24e5 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 20 Aug 2018 11:07:52 -0700 Subject: [PATCH 058/207] Fix ignoring elements in CollisionPick::filterIntersections --- interface/src/raypick/CollisionPick.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index b93c5b6dbb..8162b27b60 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -348,6 +348,7 @@ void CollisionPick::filterIntersections(std::vector& intersec if (ignoreItems.contains(id) || (isWhitelist && !includeItems.contains(id))) { intersections[i] = intersections[--n]; intersections.pop_back(); + --i; } } } From 813290aad81e6b6203b840e04a23056d44da64f1 Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 20 Aug 2018 11:23:22 -0700 Subject: [PATCH 059/207] Do filtering with copy vector in CollisionPick::filterIntersections --- interface/src/raypick/CollisionPick.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 8162b27b60..889a241bcc 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -341,16 +341,18 @@ void CollisionPick::filterIntersections(std::vector& intersec return; } + std::vector filteredIntersections; + int n = (int)intersections.size(); for (int i = 0; i < n; i++) { auto& intersection = intersections[i]; const QUuid& id = intersection.foundID; - if (ignoreItems.contains(id) || (isWhitelist && !includeItems.contains(id))) { - intersections[i] = intersections[--n]; - intersections.pop_back(); - --i; + if (!ignoreItems.contains(id) && (!isWhitelist || includeItems.contains(id))) { + filteredIntersections.push_back(intersection); } } + + intersections = filteredIntersections; } PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pick) { From 05ff99a1d2f2f0aa8387bd505d665de26bc58519 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 9 Aug 2018 18:39:19 -0700 Subject: [PATCH 060/207] working on blendshapes --- interface/src/avatar/AvatarManager.cpp | 4 +- .../render-utils/src/CauterizedModel.cpp | 5 ++ .../render-utils/src/MeshPartPayload.cpp | 5 +- libraries/render-utils/src/Model.cpp | 54 +++++-------------- libraries/render-utils/src/Model.h | 2 +- 5 files changed, 26 insertions(+), 44 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index dbafd06611..efd1c103d9 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -353,15 +353,14 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { QReadLocker locker(&_hashLock); QVector::iterator avatarItr = _avatarsToFade.begin(); const render::ScenePointer& scene = qApp->getMain3DScene(); + render::Transaction transaction; while (avatarItr != _avatarsToFade.end()) { auto avatar = std::static_pointer_cast(*avatarItr); avatar->updateFadingStatus(scene); if (!avatar->isFading()) { // fading to zero is such a rare event we push a unique transaction for each if (avatar->isInScene()) { - render::Transaction transaction; avatar->removeFromScene(*avatarItr, scene, transaction); - scene->enqueueTransaction(transaction); } avatarItr = _avatarsToFade.erase(avatarItr); } else { @@ -370,6 +369,7 @@ void AvatarManager::simulateAvatarFades(float deltaTime) { ++avatarItr; } } + scene->enqueueTransaction(transaction); } AvatarSharedPointer AvatarManager::newSharedAvatar() { diff --git a/libraries/render-utils/src/CauterizedModel.cpp b/libraries/render-utils/src/CauterizedModel.cpp index 80a9c5ccae..ba394b8b2f 100644 --- a/libraries/render-utils/src/CauterizedModel.cpp +++ b/libraries/render-utils/src/CauterizedModel.cpp @@ -75,6 +75,7 @@ void CauterizedModel::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; + const FBXGeometry& fbxGeometry = getFBXGeometry(); uint32_t numMeshes = (uint32_t)meshes.size(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); @@ -85,6 +86,10 @@ void CauterizedModel::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (!fbxGeometry.meshes[i].blendshapes.empty()) { + _blendedVertexBuffers[i] = std::make_shared(); + _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + } auto ptr = std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); _modelMeshRenderItems << std::static_pointer_cast(ptr); auto material = getGeometry()->getShapeMaterial(shapeID); diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index ca6e736a65..58f4f170a0 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -208,7 +208,10 @@ ModelMeshPartPayload::ModelMeshPartPayload(ModelPointer model, int meshIndex, in bool useDualQuaternionSkinning = model->getUseDualQuaternionSkinning(); - _blendedVertexBuffer = model->_blendedVertexBuffers[_meshIndex]; + auto blendedVertexBuffer = model->_blendedVertexBuffers.find(_meshIndex); + if (blendedVertexBuffer != model->_blendedVertexBuffers.end()) { + _blendedVertexBuffer = blendedVertexBuffer->second; + } auto& modelMesh = model->getGeometry()->getMeshes().at(_meshIndex); const Model::MeshState& state = model->getMeshState(_meshIndex); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ba0d714f7a..f92a6809f3 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -305,36 +305,6 @@ bool Model::updateGeometry() { state.clusterDualQuaternions.resize(mesh.clusters.size()); state.clusterMatrices.resize(mesh.clusters.size()); _meshStates.push_back(state); - - // Note: we add empty buffers for meshes that lack blendshapes so we can access the buffers by index - // later in ModelMeshPayload, however the vast majority of meshes will not have them. - // TODO? make _blendedVertexBuffers a map instead of vector and only add for meshes with blendshapes? - auto buffer = std::make_shared(); - if (!mesh.blendshapes.isEmpty()) { - std::vector normalsAndTangents; - normalsAndTangents.reserve(mesh.normals.size() + mesh.tangents.size()); - - for (auto normalIt = mesh.normals.begin(), tangentIt = mesh.tangents.begin(); - normalIt != mesh.normals.end(); - ++normalIt, ++tangentIt) { -#if FBX_PACK_NORMALS - glm::uint32 finalNormal; - glm::uint32 finalTangent; - buffer_helpers::packNormalAndTangent(*normalIt, *tangentIt, finalNormal, finalTangent); -#else - const auto finalNormal = *normalIt; - const auto finalTangent = *tangentIt; -#endif - normalsAndTangents.push_back(finalNormal); - normalsAndTangents.push_back(finalTangent); - } - - buffer->resize(mesh.vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); - buffer->setSubData(0, mesh.vertices.size() * sizeof(glm::vec3), (const gpu::Byte*) mesh.vertices.constData()); - buffer->setSubData(mesh.vertices.size() * sizeof(glm::vec3), - mesh.normals.size() * 2 * sizeof(NormalType), (const gpu::Byte*) normalsAndTangents.data()); - } - _blendedVertexBuffers.push_back(buffer); } needFullUpdate = true; emit rigReady(); @@ -1280,9 +1250,9 @@ void Blender::run() { } // post the result to the geometry cache, which will dispatch to the model if still alive QMetaObject::invokeMethod(DependencyManager::get().data(), "setBlendedVertices", - Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), - Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), - Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); + Q_ARG(ModelPointer, _model), Q_ARG(int, _blendNumber), + Q_ARG(const Geometry::WeakPointer&, _geometry), Q_ARG(const QVector&, vertices), + Q_ARG(const QVector&, normals), Q_ARG(const QVector&, tangents)); } void Model::setScaleToFit(bool scaleToFit, const glm::vec3& dimensions, bool forceRescale) { @@ -1463,7 +1433,7 @@ bool Model::maybeStartBlender() { void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geometry, const QVector& vertices, const QVector& normals, const QVector& tangents) { auto geometryRef = geometry.lock(); - if (!geometryRef || _renderGeometry != geometryRef || _blendedVertexBuffers.empty() || blendNumber < _appliedBlendNumber) { + if (!geometryRef || _renderGeometry != geometryRef || blendNumber < _appliedBlendNumber) { return; } _appliedBlendNumber = blendNumber; @@ -1476,13 +1446,13 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo continue; } - gpu::BufferPointer& buffer = _blendedVertexBuffers[i]; + gpu::BufferPointer buffer = _blendedVertexBuffers[i]; const auto vertexCount = mesh.vertices.size(); const auto verticesSize = vertexCount * sizeof(glm::vec3); const auto offset = index * sizeof(glm::vec3); normalsAndTangents.clear(); - normalsAndTangents.resize(normals.size()+tangents.size()); + normalsAndTangents.resize(normals.size() + tangents.size()); // assert(normalsAndTangents.size() == 2 * vertexCount); // Interleave normals and tangents @@ -1510,7 +1480,7 @@ void Model::setBlendedVertices(int blendNumber, const Geometry::WeakPointer& geo } #else // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(index, index+vertexCount), [&](const tbb::blocked_range& range) { + tbb::parallel_for(tbb::blocked_range(index, index + vertexCount), [&](const tbb::blocked_range& range) { auto normalsRange = std::make_pair(normals.begin() + range.begin(), normals.begin() + range.end()); auto tangentsRange = std::make_pair(tangents.begin() + range.begin(), tangents.begin() + range.end()); auto normalsAndTangentsIt = normalsAndTangents.begin() + (range.begin()-index)*2; @@ -1603,6 +1573,7 @@ void Model::createRenderItemSet() { // Run through all of the meshes, and place them into their segregated, but unsorted buckets int shapeID = 0; uint32_t numMeshes = (uint32_t)meshes.size(); + const FBXGeometry& fbxGeometry = getFBXGeometry(); for (uint32_t i = 0; i < numMeshes; i++) { const auto& mesh = meshes.at(i); if (!mesh) { @@ -1612,6 +1583,10 @@ void Model::createRenderItemSet() { // Create the render payloads int numParts = (int)mesh->getNumParts(); for (int partIndex = 0; partIndex < numParts; partIndex++) { + if (!fbxGeometry.meshes[i].blendshapes.empty()) { + _blendedVertexBuffers[i] = std::make_shared(); + _blendedVertexBuffers[i]->resize(fbxGeometry.meshes[i].vertices.size() * (sizeof(glm::vec3) + 2 * sizeof(NormalType))); + } _modelMeshRenderItems << std::make_shared(shared_from_this(), i, partIndex, shapeID, transform, offset); auto material = getGeometry()->getShapeMaterial(shapeID); _modelMeshMaterialNames.push_back(material ? material->getName() : ""); @@ -1724,8 +1699,8 @@ void ModelBlender::noteRequiresBlend(ModelPointer model) { } } -void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, - const QVector& vertices, const QVector& normals, +void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const Geometry::WeakPointer& geometry, + const QVector& vertices, const QVector& normals, const QVector& tangents) { if (model) { model->setBlendedVertices(blendNumber, geometry, vertices, normals, tangents); @@ -1744,4 +1719,3 @@ void ModelBlender::setBlendedVertices(ModelPointer model, int blendNumber, const } } } - diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 627e5fddab..1d0570f087 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -413,7 +413,7 @@ protected: QUrl _url; - gpu::Buffers _blendedVertexBuffers; + std::unordered_map _blendedVertexBuffers; QVector > > _dilatedTextures; From 3068865a6c8f9624d816e17db6931b22943c8bd7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Aug 2018 11:42:28 -0700 Subject: [PATCH 061/207] fix MyAvatar collision bug at transitions b/w meshes --- libraries/physics/src/PhysicsEngine.cpp | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index ef8c28bdc6..3837cee7a8 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -28,24 +28,6 @@ #include "PhysicsLogging.h" -static bool flipNormalsMyAvatarVsBackfacingTriangles( btManifoldPoint& cp, - const btCollisionObjectWrapper* colObj0Wrap, int partId0, int index0, - const btCollisionObjectWrapper* colObj1Wrap, int partId1, int index1) { - if (colObj1Wrap->getCollisionShape()->getShapeType() == TRIANGLE_SHAPE_PROXYTYPE) { - auto triShape = static_cast(colObj1Wrap->getCollisionShape()); - const btVector3* v = triShape->m_vertices1; - btVector3 faceNormal = colObj1Wrap->getWorldTransform().getBasis() * btCross(v[1] - v[0], v[2] - v[0]); - float nDotF = btDot(faceNormal, cp.m_normalWorldOnB); - if (nDotF <= 0.0f) { - faceNormal.normalize(); - // flip the contact normal to be aligned with the face normal - cp.m_normalWorldOnB += -2.0f * nDotF * faceNormal; - } - } - // return value is currently ignored but to be future-proof: return false when not modifying friction - return false; -} - PhysicsEngine::PhysicsEngine(const glm::vec3& offset) : _originOffset(offset), _myAvatarController(nullptr) { @@ -88,9 +70,6 @@ void PhysicsEngine::init() { // in order for its broadphase collision queries to work correctly. Look at how we use // _activeStaticBodies to track and update the Aabb's of moved static objects. _dynamicsWorld->setForceUpdateAllAabbs(false); - - // register contact filter to help MyAvatar pass through backfacing triangles - gContactAddedCallback = flipNormalsMyAvatarVsBackfacingTriangles; } } From c564119edc03948c5d4c067769e6c7b19bbcec2f Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 20 Aug 2018 11:58:19 -0700 Subject: [PATCH 062/207] Keep attachments when playing back recordings --- libraries/avatars/src/AvatarData.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bc504b4496..bfe483e8d7 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2255,6 +2255,7 @@ static const QString JSON_AVATAR_HEAD_MODEL = QStringLiteral("headModel"); static const QString JSON_AVATAR_BODY_MODEL = QStringLiteral("bodyModel"); static const QString JSON_AVATAR_DISPLAY_NAME = QStringLiteral("displayName"); // It isn't meaningful to persist sessionDisplayName. +static const QString JSON_AVATAR_ATTACHMENTS = QStringLiteral("attachments"); static const QString JSON_AVATAR_ENTITIES = QStringLiteral("attachedEntities"); static const QString JSON_AVATAR_SCALE = QStringLiteral("scale"); static const QString JSON_AVATAR_VERSION = QStringLiteral("version"); @@ -2302,7 +2303,6 @@ QJsonObject AvatarData::toJson() const { if (!getDisplayName().isEmpty()) { root[JSON_AVATAR_DISPLAY_NAME] = getDisplayName(); } - _avatarEntitiesLock.withReadLock([&] { if (!_avatarEntityData.empty()) { QJsonArray avatarEntityJson; @@ -2418,6 +2418,19 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { setTargetScale((float)json[JSON_AVATAR_SCALE].toDouble()); } + QVector attachments; + if (json.contains(JSON_AVATAR_ATTACHMENTS) && json[JSON_AVATAR_ATTACHMENTS].isArray()) { + QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); + for (auto attachmentJson : attachmentsJson) { + AttachmentData attachment; + attachment.fromJson(attachmentJson.toObject()); + attachments.push_back(attachment); + } + } + if (attachments != getAttachmentData()) { + setAttachmentData(attachments); + } + if (json.contains(JSON_AVATAR_JOINT_ARRAY)) { if (version == (int)JsonAvatarFrameVersion::JointRotationsInRelativeFrame) { // because we don't have the full joint hierarchy skeleton of the model, From a891b74fac568e79a20c7e82fad63131d1fa35be Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 20 Aug 2018 12:31:21 -0700 Subject: [PATCH 063/207] Reset SafeLanding in all cases Also remove unused data member, elevate load priority only for entities with collisions, handle case of no nearby entities. --- interface/src/Application.cpp | 3 +- interface/src/octree/SafeLanding.cpp | 45 +++++++++++++++++----------- interface/src/octree/SafeLanding.h | 3 +- 3 files changed, 29 insertions(+), 22 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index cf5da63dc8..ee50447a5b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5272,6 +5272,7 @@ void Application::resetPhysicsReadyInformation() { _nearbyEntitiesCountAtLastPhysicsCheck = 0; _nearbyEntitiesStabilityCount = 0; _physicsEnabled = false; + _octreeProcessor.startEntitySequence(); } @@ -6324,8 +6325,6 @@ void Application::clearDomainOctreeDetails() { // reset the model renderer getEntities()->clear(); - _octreeProcessor.startEntitySequence(); - auto skyStage = DependencyManager::get()->getSkyStage(); skyStage->setBackgroundMode(graphics::SunSkyStage::SKY_DEFAULT); diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 16e357c9dd..3568dade08 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -31,22 +31,22 @@ bool SafeLanding::SequenceLessThan::operator()(const int& a, const int& b) const } void SafeLanding::startEntitySequence(QSharedPointer entityTreeRenderer) { - if (!_trackingEntities) { - auto entityTree = entityTreeRenderer->getTree(); + auto entityTree = entityTreeRenderer->getTree(); - if (entityTree) { - Locker lock(_lock); - _entityTree = entityTree; - _trackingEntities = true; - connect(std::const_pointer_cast(_entityTree).get(), - &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity); - connect(std::const_pointer_cast(_entityTree).get(), - &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity); - _sequenceNumbers.clear(); - _initialStart = INVALID_SEQUENCE; - _initialEnd = INVALID_SEQUENCE; - EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority); - } + if (entityTree) { + Locker lock(_lock); + _entityTree = entityTree; + _trackedEntities.clear(); + _trackingEntities = true; + connect(std::const_pointer_cast(_entityTree).get(), + &EntityTree::addingEntity, this, &SafeLanding::addTrackedEntity); + connect(std::const_pointer_cast(_entityTree).get(), + &EntityTree::deletingEntity, this, &SafeLanding::deleteTrackedEntity); + + _sequenceNumbers.clear(); + _initialStart = INVALID_SEQUENCE; + _initialEnd = INVALID_SEQUENCE; + EntityTreeRenderer::setEntityLoadingPriorityFunction(&ElevatedPriority); } } @@ -70,7 +70,9 @@ void SafeLanding::addTrackedEntity(const EntityItemID& entityID) { ModelEntityItem * modelEntity = std::dynamic_pointer_cast(entity).get(); static const std::set downloadedCollisionTypes { SHAPE_TYPE_COMPOUND, SHAPE_TYPE_SIMPLE_COMPOUND, SHAPE_TYPE_STATIC_MESH, SHAPE_TYPE_SIMPLE_HULL }; - if (downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { + bool hasAABox; + entity->getAABox(hasAABox); + if (hasAABox && downloadedCollisionTypes.count(modelEntity->getShapeType()) != 0) { // Only track entities with downloaded collision bodies. _trackedEntities.emplace(entityID, entity); qCDebug(interfaceapp) << "Safe Landing: Tracking entity " << entity->getItemName(); @@ -117,11 +119,14 @@ bool SafeLanding::isLoadSequenceComplete() { bool SafeLanding::sequenceNumbersComplete() { if (_initialStart != INVALID_SEQUENCE) { Locker lock(_lock); - int sequenceSize = _initialStart < _initialEnd ? _initialEnd - _initialStart: + int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart: _initialEnd + SEQUENCE_MODULO - _initialStart; auto startIter = _sequenceNumbers.find(_initialStart); auto endIter = _sequenceNumbers.find(_initialEnd); - if (startIter != _sequenceNumbers.end() && endIter != _sequenceNumbers.end() && distance(startIter, endIter) == sequenceSize) { + if (sequenceSize == 0 || + (startIter != _sequenceNumbers.end() + && endIter != _sequenceNumbers.end() + && distance(startIter, endIter) == sequenceSize) ) { _trackingEntities = false; // Don't track anything else that comes in. return true; } @@ -143,6 +148,10 @@ bool SafeLanding::entityPhysicsComplete() { return _trackedEntities.empty(); } +float SafeLanding::ElevatedPriority(const EntityItem& entityItem) { + return entityItem.getCollisionless() ? 0.0f : 10.0f; +} + void SafeLanding::debugDumpSequenceIDs() const { int p = -1; qCDebug(interfaceapp) << "Sequence set size:" << _sequenceNumbers.size(); diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index 9a846294bf..64de50b883 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -55,9 +55,8 @@ private: }; std::set _sequenceNumbers; - std::set _trackedURLs; - static float ElevatedPriority(const EntityItem&) { return 10.0f; } + static float ElevatedPriority(const EntityItem& entityItem); static float StandardPriority(const EntityItem&) { return 0.0f; } static const int SEQUENCE_MODULO; From 9c36519e8762351e85adbee1aa6b75ca379ae6eb Mon Sep 17 00:00:00 2001 From: sabrina-shanman Date: Mon, 20 Aug 2018 12:42:23 -0700 Subject: [PATCH 064/207] Fix cross-platform build error in ColliisonPick.cpp (binding to temporary) --- interface/src/raypick/CollisionPick.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/src/raypick/CollisionPick.cpp b/interface/src/raypick/CollisionPick.cpp index 889a241bcc..9f2e6da2e8 100644 --- a/interface/src/raypick/CollisionPick.cpp +++ b/interface/src/raypick/CollisionPick.cpp @@ -361,7 +361,7 @@ PickResultPointer CollisionPick::getEntityIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } - auto& entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); + auto entityIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_ENTITIES, *pick.shapeInfo, pick.transform); filterIntersections(entityIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, entityIntersections, std::vector()); } @@ -376,7 +376,7 @@ PickResultPointer CollisionPick::getAvatarIntersection(const CollisionRegion& pi return std::make_shared(pick.toVariantMap(), CollisionPickResult::LOAD_STATE_NOT_LOADED, std::vector(), std::vector()); } - auto &avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); + auto avatarIntersections = _physicsEngine->contactTest(USER_COLLISION_MASK_AVATARS, *pick.shapeInfo, pick.transform); filterIntersections(avatarIntersections); return std::make_shared(pick, CollisionPickResult::LOAD_STATE_LOADED, std::vector(), avatarIntersections); } From dc0fbcca59ecc3d671ed4e08177c25a3994d9975 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 20 Aug 2018 13:10:36 -0700 Subject: [PATCH 065/207] Include avatar entities on recordings --- libraries/avatars/src/AvatarData.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bfe483e8d7..58b86db881 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2431,6 +2431,18 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { setAttachmentData(attachments); } + if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { + QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); + for (auto attachmentJson : attachmentsJson) { + if (attachmentJson.isObject()) { + QVariantMap entityData = attachmentJson.toObject().toVariantMap(); + QUuid entityID = entityData.value("id").toUuid(); + QByteArray properties = entityData.value("properties").toByteArray(); + updateAvatarEntity(entityID, properties); + } + } + } + if (json.contains(JSON_AVATAR_JOINT_ARRAY)) { if (version == (int)JsonAvatarFrameVersion::JointRotationsInRelativeFrame) { // because we don't have the full joint hierarchy skeleton of the model, From 8bd9bf39b6abb052e6bcdcb92456ba407298757a Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 20 Aug 2018 14:31:03 -0700 Subject: [PATCH 066/207] Fix json load --- libraries/avatars/src/AvatarData.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 58b86db881..8c72be633f 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2432,7 +2432,7 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { } if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ATTACHMENTS].toArray(); + QJsonArray attachmentsJson = json[JSON_AVATAR_ENTITIES].toArray(); for (auto attachmentJson : attachmentsJson) { if (attachmentJson.isObject()) { QVariantMap entityData = attachmentJson.toObject().toVariantMap(); From 67eb03ba7d049e0e08fdca580770e8f75ceec1e2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 11:18:00 +1200 Subject: [PATCH 067/207] Reduce strength of teleport parabola --- scripts/system/controllers/controllerModules/teleport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 92e3168147..2253432f48 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -110,7 +110,7 @@ Script.include("/~/system/libraries/controllers.js"); SEAT: 'seat' // The current target is a seat }; - var speed = 12.0; + var speed = 9.3; var accelerationAxis = {x: 0.0, y: -5.0, z: 0.0}; function Teleporter(hand) { From 94026fb4dda683a0af532fdc61e693201f2997f1 Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 20 Aug 2018 16:47:14 -0700 Subject: [PATCH 068/207] added activate(rotation) to prephysics update --- interface/src/avatar/MyAvatar.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 10e38afbe3..2a6cb0017a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3582,6 +3582,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { + activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } @@ -3589,6 +3590,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { + activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } From 8d6aaf3fb5592e4ac243eae6e322e5a78c604f39 Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Mon, 20 Aug 2018 17:50:29 -0700 Subject: [PATCH 069/207] Tidy some method names per reviewer comment --- interface/src/octree/OctreePacketProcessor.cpp | 2 +- interface/src/octree/SafeLanding.cpp | 8 ++++---- interface/src/octree/SafeLanding.h | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/interface/src/octree/OctreePacketProcessor.cpp b/interface/src/octree/OctreePacketProcessor.cpp index f740995123..4bc6817a9e 100644 --- a/interface/src/octree/OctreePacketProcessor.cpp +++ b/interface/src/octree/OctreePacketProcessor.cpp @@ -112,7 +112,7 @@ void OctreePacketProcessor::processPacket(QSharedPointer messag auto renderer = qApp->getEntities(); if (renderer) { renderer->processDatagram(*message, sendingNode); - _safeLanding->sequenceNumberReceived(renderer->getLastOctreeMessageSequence()); + _safeLanding->noteReceivedsequenceNumber(renderer->getLastOctreeMessageSequence()); } } } break; diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 3568dade08..31106457fb 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -95,7 +95,7 @@ void SafeLanding::setCompletionSequenceNumbers(int first, int last) { } } -void SafeLanding::sequenceNumberReceived(int sequenceNumber) { +void SafeLanding::noteReceivedsequenceNumber(int sequenceNumber) { if (_trackingEntities) { Locker lock(_lock); _sequenceNumbers.insert(sequenceNumber); @@ -103,7 +103,7 @@ void SafeLanding::sequenceNumberReceived(int sequenceNumber) { } bool SafeLanding::isLoadSequenceComplete() { - if (entityPhysicsComplete() && sequenceNumbersComplete()) { + if (isEntityPhysicsComplete() && isSequenceNumbersComplete()) { Locker lock(_lock); _trackedEntities.clear(); _initialStart = INVALID_SEQUENCE; @@ -116,7 +116,7 @@ bool SafeLanding::isLoadSequenceComplete() { return !_trackingEntities; } -bool SafeLanding::sequenceNumbersComplete() { +bool SafeLanding::isSequenceNumbersComplete() { if (_initialStart != INVALID_SEQUENCE) { Locker lock(_lock); int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart: @@ -134,7 +134,7 @@ bool SafeLanding::sequenceNumbersComplete() { return false; } -bool SafeLanding::entityPhysicsComplete() { +bool SafeLanding::isEntityPhysicsComplete() { Locker lock(_lock); for (auto entityMapIter = _trackedEntities.begin(); entityMapIter != _trackedEntities.end(); ++entityMapIter) { auto entity = entityMapIter->second; diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index 64de50b883..210dfbac25 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -27,7 +27,7 @@ public: void startEntitySequence(QSharedPointer entityTreeRenderer); void stopEntitySequence(); void setCompletionSequenceNumbers(int first, int last); - void sequenceNumberReceived(int sequenceNumber); + void noteReceivedsequenceNumber(int sequenceNumber); bool isLoadSequenceComplete(); private slots: @@ -35,9 +35,9 @@ private slots: void deleteTrackedEntity(const EntityItemID& entityID); private: - bool sequenceNumbersComplete(); + bool isSequenceNumbersComplete(); void debugDumpSequenceIDs() const; - bool entityPhysicsComplete(); + bool isEntityPhysicsComplete(); std::mutex _lock; using Locker = std::lock_guard; From 1914f1f1031ebe47670bf59a00f3127680455aa6 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 21 Aug 2018 10:10:39 -0700 Subject: [PATCH 070/207] Extending the default size of the regions to 16km --- libraries/workload/src/workload/ViewTask.h | 2 +- scripts/developer/utilities/workload/workloadInspector.qml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/workload/src/workload/ViewTask.h b/libraries/workload/src/workload/ViewTask.h index c20314d514..4fdd7e0f4c 100644 --- a/libraries/workload/src/workload/ViewTask.h +++ b/libraries/workload/src/workload/ViewTask.h @@ -192,7 +192,7 @@ namespace workload { struct Data { - bool regulateViewRanges{ false }; + bool regulateViewRanges{ false }; // regulation is OFF by default } data; struct DataExport { diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index c55fdee040..2eaa9d8133 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -160,9 +160,9 @@ Rectangle { } Repeater { model: [ - "r1Front:500:1.0", - "r2Front:500:1.0", - "r3Front:500:1.0" + "r1Front:16000:1.0", + "r2Front:16000:1.0", + "r3Front:16000:1.0" ] ConfigSlider { showLabel: false From a378435683b2bf9c5130ade161a29caa8f4d21ab Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Tue, 21 Aug 2018 10:20:51 -0700 Subject: [PATCH 071/207] Fix MS16003: In PAL/GoTo WebView, 'BACK' closes app when you can't go back --- interface/resources/qml/controls/TabletWebView.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/controls/TabletWebView.qml b/interface/resources/qml/controls/TabletWebView.qml index bc958aa876..db695dbfb2 100644 --- a/interface/resources/qml/controls/TabletWebView.qml +++ b/interface/resources/qml/controls/TabletWebView.qml @@ -52,12 +52,18 @@ Item { id: back enabledColor: hifi.colors.darkGray disabledColor: hifi.colors.lightGrayText - enabled: historyIndex > 0 + enabled: true text: "BACK" MouseArea { anchors.fill: parent - onClicked: goBack() + onClicked: { + if (historyIndex > 0) { + goBack(); + } else { + closeWebEngine(); + } + } } } From 486557e28a7d35e635224e7849700b287e1c705c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 30 Jul 2018 16:29:28 -0700 Subject: [PATCH 072/207] initial trait packet sending on skeleton URL change --- assignment-client/src/avatars/AvatarMixer.cpp | 5 ++ assignment-client/src/avatars/AvatarMixer.h | 1 + interface/src/avatar/MyAvatar.cpp | 7 +- interface/src/avatar/MyAvatar.h | 20 +++-- libraries/avatars/src/AvatarTraits.h | 29 +++++++ libraries/avatars/src/ClientTraitsHandler.cpp | 84 +++++++++++++++++++ libraries/avatars/src/ClientTraitsHandler.h | 42 ++++++++++ libraries/networking/src/udt/PacketHeaders.h | 2 +- 8 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 libraries/avatars/src/AvatarTraits.h create mode 100644 libraries/avatars/src/ClientTraitsHandler.cpp create mode 100644 libraries/avatars/src/ClientTraitsHandler.h diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 8f1df9c321..a5eba6ad11 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -54,6 +54,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket"); + packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "handleSetAvatarTraitsMessage"); packetReceiver.registerListenerForTypes({ PacketType::ReplicatedAvatarIdentity, @@ -606,6 +607,10 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes _handleAvatarIdentityPacketElapsedTime += (end - start); } +void AvatarMixer::handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode) { + qDebug() << "Got a traits packet of" << message->getSize() << "bytes from" << senderNode; +} + void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointer message, SharedNodePointer senderNode) { if (message->getSize() < NUM_BYTES_RFC4122_UUID) { qCDebug(avatars) << "Malformed AvatarIdentityRequest received from" << message->getSenderSockAddr().toString(); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index c27ca33729..b0405a0b6d 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -48,6 +48,7 @@ private slots: void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message, SharedNodePointer senderNode); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 41855e7973..3ba2cbfc1e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -119,7 +119,8 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()) + _hmdAtRestDetector(glm::vec3(0), glm::quat()), + _clientTraitsHandler(this) { // give the pointer to our head to inherited _headData variable from AvatarData @@ -513,6 +514,8 @@ void MyAvatar::update(float deltaTime) { sendIdentityPacket(); } + _clientTraitsHandler.sendChangedTraitsToMixer(); + simulate(deltaTime); currentEnergy += energyChargeRate; @@ -1696,6 +1699,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { saveAvatarUrl(); emit skeletonChanged(); emit skeletonModelURLChanged(); + + _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); } void MyAvatar::removeAvatarEntities(const std::function& condition) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f2c52624d9..8fdc4ba4e3 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -18,22 +18,22 @@ #include -#include -#include -#include -#include - -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "AtRestDetector.h" #include "MyCharacterController.h" #include "RingBufferHistory.h" -#include -#include class AvatarActionHold; class ModelItemID; @@ -1776,6 +1776,8 @@ private: bool _haveReceivedHeightLimitsFromDomain { false }; int _disableHandTouchCount { 0 }; + + ClientTraitsHandler _clientTraitsHandler; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h new file mode 100644 index 0000000000..ebeaeaea20 --- /dev/null +++ b/libraries/avatars/src/AvatarTraits.h @@ -0,0 +1,29 @@ +// +// AvatarTraits.h +// libraries/avatars/src +// +// Created by Stephen Birarda on 7/30/18. +// Copyright 2018 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_AvatarTraits_h +#define hifi_AvatarTraits_h + +#include + +namespace AvatarTraits { + enum Trait : uint8_t { + SkeletonModelURL, + TotalTraits + }; + + using TraitVersion = uint32_t; + const TraitVersion DEFAULT_TRAIT_VERSION = 0; + + using TraitVersions = std::vector; +} + +#endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp new file mode 100644 index 0000000000..c4685461a0 --- /dev/null +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -0,0 +1,84 @@ +// +// ClientTraitsHandler.cpp +// libraries/avatars/src +// +// Created by Stephen Birarda on 7/30/18. +// Copyright 2018 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 "ClientTraitsHandler.h" + +#include + +#include +#include + +#include "AvatarData.h" + +ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) : + _owningAvatar(owningAvatar) +{ + auto nodeList = DependencyManager::get(); + QObject::connect(nodeList.data(), &NodeList::nodeAdded, [this](SharedNodePointer addedNode){ + if (addedNode->getType() == NodeType::AvatarMixer) { + resetForNewMixer(); + } + }); +} + +void ClientTraitsHandler::resetForNewMixer() { + // re-set the current version to 0 + _currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION; + + // mark that all traits should be sent next time + _performInitialSend = true; +} + +void ClientTraitsHandler::sendChangedTraitsToMixer() { + if (hasChangedTraits() || _performInitialSend) { + // we have at least one changed trait to send + + auto nodeList = DependencyManager::get(); + auto avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); + if (!avatarMixer || !avatarMixer->getActiveSocket()) { + // we don't have an avatar mixer with an active socket, we can't send changed traits at this time + return; + } + + // we have a mixer to send to, setup our set traits packet + + // bump and write the current trait version to an extended header + // the trait version is the same for all traits in this packet list + ++_currentTraitVersion; + QByteArray extendedHeader(reinterpret_cast(&_currentTraitVersion), sizeof(_currentTraitVersion)); + + auto traitsPacketList = NLPacketList::create(PacketType::SetAvatarTraits, extendedHeader, true); + + // take a copy of the set of changed traits and clear the stored set + auto changedTraitsCopy { _changedTraits }; + _changedTraits.clear(); + + if (_performInitialSend || changedTraitsCopy.count(AvatarTraits::SkeletonModelURL)) { + traitsPacketList->startSegment(); + + traitsPacketList->writePrimitive(AvatarTraits::SkeletonModelURL); + + auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded(); + + uint16_t encodedURLSize = encodedSkeletonURL.size(); + traitsPacketList->writePrimitive(encodedURLSize); + + traitsPacketList->write(encodedSkeletonURL); + + traitsPacketList->endSegment(); + } + + nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); + + // if this was an initial send of all traits, consider it completed + _performInitialSend = false; + } +} diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h new file mode 100644 index 0000000000..9fd3104e7e --- /dev/null +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -0,0 +1,42 @@ +// +// ClientTraitsHandler.h +// libraries/avatars/src +// +// Created by Stephen Birarda on 7/30/18. +// Copyright 2018 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_ClientTraitsHandler_h +#define hifi_ClientTraitsHandler_h + +#include + +#include "AvatarTraits.h" + +class AvatarData; + +class ClientTraitsHandler { +public: + ClientTraitsHandler(AvatarData* owningAvatar); + + void sendChangedTraitsToMixer(); + + bool hasChangedTraits() { return _changedTraits.size(); } + void markTraitChanged(AvatarTraits::Trait changedTrait) { _changedTraits.insert(changedTrait); } + + bool hasTraitChanged(AvatarTraits::Trait checkTrait) { return _changedTraits.count(checkTrait) > 0; } + + void resetForNewMixer(); + +private: + AvatarData* _owningAvatar; + + std::set _changedTraits; + AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; + bool _performInitialSend { false }; +}; + +#endif // hifi_ClientTraitsHandler_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 126dac7c8f..3990afa79a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -56,7 +56,7 @@ public: ICEServerPeerInformation, ICEServerQuery, OctreeStats, - UNUSED_PACKET_TYPE_1, + SetAvatarTraits, AvatarIdentityRequest, AssignmentClientStatus, NoisyMute, From ac835650b74421be618aaef7bd362f6dd17c0e95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 30 Jul 2018 17:33:01 -0700 Subject: [PATCH 073/207] add skeleton trait sending to Agent, queuing in AvatarMixer --- assignment-client/src/Agent.h | 1 + assignment-client/src/avatars/AvatarMixer.cpp | 6 +----- assignment-client/src/avatars/AvatarMixer.h | 3 --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 8 ++++++++ assignment-client/src/avatars/AvatarMixerClientData.h | 2 ++ assignment-client/src/avatars/ScriptableAvatar.cpp | 5 +++++ assignment-client/src/avatars/ScriptableAvatar.h | 5 ++++- interface/src/avatar/MyAvatar.cpp | 9 ++++++++- libraries/avatars/src/AvatarTraits.h | 2 -- 9 files changed, 29 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 7d47c8e713..2b5ff51b49 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a5eba6ad11..56fb15c9f0 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -54,7 +54,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket"); - packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "handleSetAvatarTraitsMessage"); + packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket"); packetReceiver.registerListenerForTypes({ PacketType::ReplicatedAvatarIdentity, @@ -607,10 +607,6 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes _handleAvatarIdentityPacketElapsedTime += (end - start); } -void AvatarMixer::handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode) { - qDebug() << "Got a traits packet of" << message->getSize() << "bytes from" << senderNode; -} - void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointer message, SharedNodePointer senderNode) { if (message->getSize() < NUM_BYTES_RFC4122_UUID) { qCDebug(avatars) << "Malformed AvatarIdentityRequest received from" << message->getSenderSockAddr().toString(); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index b0405a0b6d..14f84c9e7b 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -48,7 +48,6 @@ private slots: void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); - void handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message, SharedNodePointer senderNode); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); @@ -127,9 +126,7 @@ private: RateCounter<> _loopRate; // this is the rate that the main thread tight loop runs - AvatarMixerSlavePool _slavePool; - }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 9aa3e88b52..f6718b839a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -62,6 +62,9 @@ int AvatarMixerClientData::processPackets() { case PacketType::AvatarData: parseData(*packet); break; + case PacketType::SetAvatarTraits: + processSetTraitsMessage(*packet); + break; default: Q_UNREACHABLE(); } @@ -87,6 +90,11 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { // compute the offset to the data payload return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead())); } + +void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { + qDebug() << "Pulling a traits message of" << message.getSize(); +} + uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastTimes.find(nodeUUID); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index e038e81505..2e30b2c8a1 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -120,6 +120,8 @@ public: void queuePacket(QSharedPointer message, SharedNodePointer node); int processPackets(); // returns number of packets processed + void processSetTraitsMessage(ReceivedMessage& message); + private: struct PacketQueue : public std::queue> { QWeakPointer node; diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index e7210db83a..4e47cf96f1 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -61,7 +61,10 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() { void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _bind.reset(); _animSkeleton.reset(); + AvatarData::setSkeletonModelURL(skeletonModelURL); + + _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); } static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { @@ -137,4 +140,6 @@ void ScriptableAvatar::update(float deltatime) { _animation.clear(); } } + + _clientTraitsHandler.sendChangedTraitsToMixer(); } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index d34ad2d21e..8e3d779dda 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -15,6 +15,7 @@ #include #include #include +#include #include /**jsdoc @@ -123,7 +124,7 @@ class ScriptableAvatar : public AvatarData, public Dependency { Q_OBJECT public: - + /**jsdoc * @function Avatar.startAnimation * @param {string} url @@ -164,6 +165,8 @@ private: QStringList _maskedJoints; AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies std::shared_ptr _animSkeleton; + + ClientTraitsHandler _clientTraitsHandler { this }; }; #endif // hifi_ScriptableAvatar_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3ba2cbfc1e..53ad8c0a0f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1669,7 +1669,10 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModelChangeCount++; int skeletonModelChangeCount = _skeletonModelChangeCount; + + auto previousSkeletonModelURL = _skeletonModelURL; Avatar::setSkeletonModelURL(skeletonModelURL); + _skeletonModel->setTagMask(render::hifi::TAG_NONE); _skeletonModel->setGroupCulled(true); _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); @@ -1700,7 +1703,11 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); emit skeletonModelURLChanged(); - _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); + if (previousSkeletonModelURL != _skeletonModelURL) { + _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); + } else { + qDebug() << "Not marking skeleton model URL trait changed since the new value matches the previous"; + } } void MyAvatar::removeAvatarEntities(const std::function& condition) { diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index ebeaeaea20..c35bfae95c 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -22,8 +22,6 @@ namespace AvatarTraits { using TraitVersion = uint32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; - - using TraitVersions = std::vector; } #endif // hifi_AvatarTraits_h From f23a036f4aab08ab49a2cd49803e9dc0353076b6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 11:29:03 -0700 Subject: [PATCH 074/207] add node local ID retreivable from NodeData --- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.cpp | 4 ++-- assignment-client/src/audio/AudioMixerClientData.h | 2 +- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- assignment-client/src/avatars/AvatarMixerClientData.cpp | 2 +- assignment-client/src/avatars/AvatarMixerClientData.h | 2 +- libraries/networking/src/NodeData.cpp | 9 ++++----- libraries/networking/src/NodeData.h | 5 ++++- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d56b22466e..0d42cc83be 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -366,7 +366,7 @@ AudioMixerClientData* AudioMixer::getOrCreateClientData(Node* node) { auto clientData = dynamic_cast(node->getLinkedData()); if (!clientData) { - node->setLinkedData(std::unique_ptr { new AudioMixerClientData(node->getUUID()) }); + node->setLinkedData(std::unique_ptr { new AudioMixerClientData(node->getUUID(), node->getLocalID()) }); clientData = dynamic_cast(node->getLinkedData()); connect(clientData, &AudioMixerClientData::injectorStreamFinished, this, &AudioMixer::removeHRTFsForFinishedInjector); } diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index bc08c6f24a..07cc5493b0 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -25,8 +25,8 @@ #include "AudioHelpers.h" #include "AudioMixer.h" -AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) : - NodeData(nodeID), +AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : + NodeData(nodeID, nodeLocalID), audioLimiter(AudioConstants::SAMPLE_RATE, AudioConstants::STEREO), _ignoreZone(*this), _outgoingMixedAudioSequenceNumber(0), diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 514e1c9756..82bdc0e5c5 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -30,7 +30,7 @@ class AudioMixerClientData : public NodeData { Q_OBJECT public: - AudioMixerClientData(const QUuid& nodeID); + AudioMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID); ~AudioMixerClientData(); using SharedStreamPointer = std::shared_ptr; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 56fb15c9f0..b139870e6e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -904,7 +904,7 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node auto clientData = dynamic_cast(node->getLinkedData()); if (!clientData) { - node->setLinkedData(std::unique_ptr { new AvatarMixerClientData(node->getUUID()) }); + node->setLinkedData(std::unique_ptr { new AvatarMixerClientData(node->getUUID(), node->getLocalID()) }); clientData = dynamic_cast(node->getLinkedData()); auto& avatar = clientData->getAvatar(); avatar.setDomainMinimumHeight(_domainMinimumHeight); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index f6718b839a..19016ae80e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -16,7 +16,7 @@ #include #include -AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) : +AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 2e30b2c8a1..ceb66a9d22 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -36,7 +36,7 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; class AvatarMixerClientData : public NodeData { Q_OBJECT public: - AvatarMixerClientData(const QUuid& nodeID = QUuid()); + AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID); virtual ~AvatarMixerClientData() {} using HRCTime = p_high_resolution_clock::time_point; diff --git a/libraries/networking/src/NodeData.cpp b/libraries/networking/src/NodeData.cpp index 300c76ca28..dd926852cd 100644 --- a/libraries/networking/src/NodeData.cpp +++ b/libraries/networking/src/NodeData.cpp @@ -11,13 +11,12 @@ #include "NodeData.h" -NodeData::NodeData(const QUuid& nodeID) : +NodeData::NodeData(const QUuid& nodeID, NetworkPeer::LocalID nodeLocalID) : _mutex(), - _nodeID(nodeID) + _nodeID(nodeID), + _nodeLocalID(nodeLocalID) { } -NodeData::~NodeData() { - -} +NodeData::~NodeData() {} diff --git a/libraries/networking/src/NodeData.h b/libraries/networking/src/NodeData.h index 9aeac03837..ffbc0c2376 100644 --- a/libraries/networking/src/NodeData.h +++ b/libraries/networking/src/NodeData.h @@ -16,6 +16,7 @@ #include #include +#include "NetworkPeer.h" #include "NLPacket.h" #include "ReceivedMessage.h" @@ -24,17 +25,19 @@ class Node; class NodeData : public QObject { Q_OBJECT public: - NodeData(const QUuid& nodeID = QUuid()); + NodeData(const QUuid& nodeID = QUuid(), NetworkPeer::LocalID localID = NetworkPeer::NULL_LOCAL_ID); virtual ~NodeData() = 0; virtual int parseData(ReceivedMessage& message) { return 0; } const QUuid& getNodeID() const { return _nodeID; } + NetworkPeer::LocalID getNodeLocalID() const { return _nodeLocalID; } QMutex& getMutex() { return _mutex; } private: QMutex _mutex; QUuid _nodeID; + NetworkPeer::LocalID _nodeLocalID; }; #endif // hifi_NodeData_h From 26a1f0331478d15fbe0824542b14b0cf61db4a4b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 14:02:07 -0700 Subject: [PATCH 075/207] send traits in bulk to avatar mixer client --- .../src/avatars/AvatarMixerClientData.cpp | 75 ++++++++++++++++++- .../src/avatars/AvatarMixerClientData.h | 21 ++++++ .../src/avatars/AvatarMixerSlave.cpp | 72 +++++++++++++++++- .../src/avatars/AvatarMixerSlave.h | 4 + libraries/avatars/src/AvatarTraits.h | 14 +++- libraries/avatars/src/ClientTraitsHandler.cpp | 4 +- libraries/avatars/src/ClientTraitsHandler.h | 6 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 8 files changed, 188 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 19016ae80e..cf30aca1a7 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -17,7 +17,8 @@ #include AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID) + NodeData(nodeID), + _receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size()) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); @@ -92,7 +93,41 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { } void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { - qDebug() << "Pulling a traits message of" << message.getSize(); + // pull the trait version from the message + AvatarTraits::TraitVersion packetTraitVersion; + message.readPrimitive(&packetTraitVersion); + + bool anyTraitsChanged = false; + + while (message.getBytesLeftToRead() > 0) { + // for each trait in the packet, apply it if the trait version is newer than what we have + + AvatarTraits::TraitType traitType; + message.readPrimitive(&traitType); + + AvatarTraits::TraitWireSize traitSize; + message.readPrimitive(&traitSize); + + if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { + if (traitType == AvatarTraits::SkeletonModelURL) { + // get the URL from the binary data + auto skeletonModelURL = QUrl::fromEncoded(message.read(traitSize)); + _avatar->setSkeletonModelURL(skeletonModelURL); + + qDebug() << "Set skeleton URL to" << skeletonModelURL << "for trait packet version" << packetTraitVersion; + + _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + + anyTraitsChanged = true; + } + } else { + message.seek(message.getPosition() + traitSize); + } + } + + if (anyTraitsChanged) { + _lastReceivedTraitsChange = std::chrono::steady_clock::now(); + } } uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { @@ -172,3 +207,39 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView; jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView; } + +AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const { + auto it = _lastSentTraitsTimestamps.find(otherAvatar); + + if (it != _lastSentTraitsTimestamps.end()) { + return it->second; + } else { + return TraitsCheckTimestamp(); + } +} + +AvatarTraits::TraitVersion AvatarMixerClientData::getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, + AvatarTraits::TraitType traitType) const { + auto it = _sentSimpleTraitVersions.find(otherAvatar); + + if (it != _sentSimpleTraitVersions.end()) { + return it->second[traitType]; + } + + return AvatarTraits::DEFAULT_TRAIT_VERSION; +} + +void AvatarMixerClientData::setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion traitVersion) { + + auto it = _sentSimpleTraitVersions.find(otherAvatar); + + if (it == _sentSimpleTraitVersions.end()) { + auto pair = _sentSimpleTraitVersions.insert({ + otherAvatar, { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } + }); + + it = pair.first; + } + + it->second[traitType] = traitVersion; +} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index ceb66a9d22..8792ecfa5d 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -122,6 +123,20 @@ public: void processSetTraitsMessage(ReceivedMessage& message); + using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; + + TraitsCheckTimestamp getLastReceivedTraitsChange() const { return _lastReceivedTraitsChange; } + AvatarTraits::TraitVersion getLastReceivedSimpleTraitVersion(AvatarTraits::TraitType traitType) const + { return _receivedSimpleTraitVersions[traitType]; } + + TraitsCheckTimestamp getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const; + void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint) + { _lastSentTraitsTimestamps[otherAvatar] = sendPoint; } + + AvatarTraits::TraitVersion getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType) const; + void setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, + AvatarTraits::TraitVersion traitVersion); + private: struct PacketQueue : public std::queue> { QWeakPointer node; @@ -158,6 +173,12 @@ private: int _recentOtherAvatarsOutOfView { 0 }; QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. bool _requestsDomainListData { false }; + + AvatarTraits::SimpleTraitVersions _receivedSimpleTraitVersions; + TraitsCheckTimestamp _lastReceivedTraitsChange; + + std::unordered_map _lastSentTraitsTimestamps; + std::unordered_map _sentSimpleTraitVersions; }; #endif // hifi_AvatarMixerClientData_h diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 563aac879f..c996008a48 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -79,6 +79,61 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, } } +void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList) { + + auto otherNodeLocalID = sendingNodeData->getNodeLocalID(); + + // Perform a simple check with two server clock time points + // to see if there is any new traits data for this avatar that we need to send + auto timeOfLastTraitsSent = listeningNodeData->getLastOtherAvatarTraitsSendPoint(otherNodeLocalID); + auto timeOfLastTraitsChange = sendingNodeData->getLastReceivedTraitsChange(); + + if (timeOfLastTraitsChange > timeOfLastTraitsSent) { + // there is definitely new traits data to send + + // add the avatar ID to mark the beginning of traits for this avatar + traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122()); + + auto sendingAvatar = sendingNodeData->getAvatarSharedPointer(); + + // compare trait versions so we can see what exactly needs to go out + for (int i = 0; i < AvatarTraits::TotalTraitTypes; ++i) { + AvatarTraits::TraitType traitType = static_cast(i); + + auto lastSentVersion = listeningNodeData->getLastSentSimpleTraitVersion(otherNodeLocalID, traitType); + auto lastReceivedVersion = sendingNodeData->getLastReceivedSimpleTraitVersion(traitType); + + if (lastReceivedVersion > lastSentVersion) { + // there is an update to this trait, add it to the traits packet + + // write the trait type and the trait version + traitsPacketList.writePrimitive(traitType); + traitsPacketList.writePrimitive(lastReceivedVersion); + + // update the last sent version since we're adding this to the packet + listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion); + + if (traitType == AvatarTraits::SkeletonModelURL) { + // get an encoded version of the URL, write its size and then the data itself + auto encodedSkeletonURL = sendingAvatar->getSkeletonModelURL().toEncoded(); + + traitsPacketList.writePrimitive(uint16_t(encodedSkeletonURL.size())); + traitsPacketList.write(encodedSkeletonURL); + } + } + } + + // write a null trait type to mark the end of trait data for this avatar + traitsPacketList.writePrimitive(AvatarTraits::NullTrait); + + // since we send all traits for this other avatar, update the time of last traits sent + // to match the time of last traits change + listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange); + } +} + int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) { if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) { QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true); @@ -326,6 +381,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // loop through our sorted avatars and allocate our bandwidth to them accordingly int remainingAvatars = (int)sortedAvatars.size(); + auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true); while (!sortedAvatars.empty()) { const auto avatarData = sortedAvatars.top().getAvatar(); sortedAvatars.pop(); @@ -392,11 +448,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 start = usecTimestampNow(); QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); + hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, + &lastSentJointsForOther); quint64 end = usecTimestampNow(); _stats.toByteArrayElapsedTime += (end - start); - auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; + static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; if (bytes.size() > maxAvatarDataBytes) { qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() << "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data"; @@ -445,6 +502,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); + + // use helper to add any changed traits to our packet list + addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); } quint64 startPacketSending = usecTimestampNow(); @@ -461,6 +521,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes); + // close the current traits packet list + traitsPacketList->closeCurrentPacket(); + + if (traitsPacketList->getNumPackets() >= 1) { + // send the traits packet list + nodeList->sendPacketList(std::move(traitsPacketList), *node); + } + // record the number of avatars held back this frame nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 7be119c4b2..ed27709e3e 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -99,6 +99,10 @@ private: int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode); + void addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList); + void broadcastAvatarDataToAgent(const SharedNodePointer& node); void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index c35bfae95c..90258982c3 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -13,15 +13,25 @@ #define hifi_AvatarTraits_h #include +#include +#include namespace AvatarTraits { - enum Trait : uint8_t { + enum TraitType : int8_t { + NullTrait = -1, SkeletonModelURL, - TotalTraits + TotalTraitTypes }; + using TraitTypeSet = std::set; + const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL }; + using TraitVersion = uint32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; + + using TraitWireSize = uint16_t; + + using SimpleTraitVersions = std::vector; } #endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index c4685461a0..5d1f2e4926 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -68,9 +68,11 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded(); - uint16_t encodedURLSize = encodedSkeletonURL.size(); + AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); traitsPacketList->writePrimitive(encodedURLSize); + qDebug() << "Sending trait of size" << encodedURLSize; + traitsPacketList->write(encodedSkeletonURL); traitsPacketList->endSegment(); diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 9fd3104e7e..4aea0bb433 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -25,16 +25,16 @@ public: void sendChangedTraitsToMixer(); bool hasChangedTraits() { return _changedTraits.size(); } - void markTraitChanged(AvatarTraits::Trait changedTrait) { _changedTraits.insert(changedTrait); } + void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); } - bool hasTraitChanged(AvatarTraits::Trait checkTrait) { return _changedTraits.count(checkTrait) > 0; } + bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.count(checkTrait) > 0; } void resetForNewMixer(); private: AvatarData* _owningAvatar; - std::set _changedTraits; + AvatarTraits::TraitTypeSet _changedTraits; AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; bool _performInitialSend { false }; }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3990afa79a..616694c8da 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -133,7 +133,8 @@ public: EntityClone, EntityQueryInitialResultsComplete, - + BulkAvatarTraits, + NUM_PACKET_TYPE }; From a80d19a44a393c45e6855cd97c0a9d8bf49e72d2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 15:38:57 -0700 Subject: [PATCH 076/207] remove skeleton from identity, handle in clients --- assignment-client/src/Agent.cpp | 5 -- .../src/avatars/AvatarMixerClientData.cpp | 15 +--- .../src/scripts/EntityScriptServer.cpp | 3 - interface/src/avatar/AvatarManager.cpp | 4 - interface/src/avatar/MyAvatar.cpp | 2 - libraries/avatars/src/AvatarData.cpp | 27 +++---- libraries/avatars/src/AvatarData.h | 7 +- libraries/avatars/src/AvatarHashMap.cpp | 76 ++++++++++++++++++- libraries/avatars/src/AvatarHashMap.h | 7 ++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 11 files changed, 104 insertions(+), 47 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index f534be9346..4cc24e2110 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -447,11 +447,6 @@ void Agent::executeScript() { auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); - packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); - packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); - // register ourselves to the script engine _scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this)); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index cf30aca1a7..f279d76450 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -109,17 +109,10 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { message.readPrimitive(&traitSize); if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { - if (traitType == AvatarTraits::SkeletonModelURL) { - // get the URL from the binary data - auto skeletonModelURL = QUrl::fromEncoded(message.read(traitSize)); - _avatar->setSkeletonModelURL(skeletonModelURL); - - qDebug() << "Set skeleton URL to" << skeletonModelURL << "for trait packet version" << packetTraitVersion; - - _receivedSimpleTraitVersions[traitType] = packetTraitVersion; - - anyTraitsChanged = true; - } + _avatar->processTrait(traitType, message.readWithoutCopy(traitSize)); + _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + + anyTraitsChanged = true; } else { message.seek(message.getPosition() + traitSize); } diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 0e1126cebe..05002828f5 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -81,9 +81,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); auto avatarHashMap = DependencyManager::set(); - packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); - packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); - packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 09fa6dc573..8569aaf05a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -72,10 +72,6 @@ AvatarManager::AvatarManager(QObject* parent) : qRegisterMetaType >("NodeWeakPointer"); auto nodeList = DependencyManager::get(); - auto& packetReceiver = nodeList->getPacketReceiver(); - packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); - packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar"); - packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket"); // when we hear that the user has ignored an avatar by session UUID // immediately remove that avatar instead of waiting for the absence of packets from avatar mixer diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 53ad8c0a0f..df9afeb92d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1705,8 +1705,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (previousSkeletonModelURL != _skeletonModelURL) { _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); - } else { - qDebug() << "Not marking skeleton model URL trait changed since the new value matches the previous"; } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 62c7a7053f..ddfeb4df24 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -42,6 +42,7 @@ #include #include "AvatarLogging.h" +#include "AvatarTraits.h" //#define WANT_DEBUG @@ -1756,7 +1757,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { } void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, - bool& displayNameChanged, bool& skeletonModelUrlChanged) { + bool& displayNameChanged) { QDataStream packetStream(identityData); @@ -1777,7 +1778,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide if (incomingSequenceNumber > _identitySequenceNumber) { Identity identity; - packetStream >> identity.skeletonModelURL + packetStream >> identity.attachmentData >> identity.displayName >> identity.sessionDisplayName @@ -1789,16 +1790,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide // set the store identity sequence number to match the incoming identity _identitySequenceNumber = incomingSequenceNumber; - if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { - setSkeletonModelURL(identity.skeletonModelURL); - identityChanged = true; - skeletonModelUrlChanged = true; - if (_firstSkeletonCheck) { - displayNameChanged = true; - } - _firstSkeletonCheck = false; - } - if (identity.displayName != _displayName) { _displayName = identity.displayName; identityChanged = true; @@ -1834,7 +1825,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide #ifdef WANT_DEBUG qCDebug(avatars) << __FUNCTION__ << "identity.uuid:" << identity.uuid - << "identity.skeletonModelURL:" << identity.skeletonModelURL << "identity.displayName:" << identity.displayName << "identity.sessionDisplayName:" << identity.sessionDisplayName; } else { @@ -1846,10 +1836,18 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide } } +void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { + if (traitType == AvatarTraits::SkeletonModelURL) { + // get the URL from the binary data + auto skeletonModelURL = QUrl::fromEncoded(traitBinaryData); + qDebug() << "Setting skeleton model URL from trait packet to" << skeletonModelURL; + setSkeletonModelURL(skeletonModelURL); + } +} + QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); - const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL // when mixers send identity packets to agents, they simply forward along the last incoming sequence number they received // whereas agents send a fresh outgoing sequence number when identity data has changed @@ -1857,7 +1855,6 @@ QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { _avatarEntitiesLock.withReadLock([&] { identityStream << getSessionUUID() << (udt::SequenceNumber::Type) _identitySequenceNumber - << urlToSend << _attachmentData << _displayName << getSessionDisplayNameForTransport() // depends on _sessionDisplayName diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fcc63fdc98..12e8370b86 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -51,6 +51,7 @@ #include #include "AABox.h" +#include "AvatarTraits.h" #include "HeadData.h" #include "PathUtils.h" @@ -955,8 +956,9 @@ public: // identityChanged returns true if identity has changed, false otherwise. // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. - void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, - bool& displayNameChanged, bool& skeletonModelUrlChanged); + void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + + void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); QByteArray identityByteArray(bool setIsReplicated = false) const; @@ -1327,7 +1329,6 @@ protected: mutable HeadData* _headData { nullptr }; QUrl _skeletonModelURL; - bool _firstSkeletonCheck { true }; QUrl _skeletonFBXURL; QVector _attachmentData; QVector _oldAttachmentData; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 174e81bb31..407e88e27c 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -19,10 +19,17 @@ #include #include "AvatarLogging.h" +#include "AvatarTraits.h" AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); + packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar"); + packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket"); + packetReceiver.registerListener(PacketType::BulkAvatarTraits, this, "processBulkAvatarTraits"); + connect(nodeList.data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } @@ -182,9 +189,74 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identityUUID, sendingNode, isNewAvatar); bool identityChanged = false; bool displayNameChanged = false; - bool skeletonModelUrlChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. - avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged, skeletonModelUrlChanged); + avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + } +} + +bool AvatarHashMap::checkLastProcessedTraitVersion(QUuid avatarID, + AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion) { + auto it = _processedSimpleTraitVersions.find(avatarID); + if (it == _processedSimpleTraitVersions.end()) { + auto pair = _processedSimpleTraitVersions.insert({ + avatarID, + { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } + }); + + it = pair.first; + }; + + if (it->second[traitType] < newVersion) { + it->second[traitType] = newVersion; + return true; + } else { + return false; + } +} + +void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + // grab the avatar so we can ask it to process trait data + AvatarSharedPointer avatar; + + QReadLocker locker(&_hashLock); + auto it = _avatarHash.find(avatarID); + if (it != _avatarHash.end()) { + avatar = *it; + } + locker.unlock(); + + // read the first trait type for this avatar + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + while (traitType != AvatarTraits::NullTrait) { + AvatarTraits::TraitVersion traitVersion; + message->readPrimitive(&traitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + message->readPrimitive(&traitBinarySize); + + if (avatar) { + // check if this trait version is newer than what we already have for this avatar + bool traitIsNewer = checkLastProcessedTraitVersion(avatarID, traitType, traitVersion); + if (traitIsNewer) { + avatar->processTrait(traitType, message->readWithoutCopy(traitBinarySize)); + } else { + message->seek(message->getPosition() + traitBinarySize); + } + } else { + // though we have no avatar pointer, we still hop through the packet in case there are + // traits for avatars we do have later in the packet + message->seek(message->getPosition() + traitBinarySize); + } + + // read the next trait type, which is null if there are no more traits for this avatar + message->readPrimitive(&traitType); + } } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index fd2cd76fbf..ed8440eb89 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -30,6 +30,7 @@ #include "ScriptAvatarData.h" #include "AvatarData.h" +#include "AvatarTraits.h" /**jsdoc * Note: An AvatarList API is also provided for Interface and client entity scripts: it is a @@ -133,6 +134,8 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); + void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); + /**jsdoc * @function AvatarList.processKillAvatar * @param {} message @@ -153,6 +156,9 @@ protected: virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); + bool checkLastProcessedTraitVersion(QUuid avatarID, + AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion); + AvatarHash _avatarHash; struct PendingAvatar { std::chrono::steady_clock::time_point creationTime; @@ -163,6 +169,7 @@ protected: AvatarPendingHash _pendingAvatars; mutable QReadWriteLock _hashLock; + std::unordered_map _processedSimpleTraitVersions; private: QUuid _lastOwnerSessionUUID; }; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8b50e37699..94137786cd 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::FarGrabJoints); + return static_cast(AvatarMixerPacketVersion::MigrateSkeletonURLToTraits); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 616694c8da..31724ab5dc 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -134,7 +134,7 @@ public: EntityClone, EntityQueryInitialResultsComplete, BulkAvatarTraits, - + NUM_PACKET_TYPE }; @@ -291,6 +291,7 @@ enum class AvatarMixerPacketVersion : PacketVersion { FixMannequinDefaultAvatarFeet, ProceduralFaceMovementFlagsAndBlendshapes, FarGrabJoints + MigrateSkeletonURLToTraits }; enum class DomainConnectRequestVersion : PacketVersion { From be7eb572058815eec125ee1d43880acaf6bd8b66 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 18:08:10 -0700 Subject: [PATCH 077/207] handle whitelist avatar URL override via traits --- assignment-client/src/Agent.cpp | 23 ++++++-- assignment-client/src/AssignmentClient.cpp | 19 +------ assignment-client/src/avatars/AvatarMixer.cpp | 53 +++++-------------- assignment-client/src/avatars/AvatarMixer.h | 8 +-- .../src/avatars/AvatarMixerClientData.cpp | 53 +++++++++++++++++-- .../src/avatars/AvatarMixerClientData.h | 10 ++-- .../src/avatars/AvatarMixerSlave.cpp | 16 ++---- .../src/avatars/AvatarMixerSlave.h | 6 +++ .../src/avatars/AvatarMixerSlavePool.cpp | 2 +- .../src/avatars/AvatarMixerSlavePool.h | 8 ++- interface/src/Application.cpp | 4 +- libraries/avatars/src/AvatarData.cpp | 27 +++++++--- libraries/avatars/src/AvatarData.h | 2 +- libraries/avatars/src/AvatarTraits.h | 3 ++ libraries/avatars/src/ClientTraitsHandler.cpp | 48 ++++++++++++----- libraries/avatars/src/ClientTraitsHandler.h | 12 ++++- libraries/networking/src/ExtendedIODevice.h | 39 ++++++++++++++ libraries/networking/src/udt/BasePacket.cpp | 2 +- libraries/networking/src/udt/BasePacket.h | 22 +------- libraries/networking/src/udt/PacketList.h | 18 +------ 20 files changed, 224 insertions(+), 151 deletions(-) create mode 100644 libraries/networking/src/ExtendedIODevice.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4cc24e2110..049e7d0ede 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -25,16 +25,19 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include +#include #include #include @@ -49,12 +52,12 @@ #include #include // TODO: consider moving to scriptengine.h +#include "AssignmentDynamicFactory.h" #include "entities/AssignmentParentFinder.h" #include "RecordingScriptingInterface.h" #include "AbstractAudioInterface.h" #include "AgentScriptingInterface.h" - static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10; Agent::Agent(ReceivedMessage& message) : @@ -63,6 +66,18 @@ Agent::Agent(ReceivedMessage& message) : _audioGate(AudioConstants::SAMPLE_RATE, AudioConstants::MONO), _avatarAudioTimer(this) { + DependencyManager::set(); + + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(false); + + DependencyManager::registerInheritance(); + DependencyManager::set(); + + DependencyManager::set(); + DependencyManager::set(); + _entityEditSender.setPacketsPerSecond(DEFAULT_ENTITY_PPS_PER_SCRIPT); DependencyManager::get()->setPacketSender(&_entityEditSender); @@ -99,7 +114,6 @@ Agent::Agent(ReceivedMessage& message) : this, "handleOctreePacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); - // 100Hz timer for audio const int TARGET_INTERVAL_MSEC = 10; // 10ms connect(&_avatarAudioTimer, &QTimer::timeout, this, &Agent::processAgentAvatarAudio); @@ -439,7 +453,7 @@ void Agent::executeScript() { encodedBuffer = audio; } - AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, + AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0), packetType, _selectedCodecName); }); @@ -842,6 +856,9 @@ void Agent::aboutToFinish() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + + DependencyManager::destroy(); + QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); // cleanup codec & encoder diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index d761699285..426f3ce6fc 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -21,10 +21,7 @@ #include #include #include -#include #include -#include -#include #include #include #include @@ -32,16 +29,12 @@ #include #include #include -#include -#include -#include + #include #include #include "AssignmentClientLogging.h" -#include "AssignmentDynamicFactory.h" #include "AssignmentFactory.h" -#include "avatars/ScriptableAvatar.h" const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; @@ -57,21 +50,11 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri DependencyManager::set(); DependencyManager::set(); - auto scriptableAvatar = DependencyManager::set(); auto addressManager = DependencyManager::set(); // create a NodeList as an unassigned client, must be after addressManager auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); - auto animationCache = DependencyManager::set(); - DependencyManager::set(); - auto entityScriptingInterface = DependencyManager::set(false); - - DependencyManager::registerInheritance(); - auto dynamicFactory = DependencyManager::set(); - DependencyManager::set(); - DependencyManager::set(); - nodeList->startThread(); // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b139870e6e..228102ee53 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -39,7 +39,8 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer"; const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; AvatarMixer::AvatarMixer(ReceivedMessage& message) : - ThreadedAssignment(message) + ThreadedAssignment(message), + _slavePool(&_slaveSharedData) { // make sure we hear about node kills so we can tell the other nodes connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled); @@ -338,17 +339,7 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { sendIdentity = true; qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } - if (nodeData && nodeData->getAvatarSkeletonModelUrlMustChange()) { // never true for an empty _avatarWhitelist - nodeData->setAvatarSkeletonModelUrlMustChange(false); - AvatarData& avatar = nodeData->getAvatar(); - static const QUrl emptyURL(""); - QUrl url = avatar.cannonicalSkeletonModelURL(emptyURL); - if (!isAvatarInWhitelist(url)) { - qCDebug(avatars) << "Forbidden avatar" << nodeData->getNodeID() << avatar.getSkeletonModelURL() << "replaced with" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); - avatar.setSkeletonModelURL(_replacementAvatar); - sendIdentity = true; - } - } + if (sendIdentity && !node->isUpstream()) { sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar. // since this packet includes a change to either the skeleton model URL or the display name @@ -360,22 +351,6 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { } } -bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { - // The avatar is in the whitelist if: - // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND - // 2. The avatar's URL's path starts with the path of that same URL in the whitelist - for (const auto& whiteListedPrefix : _avatarWhitelist) { - auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); - // check if this script URL matches the whitelist domain and, optionally, is beneath the path - if (url.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && - url.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { - return true; - } - } - - return false; -} - void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) { // throttle using a modified proportional-integral controller const float FRAME_TIME = USECS_PER_SECOND / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; @@ -588,8 +563,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate bool identityChanged = false; bool displayNameChanged = false; - bool skeletonModelUrlChanged = false; - avatar.processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged, skeletonModelUrlChanged); + avatar.processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); @@ -597,9 +571,6 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes if (displayNameChanged) { nodeData->setAvatarSessionDisplayNameMustChange(true); } - if (skeletonModelUrlChanged && !_avatarWhitelist.isEmpty()) { - nodeData->setAvatarSkeletonModelUrlMustChange(true); - } } } } @@ -992,20 +963,22 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight << "and a maximum avatar height of" << _domainMaximumHeight; - const QString AVATAR_WHITELIST_DEFAULT{ "" }; static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; - _avatarWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION].toString(AVATAR_WHITELIST_DEFAULT).split(',', QString::KeepEmptyParts); + _slaveSharedData.skeletonURLWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION] + .toString().split(',', QString::KeepEmptyParts); static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar"; - _replacementAvatar = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION].toString(REPLACEMENT_AVATAR_DEFAULT); + _slaveSharedData.skeletonReplacementURL = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION] + .toString(); - if ((_avatarWhitelist.count() == 1) && _avatarWhitelist[0].isEmpty()) { - _avatarWhitelist.clear(); // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + if (_slaveSharedData.skeletonURLWhitelist.count() == 1 && _slaveSharedData.skeletonURLWhitelist[0].isEmpty()) { + // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + _slaveSharedData.skeletonURLWhitelist.clear(); } - if (_avatarWhitelist.isEmpty()) { + if (_slaveSharedData.skeletonURLWhitelist.isEmpty()) { qCDebug(avatars) << "All avatars are allowed."; } else { - qCDebug(avatars) << "Avatars other than" << _avatarWhitelist << "will be replaced by" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + qCDebug(avatars) << "Avatars other than" << _slaveSharedData.skeletonURLWhitelist << "will be replaced by" << (_slaveSharedData.skeletonReplacementURL.isEmpty() ? "default" : _slaveSharedData.skeletonReplacementURL.toString()); } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 14f84c9e7b..8ae7fc9931 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -59,7 +59,6 @@ private slots: void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID); void start(); - private: AvatarMixerClientData* getOrCreateClientData(SharedNodePointer node); std::chrono::microseconds timeFrame(p_high_resolution_clock::time_point& timestamp); @@ -69,11 +68,6 @@ private: void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); void manageIdentityData(const SharedNodePointer& node); - bool isAvatarInWhitelist(const QUrl& url); - - const QString REPLACEMENT_AVATAR_DEFAULT{ "" }; - QStringList _avatarWhitelist { }; - QString _replacementAvatar { REPLACEMENT_AVATAR_DEFAULT }; void optionallyReplicatePacket(ReceivedMessage& message, const Node& node); @@ -83,7 +77,6 @@ private: float _trailingMixRatio { 0.0f }; float _throttlingRatio { 0.0f }; - int _sumListeners { 0 }; int _numStatFrames { 0 }; int _numTightLoopFrames { 0 }; @@ -127,6 +120,7 @@ private: RateCounter<> _loopRate; // this is the rate that the main thread tight loop runs AvatarMixerSlavePool _slavePool; + SlaveSharedData _slaveSharedData; }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index f279d76450..552fe9a58b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -16,6 +16,8 @@ #include #include +#include "AvatarMixerSlave.h" + AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID), _receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size()) @@ -48,7 +50,7 @@ void AvatarMixerClientData::queuePacket(QSharedPointer message, _packetQueue.push(message); } -int AvatarMixerClientData::processPackets() { +int AvatarMixerClientData::processPackets(SlaveSharedData* slaveSharedData) { int packetsProcessed = 0; SharedNodePointer node = _packetQueue.node; assert(_packetQueue.empty() || node); @@ -64,7 +66,7 @@ int AvatarMixerClientData::processPackets() { parseData(*packet); break; case PacketType::SetAvatarTraits: - processSetTraitsMessage(*packet); + processSetTraitsMessage(*packet, slaveSharedData, *node); break; default: Q_UNREACHABLE(); @@ -92,7 +94,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead())); } -void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { +void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode) { // pull the trait version from the message AvatarTraits::TraitVersion packetTraitVersion; message.readPrimitive(&packetTraitVersion); @@ -111,6 +113,11 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { _avatar->processTrait(traitType, message.readWithoutCopy(traitSize)); _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + + if (traitType == AvatarTraits::SkeletonModelURL) { + // special handling for skeleton model URL, since we need to make sure it is in the whitelist + checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); + } anyTraitsChanged = true; } else { @@ -123,6 +130,46 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { } } +void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *slaveSharedData, Node& sendingNode, + AvatarTraits::TraitVersion traitVersion) { + const auto& whitelist = slaveSharedData->skeletonURLWhitelist; + + if (!whitelist.isEmpty()) { + bool inWhitelist = false; + auto avatarURL = _avatar->getSkeletonModelURL(); + + // The avatar is in the whitelist if: + // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND + // 2. The avatar's URL's path starts with the path of that same URL in the whitelist + for (const auto& whiteListedPrefix : whitelist) { + auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + // check if this script URL matches the whitelist domain and, optionally, is beneath the path + if (avatarURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && + avatarURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { + inWhitelist = true; + + break; + } + } + + if (!inWhitelist) { + // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change + _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); + + qDebug() << "Sending overwritten" << _avatar->getSkeletonModelURL() << "back to sending avatar"; + + auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); + + // the returned set traits packet uses the trait version from the incoming packet + // so the client knows they should not overwrite if they have since changed the trait + _avatar->packTrait(AvatarTraits::SkeletonModelURL, *packet, traitVersion); + + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(packet), sendingNode); + } + } +} + uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastTimes.find(nodeUUID); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 8792ecfa5d..96b420afc1 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -34,6 +34,8 @@ const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; +struct SlaveSharedData; + class AvatarMixerClientData : public NodeData { Q_OBJECT public: @@ -66,8 +68,6 @@ public: void flagIdentityChange() { _identityChangeTimestamp = usecTimestampNow(); } bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } - bool getAvatarSkeletonModelUrlMustChange() const { return _avatarSkeletonModelUrlMustChange; } - void setAvatarSkeletonModelUrlMustChange(bool set = true) { _avatarSkeletonModelUrlMustChange = set; } void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } @@ -119,9 +119,11 @@ public: QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } void queuePacket(QSharedPointer message, SharedNodePointer node); - int processPackets(); // returns number of packets processed + int processPackets(SlaveSharedData* slaveSharedData); // returns number of packets processed - void processSetTraitsMessage(ReceivedMessage& message); + void processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode); + void checkSkeletonURLAgainstWhitelist(SlaveSharedData* slaveSharedData, Node& sendingNode, + AvatarTraits::TraitVersion traitVersion); using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index c996008a48..88e394bc95 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -59,7 +59,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { auto nodeData = dynamic_cast(node->getLinkedData()); if (nodeData) { _stats.nodesProcessed++; - _stats.packetsProcessed += nodeData->processPackets(); + _stats.packetsProcessed += nodeData->processPackets(_sharedData); } auto end = usecTimestampNow(); _stats.processIncomingPacketsElapsedTime += (end - start); @@ -108,20 +108,10 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste if (lastReceivedVersion > lastSentVersion) { // there is an update to this trait, add it to the traits packet - // write the trait type and the trait version - traitsPacketList.writePrimitive(traitType); - traitsPacketList.writePrimitive(lastReceivedVersion); - - // update the last sent version since we're adding this to the packet + // update the last sent version listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion); - if (traitType == AvatarTraits::SkeletonModelURL) { - // get an encoded version of the URL, write its size and then the data itself - auto encodedSkeletonURL = sendingAvatar->getSkeletonModelURL().toEncoded(); - - traitsPacketList.writePrimitive(uint16_t(encodedSkeletonURL.size())); - traitsPacketList.write(encodedSkeletonURL); - } + sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); } } diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index ed27709e3e..5112faae29 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -78,11 +78,16 @@ public: jobElapsedTime += rhs.jobElapsedTime; return *this; } +}; +struct SlaveSharedData { + QStringList skeletonURLWhitelist; + QUrl skeletonReplacementURL; }; class AvatarMixerSlave { public: + AvatarMixerSlave(SlaveSharedData* sharedData) : _sharedData(sharedData) {}; using ConstIter = NodeList::const_iterator; void configure(ConstIter begin, ConstIter end); @@ -115,6 +120,7 @@ private: float _throttlingRatio { 0.0f }; AvatarMixerSlaveStats _stats; + SlaveSharedData* _sharedData; }; #endif // hifi_AvatarMixerSlave_h diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp index 962bba21d2..cf842ac792 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp @@ -168,7 +168,7 @@ void AvatarMixerSlavePool::resize(int numThreads) { if (numThreads > _numThreads) { // start new slaves for (int i = 0; i < numThreads - _numThreads; ++i) { - auto slave = new AvatarMixerSlaveThread(*this); + auto slave = new AvatarMixerSlaveThread(*this, _slaveSharedData); slave->start(); _slaves.emplace_back(slave); } diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.h b/assignment-client/src/avatars/AvatarMixerSlavePool.h index 15bd681b2c..71a9ace0d3 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.h +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.h @@ -32,7 +32,8 @@ class AvatarMixerSlaveThread : public QThread, public AvatarMixerSlave { using Lock = std::unique_lock; public: - AvatarMixerSlaveThread(AvatarMixerSlavePool& pool) : _pool(pool) {} + AvatarMixerSlaveThread(AvatarMixerSlavePool& pool, SlaveSharedData* slaveSharedData) : + AvatarMixerSlave(slaveSharedData), _pool(pool) {}; void run() override final; @@ -59,7 +60,8 @@ class AvatarMixerSlavePool { public: using ConstIter = NodeList::const_iterator; - AvatarMixerSlavePool(int numThreads = QThread::idealThreadCount()) { setNumThreads(numThreads); } + AvatarMixerSlavePool(SlaveSharedData* slaveSharedData, int numThreads = QThread::idealThreadCount()) : + _slaveSharedData(slaveSharedData) { setNumThreads(numThreads); } ~AvatarMixerSlavePool() { resize(0); } // Jobs the slave pool can do... @@ -98,6 +100,8 @@ private: Queue _queue; ConstIter _begin; ConstIter _end; + + SlaveSharedData* _slaveSharedData; }; #endif // hifi_AvatarMixerSlavePool_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 396c6cbcac..e556fd734f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6393,8 +6393,8 @@ void Application::nodeActivated(SharedNodePointer node) { if (_avatarOverrideUrl.isValid()) { getMyAvatar()->useFullAvatarURL(_avatarOverrideUrl); } - static const QUrl empty{}; - if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->cannonicalSkeletonModelURL(empty)) { + + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->getSkeletonModelURL()) { getMyAvatar()->resetFullAvatarURL(); } getMyAvatar()->markIdentityDataChanged(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ddfeb4df24..c9d8f7bb1e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1750,12 +1750,6 @@ glm::quat AvatarData::getOrientationOutbound() const { return (getLocalOrientation()); } -static const QUrl emptyURL(""); -QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { - // We don't put file urls on the wire, but instead convert to empty. - return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; -} - void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { @@ -1836,6 +1830,27 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide } } +void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion) { + destination.writePrimitive(traitType); + + if (traitVersion > 0) { + AvatarTraits::TraitVersion typedVersion = traitVersion; + destination.writePrimitive(typedVersion); + } + + if (traitType == AvatarTraits::SkeletonModelURL) { + QByteArray encodedSkeletonURL; + if (_skeletonModelURL.scheme() != "file" && _skeletonModelURL.scheme() != "qrc") { + encodedSkeletonURL = _skeletonModelURL.toEncoded(); + } + + AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); + destination.writePrimitive(encodedURLSize); + + destination.write(encodedSkeletonURL); + } +} + void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { if (traitType == AvatarTraits::SkeletonModelURL) { // get the URL from the binary data diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 12e8370b86..619f8e1722 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -426,7 +426,6 @@ public: virtual ~AvatarData(); static const QUrl& defaultFullAvatarModelUrl(); - QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; virtual bool isMyAvatar() const { return false; } @@ -958,6 +957,7 @@ public: // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion = -1); void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); QByteArray identityByteArray(bool setIsReplicated = false) const; diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 90258982c3..121e1057c6 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -29,6 +29,9 @@ namespace AvatarTraits { using TraitVersion = uint32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; + using NullableTraitVersion = int64_t; + const NullableTraitVersion NULL_TRAIT_VERSION = -1; + using TraitWireSize = uint16_t; using SimpleTraitVersions = std::vector; diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 5d1f2e4926..2518dedf37 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -27,6 +27,8 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) : resetForNewMixer(); } }); + + nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride"); } void ClientTraitsHandler::resetForNewMixer() { @@ -63,19 +65,11 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { if (_performInitialSend || changedTraitsCopy.count(AvatarTraits::SkeletonModelURL)) { traitsPacketList->startSegment(); - - traitsPacketList->writePrimitive(AvatarTraits::SkeletonModelURL); - - auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded(); - - AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); - traitsPacketList->writePrimitive(encodedURLSize); - - qDebug() << "Sending trait of size" << encodedURLSize; - - traitsPacketList->write(encodedSkeletonURL); - + _owningAvatar->packTrait(AvatarTraits::SkeletonModelURL, *traitsPacketList); traitsPacketList->endSegment(); + + // keep track of our skeleton version in case we get an override back + _currentSkeletonVersion = _currentTraitVersion; } nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); @@ -84,3 +78,33 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { _performInitialSend = false; } } + +void ClientTraitsHandler::processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode) { + if (sendingNode->getType() == NodeType::AvatarMixer) { + while (message->getBytesLeftToRead()) { + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + AvatarTraits::TraitVersion traitVersion; + message->readPrimitive(&traitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + message->readPrimitive(&traitBinarySize); + + // only accept an override if this is for a trait type we override + // and the version matches what we last sent for skeleton + if (traitType == AvatarTraits::SkeletonModelURL + && traitVersion == _currentSkeletonVersion + && !hasTraitChanged(AvatarTraits::SkeletonModelURL)) { + // override the skeleton URL but do not mark the trait as having changed + // so that we don't unecessarily sent a new trait packet to the mixer with the overriden URL + auto encodedSkeletonURL = QUrl::fromEncoded(message->readWithoutCopy(traitBinarySize)); + _owningAvatar->setSkeletonModelURL(encodedSkeletonURL); + + _changedTraits.erase(AvatarTraits::SkeletonModelURL); + } else { + message->seek(message->getPosition() + traitBinarySize); + } + } + } +} diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 4aea0bb433..3a2b70776c 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -12,13 +12,15 @@ #ifndef hifi_ClientTraitsHandler_h #define hifi_ClientTraitsHandler_h -#include +#include #include "AvatarTraits.h" +#include "Node.h" class AvatarData; -class ClientTraitsHandler { +class ClientTraitsHandler : public QObject { + Q_OBJECT public: ClientTraitsHandler(AvatarData* owningAvatar); @@ -31,11 +33,17 @@ public: void resetForNewMixer(); +public slots: + void processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode); + private: AvatarData* _owningAvatar; AvatarTraits::TraitTypeSet _changedTraits; AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; + + AvatarTraits::NullableTraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; + bool _performInitialSend { false }; }; diff --git a/libraries/networking/src/ExtendedIODevice.h b/libraries/networking/src/ExtendedIODevice.h new file mode 100644 index 0000000000..7df1af74b6 --- /dev/null +++ b/libraries/networking/src/ExtendedIODevice.h @@ -0,0 +1,39 @@ +// +// ExtendedIODevice.h +// libraries/networking/src +// +// Created by Stephen Birarda on 8/7/18. +// Copyright 2018 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_ExtendedIODevice_h +#define hifi_ExtendedIODevice_h + +#include + +class ExtendedIODevice : public QIODevice { +public: + ExtendedIODevice(QObject* parent = nullptr) : QIODevice(parent) {}; + + template qint64 peekPrimitive(T* data); + template qint64 readPrimitive(T* data); + template qint64 writePrimitive(const T& data); +}; + +template qint64 ExtendedIODevice::peekPrimitive(T* data) { + return peek(reinterpret_cast(data), sizeof(T)); +} + +template qint64 ExtendedIODevice::readPrimitive(T* data) { + return read(reinterpret_cast(data), sizeof(T)); +} + +template qint64 ExtendedIODevice::writePrimitive(const T& data) { + static_assert(!std::is_pointer::value, "T must not be a pointer"); + return write(reinterpret_cast(&data), sizeof(T)); +} + +#endif // hifi_ExtendedIODevice_h diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 0540e60a0e..92ccdd6117 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -78,7 +78,7 @@ BasePacket::BasePacket(std::unique_ptr data, qint64 size, const HifiSock } BasePacket::BasePacket(const BasePacket& other) : - QIODevice() + ExtendedIODevice() { *this = other; } diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index d9b624b595..9c3244e08b 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -16,16 +16,15 @@ #include -#include - #include #include "../HifiSockAddr.h" #include "Constants.h" +#include "../ExtendedIODevice.h" namespace udt { -class BasePacket : public QIODevice { +class BasePacket : public ExtendedIODevice { Q_OBJECT public: static const qint64 PACKET_WRITE_ERROR; @@ -85,10 +84,6 @@ public: void setReceiveTime(p_high_resolution_clock::time_point receiveTime) { _receiveTime = receiveTime; } p_high_resolution_clock::time_point getReceiveTime() const { return _receiveTime; } - - template qint64 peekPrimitive(T* data); - template qint64 readPrimitive(T* data); - template qint64 writePrimitive(const T& data); protected: BasePacket(qint64 size); @@ -116,19 +111,6 @@ protected: p_high_resolution_clock::time_point _receiveTime; // captures the time the packet received (only used on receiving end) }; - -template qint64 BasePacket::peekPrimitive(T* data) { - return peek(reinterpret_cast(data), sizeof(T)); -} - -template qint64 BasePacket::readPrimitive(T* data) { - return read(reinterpret_cast(data), sizeof(T)); -} - -template qint64 BasePacket::writePrimitive(const T& data) { - static_assert(!std::is_pointer::value, "T must not be a pointer"); - return write(reinterpret_cast(&data), sizeof(T)); -} } // namespace udt diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index ff1860c3d1..b9bd6a8c15 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -14,8 +14,7 @@ #include -#include - +#include "../ExtendedIODevice.h" #include "Packet.h" #include "PacketHeaders.h" @@ -25,7 +24,7 @@ namespace udt { class Packet; -class PacketList : public QIODevice { +class PacketList : public ExtendedIODevice { Q_OBJECT public: using MessageNumber = uint32_t; @@ -59,9 +58,6 @@ public: virtual bool isSequential() const override { return false; } virtual qint64 size() const override { return getDataSize(); } - template qint64 readPrimitive(T* data); - template qint64 writePrimitive(const T& data); - qint64 writeString(const QString& string); protected: @@ -105,16 +101,6 @@ private: QByteArray _extendedHeader; }; -template qint64 PacketList::readPrimitive(T* data) { - static_assert(!std::is_pointer::value, "T must not be a pointer"); - return read(reinterpret_cast(data), sizeof(T)); -} - -template qint64 PacketList::writePrimitive(const T& data) { - static_assert(!std::is_pointer::value, "T must not be a pointer"); - return write(reinterpret_cast(&data), sizeof(T)); -} - template std::unique_ptr PacketList::takeFront() { static_assert(std::is_base_of::value, "T must derive from Packet."); From a0df68f32fda747afbc9d68c52524a0d4d810f9c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Aug 2018 13:53:35 -0700 Subject: [PATCH 078/207] move skeleton model URL emit to AvatarData --- assignment-client/src/avatars/ScriptableAvatar.cpp | 2 +- assignment-client/src/avatars/ScriptableAvatar.h | 2 +- interface/src/avatar/MyAvatar.cpp | 4 +--- libraries/avatars/src/AvatarData.cpp | 4 +++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 4e47cf96f1..16a49a8999 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -1,6 +1,6 @@ // // ScriptableAvatar.cpp -// +// assignment-client/src/avatars // // Created by Clement on 7/22/14. // Copyright 2014 High Fidelity, Inc. diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 8e3d779dda..201bfe67e8 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -1,6 +1,6 @@ // // ScriptableAvatar.h -// +// assignment-client/src/avatars // // Created by Clement on 7/22/14. // Copyright 2014 High Fidelity, Inc. diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index df9afeb92d..90d1ad257b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1699,9 +1699,9 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { } QObject::disconnect(*skeletonConnection); }); + saveAvatarUrl(); emit skeletonChanged(); - emit skeletonModelURLChanged(); if (previousSkeletonModelURL != _skeletonModelURL) { _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); @@ -1776,8 +1776,6 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN setSkeletonModelURL(fullAvatarURL); UserActivityLogger::getInstance().changedModel("skeleton", urlString); } - - markIdentityDataChanged(); } glm::vec3 MyAvatar::getSkeletonPosition() const { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c9d8f7bb1e..c9d8049cd3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1891,11 +1891,13 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (expanded == _skeletonModelURL) { return; } + _skeletonModelURL = expanded; qCDebug(avatars) << "Changing skeleton model for avatar" << getSessionUUID() << "to" << _skeletonModelURL.toString(); updateJointMappings(); - markIdentityDataChanged(); + + emit skeletonModelURLChanged(); } void AvatarData::setDisplayName(const QString& displayName) { From ea7c0e923a9958d2fa21ba440d15bb48cd20075b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Aug 2018 13:59:21 -0700 Subject: [PATCH 079/207] make client traits handler a unique ptr in AvatarData --- .../src/avatars/ScriptableAvatar.cpp | 10 ++++--- .../src/avatars/ScriptableAvatar.h | 5 ++-- interface/src/avatar/MyAvatar.cpp | 10 +++---- interface/src/avatar/MyAvatar.h | 4 --- libraries/avatars/src/AvatarData.cpp | 5 ++++ libraries/avatars/src/AvatarData.h | 6 +++++ libraries/avatars/src/AvatarTraits.h | 27 ++++++++++++++++--- libraries/avatars/src/ClientTraitsHandler.cpp | 2 +- libraries/avatars/src/ClientTraitsHandler.h | 4 +-- 9 files changed, 49 insertions(+), 24 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 16a49a8999..6f04cfa196 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -16,9 +16,13 @@ #include #include -#include #include +#include +#include +ScriptableAvatar::ScriptableAvatar() { + _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); +} QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { _globalPosition = getWorldPosition(); @@ -63,8 +67,6 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _animSkeleton.reset(); AvatarData::setSkeletonModelURL(skeletonModelURL); - - _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); } static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { @@ -141,5 +143,5 @@ void ScriptableAvatar::update(float deltatime) { } } - _clientTraitsHandler.sendChangedTraitsToMixer(); + _clientTraitsHandler->sendChangedTraitsToMixer(); } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 201bfe67e8..89f9369133 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -15,7 +15,6 @@ #include #include #include -#include #include /**jsdoc @@ -125,6 +124,8 @@ class ScriptableAvatar : public AvatarData, public Dependency { Q_OBJECT public: + ScriptableAvatar(); + /**jsdoc * @function Avatar.startAnimation * @param {string} url @@ -165,8 +166,6 @@ private: QStringList _maskedJoints; AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies std::shared_ptr _animSkeleton; - - ClientTraitsHandler _clientTraitsHandler { this }; }; #endif // hifi_ScriptableAvatar_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 90d1ad257b..2c88f917a1 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -119,9 +119,9 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()), - _clientTraitsHandler(this) + _hmdAtRestDetector(glm::vec3(0), glm::quat()) { + _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); // give the pointer to our head to inherited _headData variable from AvatarData _headData = new MyHead(this); @@ -514,7 +514,7 @@ void MyAvatar::update(float deltaTime) { sendIdentityPacket(); } - _clientTraitsHandler.sendChangedTraitsToMixer(); + _clientTraitsHandler->sendChangedTraitsToMixer(); simulate(deltaTime); @@ -1702,10 +1702,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { saveAvatarUrl(); emit skeletonChanged(); - - if (previousSkeletonModelURL != _skeletonModelURL) { - _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); - } } void MyAvatar::removeAvatarEntities(const std::function& condition) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8fdc4ba4e3..ba6348cc22 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1334,7 +1334,6 @@ public slots: */ void setAnimGraphUrl(const QUrl& url); // thread-safe - /**jsdoc * @function MyAvatar.getPositionForAudio * @returns {Vec3} @@ -1347,7 +1346,6 @@ public slots: */ glm::quat getOrientationForAudio(); - /**jsdoc * @function MyAvatar.setModelScale * @param {number} scale @@ -1776,8 +1774,6 @@ private: bool _haveReceivedHeightLimitsFromDomain { false }; int _disableHandTouchCount { 0 }; - - ClientTraitsHandler _clientTraitsHandler; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c9d8049cd3..f32da39bba 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -43,6 +43,7 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" +#include "ClientTraitsHandler.h" //#define WANT_DEBUG @@ -1897,6 +1898,10 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); + if (_clientTraitsHandler) { + _clientTraitsHandler->markTraitChanged(AvatarTraits::SkeletonModelURL); + } + emit skeletonModelURLChanged(); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 619f8e1722..a5d2d0749b 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -372,6 +372,8 @@ public: bool operator<(const AvatarPriority& other) const { return priority < other.priority; } }; +class ClientTraitsHandler; + class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT @@ -924,6 +926,7 @@ public: * @param {string} entityData */ Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + /**jsdoc * @function MyAvatar.clearAvatarEntity * @param {Uuid} entityID @@ -1435,6 +1438,9 @@ protected: bool _hasProcessedFirstIdentity { false }; float _density; + // null unless MyAvatar or ScriptableAvatar sending traits data to mixer + std::unique_ptr _clientTraitsHandler; + template T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { int index = getFauxJointIndex(name); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 121e1057c6..d30da0e1af 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -12,8 +12,8 @@ #ifndef hifi_AvatarTraits_h #define hifi_AvatarTraits_h +#include #include -#include #include namespace AvatarTraits { @@ -23,7 +23,28 @@ namespace AvatarTraits { TotalTraitTypes }; - using TraitTypeSet = std::set; + class TraitTypeSet { + public: + TraitTypeSet() {}; + + TraitTypeSet(std::initializer_list types) { + for (auto type : types) { + _types[type] = true; + } + }; + + bool contains(TraitType type) const { return _types[type]; } + + bool hasAny() const { return std::find(_types.begin(), _types.end(), true) != _types.end(); } + int size() const { return std::count(_types.begin(), _types.end(), true); } + + void insert(TraitType type) { _types[type] = true; } + void erase(TraitType type) { _types[type] = false; } + void clear() { std::fill(_types.begin(), _types.end(), false); } + private: + std::vector _types = { AvatarTraits::TotalTraitTypes, false }; + }; + const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL }; using TraitVersion = uint32_t; @@ -35,6 +56,6 @@ namespace AvatarTraits { using TraitWireSize = uint16_t; using SimpleTraitVersions = std::vector; -} +}; #endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 2518dedf37..8b3ded1e1c 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -63,7 +63,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto changedTraitsCopy { _changedTraits }; _changedTraits.clear(); - if (_performInitialSend || changedTraitsCopy.count(AvatarTraits::SkeletonModelURL)) { + if (_performInitialSend || changedTraitsCopy.contains(AvatarTraits::SkeletonModelURL)) { traitsPacketList->startSegment(); _owningAvatar->packTrait(AvatarTraits::SkeletonModelURL, *traitsPacketList); traitsPacketList->endSegment(); diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 3a2b70776c..1d4c67d0c4 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -26,10 +26,10 @@ public: void sendChangedTraitsToMixer(); - bool hasChangedTraits() { return _changedTraits.size(); } + bool hasChangedTraits() { return _changedTraits.hasAny(); } void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); } - bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.count(checkTrait) > 0; } + bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.contains(checkTrait) > 0; } void resetForNewMixer(); From e6b419d283f49b13e75e5348121b0a638179dcec Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 19:01:09 -0700 Subject: [PATCH 080/207] add instanced traits and migrate avatar entities --- .../src/avatars/AvatarMixerClientData.cpp | 77 +++++---- .../src/avatars/AvatarMixerClientData.h | 15 +- .../src/avatars/AvatarMixerSlave.cpp | 74 +++++++- libraries/avatars/src/AssociatedTraitValues.h | 158 ++++++++++++++++++ libraries/avatars/src/AvatarData.cpp | 124 ++++++++++---- libraries/avatars/src/AvatarData.h | 16 +- libraries/avatars/src/AvatarHashMap.cpp | 75 +++++---- libraries/avatars/src/AvatarHashMap.h | 9 +- libraries/avatars/src/AvatarTraits.h | 51 +++--- libraries/avatars/src/ClientTraitsHandler.cpp | 60 +++++-- libraries/avatars/src/ClientTraitsHandler.h | 25 ++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 5 +- 13 files changed, 502 insertions(+), 189 deletions(-) create mode 100644 libraries/avatars/src/AssociatedTraitValues.h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 552fe9a58b..34b7ec97ff 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -19,8 +19,7 @@ #include "AvatarMixerSlave.h" AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID), - _receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size()) + NodeData(nodeID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); @@ -107,21 +106,47 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, Sl AvatarTraits::TraitType traitType; message.readPrimitive(&traitType); - AvatarTraits::TraitWireSize traitSize; - message.readPrimitive(&traitSize); + if (AvatarTraits::isSimpleTrait(traitType)) { + AvatarTraits::TraitWireSize traitSize; + message.readPrimitive(&traitSize); - if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { - _avatar->processTrait(traitType, message.readWithoutCopy(traitSize)); - _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + if (packetTraitVersion > _lastReceivedTraitVersions[traitType]) { + _avatar->processTrait(traitType, message.read(traitSize)); + _lastReceivedTraitVersions[traitType] = packetTraitVersion; - if (traitType == AvatarTraits::SkeletonModelURL) { - // special handling for skeleton model URL, since we need to make sure it is in the whitelist - checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); + if (traitType == AvatarTraits::SkeletonModelURL) { + // special handling for skeleton model URL, since we need to make sure it is in the whitelist + checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); + } + + anyTraitsChanged = true; + } else { + message.seek(message.getPosition() + traitSize); } - - anyTraitsChanged = true; } else { - message.seek(message.getPosition() + traitSize); + AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + AvatarTraits::TraitWireSize traitSize; + message.readPrimitive(&traitSize); + + auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID); + + if (packetTraitVersion > instanceVersionRef) { + if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) { + _avatar->processDeletedTraitInstance(traitType, instanceID); + + // to track a deleted instance but keep version information + // the avatar mixer uses the negative value of the sent version + instanceVersionRef = -packetTraitVersion; + } else { + _avatar->processTraitInstance(traitType, instanceID, message.read(traitSize)); + instanceVersionRef = packetTraitVersion; + } + + anyTraitsChanged = true; + } else { + message.seek(message.getPosition() + traitSize); + } } } @@ -257,29 +282,3 @@ AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherA return TraitsCheckTimestamp(); } } - -AvatarTraits::TraitVersion AvatarMixerClientData::getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, - AvatarTraits::TraitType traitType) const { - auto it = _sentSimpleTraitVersions.find(otherAvatar); - - if (it != _sentSimpleTraitVersions.end()) { - return it->second[traitType]; - } - - return AvatarTraits::DEFAULT_TRAIT_VERSION; -} - -void AvatarMixerClientData::setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion traitVersion) { - - auto it = _sentSimpleTraitVersions.find(otherAvatar); - - if (it == _sentSimpleTraitVersions.end()) { - auto pair = _sentSimpleTraitVersions.insert({ - otherAvatar, { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } - }); - - it = pair.first; - } - - it->second[traitType] = traitVersion; -} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 96b420afc1..dcbf8a6dba 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -128,16 +128,15 @@ public: using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; TraitsCheckTimestamp getLastReceivedTraitsChange() const { return _lastReceivedTraitsChange; } - AvatarTraits::TraitVersion getLastReceivedSimpleTraitVersion(AvatarTraits::TraitType traitType) const - { return _receivedSimpleTraitVersions[traitType]; } + + AvatarTraits::TraitVersions& getLastReceivedTraitVersions() { return _lastReceivedTraitVersions; } + const AvatarTraits::TraitVersions& getLastReceivedTraitVersions() const { return _lastReceivedTraitVersions; } TraitsCheckTimestamp getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const; void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint) { _lastSentTraitsTimestamps[otherAvatar] = sendPoint; } - AvatarTraits::TraitVersion getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType) const; - void setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, - AvatarTraits::TraitVersion traitVersion); + AvatarTraits::TraitVersions& getLastSentTraitVersions(Node::LocalID otherAvatar) { return _sentTraitVersions[otherAvatar]; } private: struct PacketQueue : public std::queue> { @@ -176,11 +175,11 @@ private: QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. bool _requestsDomainListData { false }; - AvatarTraits::SimpleTraitVersions _receivedSimpleTraitVersions; + AvatarTraits::TraitVersions _lastReceivedTraitVersions; TraitsCheckTimestamp _lastReceivedTraitsChange; std::unordered_map _lastSentTraitsTimestamps; - std::unordered_map _sentSimpleTraitVersions; + std::unordered_map _sentTraitVersions; }; #endif // hifi_AvatarMixerClientData_h diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 88e394bc95..ebbaeb7a35 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -99,20 +99,76 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste auto sendingAvatar = sendingNodeData->getAvatarSharedPointer(); // compare trait versions so we can see what exactly needs to go out - for (int i = 0; i < AvatarTraits::TotalTraitTypes; ++i) { - AvatarTraits::TraitType traitType = static_cast(i); + auto& lastSentVersions = listeningNodeData->getLastSentTraitVersions(otherNodeLocalID); + const auto& lastReceivedVersions = sendingNodeData->getLastReceivedTraitVersions(); - auto lastSentVersion = listeningNodeData->getLastSentSimpleTraitVersion(otherNodeLocalID, traitType); - auto lastReceivedVersion = sendingNodeData->getLastReceivedSimpleTraitVersion(traitType); + auto simpleReceivedIt = lastReceivedVersions.simpleCBegin(); + while (simpleReceivedIt != lastReceivedVersions.simpleCEnd()) { + auto traitType = static_cast(std::distance(lastReceivedVersions.simpleCBegin(), + simpleReceivedIt)); - if (lastReceivedVersion > lastSentVersion) { - // there is an update to this trait, add it to the traits packet + // we need to double check that this is actually a simple trait type, since the instanced + // trait types are in the simple vector for access efficiency + if (AvatarTraits::isSimpleTrait(traitType)) { + auto lastReceivedVersion = *simpleReceivedIt; + auto& lastSentVersionRef = lastSentVersions[traitType]; - // update the last sent version - listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion); + if (lastReceivedVersions[traitType] > lastSentVersionRef) { + // there is an update to this trait, add it to the traits packet + sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); - sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); + // update the last sent version + lastSentVersionRef = lastReceivedVersion; + } } + + ++simpleReceivedIt; + } + + // enumerate the received instanced trait versions + auto instancedReceivedIt = lastReceivedVersions.instancedCBegin(); + while (instancedReceivedIt != lastReceivedVersions.instancedCEnd()) { + auto traitType = instancedReceivedIt->traitType; + + // get or create the sent trait versions for this trait type + auto& sentIDValuePairs = lastSentVersions.getInstanceIDValuePairs(traitType); + + // enumerate each received instance + for (auto& receivedInstance : instancedReceivedIt->instances) { + auto instanceID = receivedInstance.id; + const auto receivedVersion = receivedInstance.value; + + // to track deletes and maintain version information for traits + // the mixer stores the negative value of the received version when a trait instance is deleted + bool isDeleted = receivedVersion < 0; + const auto absoluteReceivedVersion = std::abs(receivedVersion); + + // look for existing sent version for this instance + auto sentInstanceIt = std::find_if(sentIDValuePairs.begin(), sentIDValuePairs.end(), + [instanceID](auto& sentInstance) + { + return sentInstance.id == instanceID; + }); + + if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) { + // this instance version exists and has never been sent or is newer so we need to send it + sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion); + + if (sentInstanceIt != sentIDValuePairs.end()) { + sentInstanceIt->value = receivedVersion; + } else { + sentIDValuePairs.emplace_back(instanceID, receivedVersion); + } + } else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) { + // this instance version was deleted and we haven't sent the delete to this client yet + AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion); + + // update the last sent version for this trait instance to the absolute value of the deleted version + sentInstanceIt->value = absoluteReceivedVersion; + } + } + + ++instancedReceivedIt; } // write a null trait type to mark the end of trait data for this avatar diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h new file mode 100644 index 0000000000..b2c0197e5c --- /dev/null +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -0,0 +1,158 @@ +// +// AssociatedTraitValues.h +// libraries/avatars/src +// +// Created by Stephen Birarda on 8/8/18. +// Copyright 2018 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_AssociatedTraitValues_h +#define hifi_AssociatedTraitValues_h + +#include "AvatarTraits.h" + +namespace AvatarTraits { + template + class AssociatedTraitValues { + public: + AssociatedTraitValues() : _simpleTypes(TotalTraitTypes, defaultValue) {} + + void insert(TraitType type, T value) { _simpleTypes[type] = value; } + void erase(TraitType type) { _simpleTypes[type] = defaultValue; } + + T& getInstanceValueRef(TraitType traitType, TraitInstanceID instanceID); + void instanceInsert(TraitType traitType, TraitInstanceID instanceID, T value); + + struct InstanceIDValuePair { + TraitInstanceID id; + T value; + + InstanceIDValuePair(TraitInstanceID id, T value) : id(id), value(value) {}; + }; + + using InstanceIDValuePairs = std::vector; + + InstanceIDValuePairs& getInstanceIDValuePairs(TraitType traitType); + + void instanceErase(TraitType traitType, TraitInstanceID instanceID); + void eraseAllInstances(TraitType traitType); + + // will return defaultValue for instanced traits + T operator[](TraitType traitType) const { return _simpleTypes[traitType]; } + T& operator[](TraitType traitType) { return _simpleTypes[traitType]; } + + void reset() { + std::fill(_simpleTypes.begin(), _simpleTypes.end(), defaultValue); + _instancedTypes.clear(); + } + + typename std::vector::const_iterator simpleCBegin() const { return _simpleTypes.cbegin(); } + typename std::vector::const_iterator simpleCEnd() const { return _simpleTypes.cend(); } + + typename std::vector::iterator simpleBegin() { return _simpleTypes.begin(); } + typename std::vector::iterator simpleEnd() { return _simpleTypes.end(); } + + struct TraitWithInstances { + TraitType traitType; + InstanceIDValuePairs instances; + + TraitWithInstances(TraitType traitType) : traitType(traitType) {}; + TraitWithInstances(TraitType traitType, TraitInstanceID instanceID, T value) : + traitType(traitType), instances({{ instanceID, value }}) {}; + }; + + typename std::vector::const_iterator instancedCBegin() const { return _instancedTypes.cbegin(); } + typename std::vector::const_iterator instancedCEnd() const { return _instancedTypes.cend(); } + + typename std::vector::iterator instancedBegin() { return _instancedTypes.begin(); } + typename std::vector::iterator instancedEnd() { return _instancedTypes.end(); } + + private: + std::vector _simpleTypes; + + typename std::vector::iterator instancesForTrait(TraitType traitType) { + return std::find_if(_instancedTypes.begin(), _instancedTypes.end(), + [traitType](TraitWithInstances& traitWithInstances){ + return traitWithInstances.traitType == traitType; + }); + } + + std::vector _instancedTypes; + }; + + template + inline typename AssociatedTraitValues::InstanceIDValuePairs& + AssociatedTraitValues::getInstanceIDValuePairs(TraitType traitType) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + return it->instances; + } else { + _instancedTypes.emplace_back(traitType); + return _instancedTypes.back().instances; + } + } + + template + inline T& AssociatedTraitValues::getInstanceValueRef(TraitType traitType, TraitInstanceID instanceID) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + auto& instancesVector = it->instances; + auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(), + [instanceID](InstanceIDValuePair& idValuePair){ + return idValuePair.id == instanceID; + }); + if (instanceIt != instancesVector.end()) { + return instanceIt->value; + } else { + instancesVector.emplace_back(instanceID, defaultValue); + return instancesVector.back().value; + } + } else { + _instancedTypes.emplace_back(traitType, instanceID, defaultValue); + return _instancedTypes.back().instances.back().value; + } + } + + template + inline void AssociatedTraitValues::instanceInsert(TraitType traitType, TraitInstanceID instanceID, T value) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + auto instancesVector = it->instances; + auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(), + [instanceID](InstanceIDValuePair& idValuePair){ + return idValuePair.id == instanceID; + }); + if (instanceIt != instancesVector.end()) { + instanceIt->value = value; + } else { + instancesVector.emplace_back(instanceID, value); + } + } else { + _instancedTypes.emplace_back(traitType, instanceID, value); + } + } + + template + inline void AssociatedTraitValues::instanceErase(TraitType traitType, TraitInstanceID instanceID) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + auto instancesVector = it->instances; + instancesVector.erase(std::remove_if(instancesVector.begin(), + instancesVector.end(), + [&instanceID](InstanceIDValuePair& idValuePair){ + return idValuePair.id == instanceID; + })); + } + } + + using TraitVersions = AssociatedTraitValues; +}; + +#endif // hifi_AssociatedTraitValues_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f32da39bba..36dbe00937 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1778,7 +1778,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide >> identity.displayName >> identity.sessionDisplayName >> identity.isReplicated - >> identity.avatarEntityData >> identity.lookAtSnappingEnabled ; @@ -1802,16 +1801,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide identityChanged = true; } - bool avatarEntityDataChanged = false; - _avatarEntitiesLock.withReadLock([&] { - avatarEntityDataChanged = (identity.avatarEntityData != _avatarEntityData); - }); - - if (avatarEntityDataChanged) { - setAvatarEntityData(identity.avatarEntityData); - identityChanged = true; - } - if (identity.lookAtSnappingEnabled != _lookAtSnappingEnabled) { setProperty("lookAtSnappingEnabled", identity.lookAtSnappingEnabled); identityChanged = true; @@ -1831,19 +1820,25 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide } } -void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion) { +QUrl AvatarData::getWireSafeSkeletonModelURL() const { + if (_skeletonModelURL.scheme() != "file" && _skeletonModelURL.scheme() != "qrc") { + return _skeletonModelURL; + } else { + return QUrl(); + } +} + +void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, + AvatarTraits::TraitVersion traitVersion) { destination.writePrimitive(traitType); - if (traitVersion > 0) { + if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; destination.writePrimitive(typedVersion); } if (traitType == AvatarTraits::SkeletonModelURL) { - QByteArray encodedSkeletonURL; - if (_skeletonModelURL.scheme() != "file" && _skeletonModelURL.scheme() != "qrc") { - encodedSkeletonURL = _skeletonModelURL.toEncoded(); - } + QByteArray encodedSkeletonURL = getWireSafeSkeletonModelURL().toEncoded(); AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); destination.writePrimitive(encodedURLSize); @@ -1852,15 +1847,61 @@ void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& } } +void AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { + destination.writePrimitive(traitType); + + if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { + AvatarTraits::TraitVersion typedVersion = traitVersion; + destination.writePrimitive(typedVersion); + } + + destination.write(traitInstanceID.toRfc4122()); + + if (traitType == AvatarTraits::AvatarEntity) { + // grab a read lock on the avatar entities and check for entity data for the given ID + QByteArray entityBinaryData; + + _avatarEntitiesLock.withReadLock([this, &entityBinaryData, &traitInstanceID] { + if (_avatarEntityData.contains(traitInstanceID)) { + entityBinaryData = _avatarEntityData[traitInstanceID]; + } + }); + + if (!entityBinaryData.isNull()) { + AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size(); + + qDebug() << QJsonDocument::fromBinaryData(entityBinaryData).toJson(); + + destination.writePrimitive(entityBinarySize); + destination.write(entityBinaryData); + } else { + destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); + } + } +} + void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { if (traitType == AvatarTraits::SkeletonModelURL) { // get the URL from the binary data auto skeletonModelURL = QUrl::fromEncoded(traitBinaryData); - qDebug() << "Setting skeleton model URL from trait packet to" << skeletonModelURL; setSkeletonModelURL(skeletonModelURL); } } +void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { + if (traitType == AvatarTraits::AvatarEntity) { + updateAvatarEntity(instanceID, traitBinaryData); + } +} + +void AvatarData::processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { + if (traitType == AvatarTraits::AvatarEntity) { + removeAvatarEntityAndDetach(instanceID); + } +} + QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); @@ -1868,17 +1909,13 @@ QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { // when mixers send identity packets to agents, they simply forward along the last incoming sequence number they received // whereas agents send a fresh outgoing sequence number when identity data has changed - _avatarEntitiesLock.withReadLock([&] { - identityStream << getSessionUUID() - << (udt::SequenceNumber::Type) _identitySequenceNumber - << _attachmentData - << _displayName - << getSessionDisplayNameForTransport() // depends on _sessionDisplayName - << (_isReplicated || setIsReplicated) - << _avatarEntityData - << _lookAtSnappingEnabled - ; - }); + identityStream << getSessionUUID() + << (udt::SequenceNumber::Type) _identitySequenceNumber + << _attachmentData + << _displayName + << getSessionDisplayNameForTransport() // depends on _sessionDisplayName + << (_isReplicated || setIsReplicated) + << _lookAtSnappingEnabled; return identityData; } @@ -1899,7 +1936,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); if (_clientTraitsHandler) { - _clientTraitsHandler->markTraitChanged(AvatarTraits::SkeletonModelURL); + _clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonModelURL); } emit skeletonModelURLChanged(); @@ -2095,7 +2132,6 @@ void AvatarData::sendIdentityPacket() { nodeList->sendPacketList(std::move(packetList), *node); }); - _avatarEntityDataLocallyEdited = false; _identityDataChanged = false; } @@ -2650,23 +2686,37 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent if (itr == _avatarEntityData.end()) { if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) { _avatarEntityData.insert(entityID, entityData); - _avatarEntityDataLocallyEdited = true; - markIdentityDataChanged(); } } else { itr.value() = entityData; - _avatarEntityDataLocallyEdited = true; - markIdentityDataChanged(); } }); + + if (_clientTraitsHandler) { + // we have a client traits handler, so we need to mark this instanced trait as changed + // so that changes will be sent next frame + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); + } } void AvatarData::clearAvatarEntity(const QUuid& entityID) { _avatarEntitiesLock.withWriteLock([&] { _avatarEntityData.remove(entityID); - _avatarEntityDataLocallyEdited = true; - markIdentityDataChanged(); }); + + if (_clientTraitsHandler) { + // we have a client traits handler, so we need to mark this removed instance trait as changed + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + } +} + +void AvatarData::removeAvatarEntityAndDetach(const QUuid &entityID) { + _avatarEntitiesLock.withWriteLock([this, &entityID]{ + _avatarEntityData.remove(entityID); + }); + + insertDetachedEntityID(entityID); } AvatarEntityMap AvatarData::getAvatarEntityData() const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a5d2d0749b..97ae90f694 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -947,12 +947,10 @@ public: const HeadData* getHeadData() const { return _headData; } struct Identity { - QUrl skeletonModelURL; QVector attachmentData; QString displayName; QString sessionDisplayName; bool isReplicated; - AvatarEntityMap avatarEntityData; bool lookAtSnappingEnabled; }; @@ -960,12 +958,21 @@ public: // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); - void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion = -1); + void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, + AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + void packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); + void processTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + void processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); QByteArray identityByteArray(bool setIsReplicated = false) const; + QUrl getWireSafeSkeletonModelURL() const; const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } + const QString& getDisplayName() const { return _displayName; } const QString& getSessionDisplayName() const { return _sessionDisplayName; } bool getLookAtSnappingEnabled() const { return _lookAtSnappingEnabled; } @@ -1311,6 +1318,8 @@ protected: virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer + void removeAvatarEntityAndDetach(const QUuid& entityID); + // Body scale float _targetScale; float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; @@ -1415,7 +1424,6 @@ protected: mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar AvatarEntityMap _avatarEntityData; - bool _avatarEntityDataLocallyEdited { false }; bool _avatarEntityDataChanged { false }; // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 407e88e27c..529614b20d 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -194,26 +194,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer } } -bool AvatarHashMap::checkLastProcessedTraitVersion(QUuid avatarID, - AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion) { - auto it = _processedSimpleTraitVersions.find(avatarID); - if (it == _processedSimpleTraitVersions.end()) { - auto pair = _processedSimpleTraitVersions.insert({ - avatarID, - { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } - }); - - it = pair.first; - }; - - if (it->second[traitType] < newVersion) { - it->second[traitType] = newVersion; - return true; - } else { - return false; - } -} - void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for @@ -233,24 +213,55 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); + // grab the last trait versions for this avatar + auto& lastProcessedVersions = _processedTraitVersions[avatarID]; + while (traitType != AvatarTraits::NullTrait) { - AvatarTraits::TraitVersion traitVersion; - message->readPrimitive(&traitVersion); + AvatarTraits::TraitVersion packetTraitVersion; + message->readPrimitive(&packetTraitVersion); AvatarTraits::TraitWireSize traitBinarySize; - message->readPrimitive(&traitBinarySize); + bool skipBinaryTrait = false; - if (avatar) { - // check if this trait version is newer than what we already have for this avatar - bool traitIsNewer = checkLastProcessedTraitVersion(avatarID, traitType, traitVersion); - if (traitIsNewer) { - avatar->processTrait(traitType, message->readWithoutCopy(traitBinarySize)); - } else { - message->seek(message->getPosition() + traitBinarySize); + if (!avatar) { + skipBinaryTrait = true; + } + + if (AvatarTraits::isSimpleTrait(traitType)) { + message->readPrimitive(&traitBinarySize); + + if (avatar) { + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + avatar->processTrait(traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } else { + skipBinaryTrait = true; + } } } else { - // though we have no avatar pointer, we still hop through the packet in case there are - // traits for avatars we do have later in the packet + AvatarTraits::TraitInstanceID traitInstanceID = + QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + message->readPrimitive(&traitBinarySize); + + if (avatar) { + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); + } else { + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); + } + processedInstanceVersion = packetTraitVersion; + } else { + skipBinaryTrait = true; + } + } + } + + if (skipBinaryTrait) { + // we didn't read this trait because it was older or because we didn't have an avatar to process it for message->seek(message->getPosition() + traitBinarySize); } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index ed8440eb89..ba16fa9568 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -30,7 +30,7 @@ #include "ScriptAvatarData.h" #include "AvatarData.h" -#include "AvatarTraits.h" +#include "AssociatedTraitValues.h" /**jsdoc * Note: An AvatarList API is also provided for Interface and client entity scripts: it is a @@ -155,10 +155,7 @@ protected: virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason); virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); - - bool checkLastProcessedTraitVersion(QUuid avatarID, - AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion); - + AvatarHash _avatarHash; struct PendingAvatar { std::chrono::steady_clock::time_point creationTime; @@ -169,7 +166,7 @@ protected: AvatarPendingHash _pendingAvatars; mutable QReadWriteLock _hashLock; - std::unordered_map _processedSimpleTraitVersions; + std::unordered_map _processedTraitVersions; private: QUuid _lastOwnerSessionUUID; }; diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index d30da0e1af..acac215799 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -16,46 +16,43 @@ #include #include +#include + namespace AvatarTraits { enum TraitType : int8_t { NullTrait = -1, SkeletonModelURL, + AvatarEntity, TotalTraitTypes }; - class TraitTypeSet { - public: - TraitTypeSet() {}; - - TraitTypeSet(std::initializer_list types) { - for (auto type : types) { - _types[type] = true; - } - }; + using TraitInstanceID = QUuid; - bool contains(TraitType type) const { return _types[type]; } + inline bool isSimpleTrait(TraitType traitType) { + return traitType == SkeletonModelURL; + } - bool hasAny() const { return std::find(_types.begin(), _types.end(), true) != _types.end(); } - int size() const { return std::count(_types.begin(), _types.end(), true); } - - void insert(TraitType type) { _types[type] = true; } - void erase(TraitType type) { _types[type] = false; } - void clear() { std::fill(_types.begin(), _types.end(), false); } - private: - std::vector _types = { AvatarTraits::TotalTraitTypes, false }; - }; - - const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL }; - - using TraitVersion = uint32_t; + using TraitVersion = int32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; + const TraitVersion NULL_TRAIT_VERSION = -1; - using NullableTraitVersion = int64_t; - const NullableTraitVersion NULL_TRAIT_VERSION = -1; + using TraitWireSize = int16_t; + const TraitWireSize DELETED_TRAIT_SIZE = -1; - using TraitWireSize = uint16_t; + inline void packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination, + TraitVersion traitVersion = NULL_TRAIT_VERSION) { + destination.writePrimitive(traitType); - using SimpleTraitVersions = std::vector; + if (traitVersion > DEFAULT_TRAIT_VERSION) { + AvatarTraits::TraitVersion typedVersion = traitVersion; + destination.writePrimitive(typedVersion); + } + + destination.write(instanceID.toRfc4122()); + + destination.writePrimitive(DELETED_TRAIT_SIZE); + + } }; #endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 8b3ded1e1c..cf67304937 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -36,11 +36,11 @@ void ClientTraitsHandler::resetForNewMixer() { _currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION; // mark that all traits should be sent next time - _performInitialSend = true; + _shouldPerformInitialSend = true; } void ClientTraitsHandler::sendChangedTraitsToMixer() { - if (hasChangedTraits() || _performInitialSend) { + if (hasChangedTraits() || _shouldPerformInitialSend) { // we have at least one changed trait to send auto nodeList = DependencyManager::get(); @@ -51,31 +51,57 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { } // we have a mixer to send to, setup our set traits packet + auto traitsPacketList = NLPacketList::create(PacketType::SetAvatarTraits, QByteArray(), true, true); // bump and write the current trait version to an extended header // the trait version is the same for all traits in this packet list - ++_currentTraitVersion; - QByteArray extendedHeader(reinterpret_cast(&_currentTraitVersion), sizeof(_currentTraitVersion)); - - auto traitsPacketList = NLPacketList::create(PacketType::SetAvatarTraits, extendedHeader, true); + traitsPacketList->writePrimitive(++_currentTraitVersion); // take a copy of the set of changed traits and clear the stored set - auto changedTraitsCopy { _changedTraits }; - _changedTraits.clear(); + auto traitStatusesCopy { _traitStatuses }; + _traitStatuses.reset(); + _hasChangedTraits = false; - if (_performInitialSend || changedTraitsCopy.contains(AvatarTraits::SkeletonModelURL)) { - traitsPacketList->startSegment(); - _owningAvatar->packTrait(AvatarTraits::SkeletonModelURL, *traitsPacketList); - traitsPacketList->endSegment(); + auto simpleIt = traitStatusesCopy.simpleCBegin(); + while (simpleIt != traitStatusesCopy.simpleCEnd()) { + // because the vector contains all trait types (for access using trait type as index) + // we double check that it is a simple iterator here + auto traitType = static_cast(std::distance(traitStatusesCopy.simpleCBegin(), simpleIt)); - // keep track of our skeleton version in case we get an override back - _currentSkeletonVersion = _currentTraitVersion; + if (AvatarTraits::isSimpleTrait(traitType)) { + if (_shouldPerformInitialSend || *simpleIt == Updated) { + if (traitType == AvatarTraits::SkeletonModelURL) { + _owningAvatar->packTrait(traitType, *traitsPacketList); + + // keep track of our skeleton version in case we get an override back + _currentSkeletonVersion = _currentTraitVersion; + } + } + } + + ++simpleIt; + } + + auto instancedIt = traitStatusesCopy.instancedCBegin(); + while (instancedIt != traitStatusesCopy.instancedCEnd()) { + for (auto& instanceIDValuePair : instancedIt->instances) { + if (_shouldPerformInitialSend || instanceIDValuePair.value == Updated) { + // this is a changed trait we need to send, ask the owning avatar to pack it + _owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); + } else if (instanceIDValuePair.value == Deleted) { + // pack delete for this trait instance + AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id, + *traitsPacketList); + } + } + + ++instancedIt; } nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); // if this was an initial send of all traits, consider it completed - _performInitialSend = false; + _shouldPerformInitialSend = false; } } @@ -95,13 +121,13 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m // and the version matches what we last sent for skeleton if (traitType == AvatarTraits::SkeletonModelURL && traitVersion == _currentSkeletonVersion - && !hasTraitChanged(AvatarTraits::SkeletonModelURL)) { + && _traitStatuses[AvatarTraits::SkeletonModelURL] != Updated) { // override the skeleton URL but do not mark the trait as having changed // so that we don't unecessarily sent a new trait packet to the mixer with the overriden URL auto encodedSkeletonURL = QUrl::fromEncoded(message->readWithoutCopy(traitBinarySize)); _owningAvatar->setSkeletonModelURL(encodedSkeletonURL); - _changedTraits.erase(AvatarTraits::SkeletonModelURL); + _traitStatuses.erase(AvatarTraits::SkeletonModelURL); } else { message->seek(message->getPosition() + traitBinarySize); } diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 1d4c67d0c4..27ba58d46b 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -14,7 +14,7 @@ #include -#include "AvatarTraits.h" +#include "AssociatedTraitValues.h" #include "Node.h" class AvatarData; @@ -26,10 +26,14 @@ public: void sendChangedTraitsToMixer(); - bool hasChangedTraits() { return _changedTraits.hasAny(); } - void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); } + bool hasChangedTraits() { return _hasChangedTraits; } - bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.contains(checkTrait) > 0; } + void markTraitUpdated(AvatarTraits::TraitType updatedTrait) + { _traitStatuses[updatedTrait] = Updated; _hasChangedTraits = true; } + void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID) + { _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); _hasChangedTraits = true; } + void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID) + { _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); _hasChangedTraits = true; } void resetForNewMixer(); @@ -37,14 +41,21 @@ public slots: void processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode); private: + enum ClientTraitStatus { + Unchanged, + Updated, + Deleted + }; + AvatarData* _owningAvatar; - AvatarTraits::TraitTypeSet _changedTraits; + AvatarTraits::AssociatedTraitValues _traitStatuses; AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; - AvatarTraits::NullableTraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; + AvatarTraits::TraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; - bool _performInitialSend { false }; + bool _shouldPerformInitialSend { false }; + bool _hasChangedTraits { false }; }; #endif // hifi_ClientTraitsHandler_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 94137786cd..9ed9a4f385 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::MigrateSkeletonURLToTraits); + return static_cast(AvatarMixerPacketVersion::MigrateAvatarEntitiesToTraits); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 31724ab5dc..8a2add3bb3 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -290,8 +290,9 @@ enum class AvatarMixerPacketVersion : PacketVersion { FBXReaderNodeReparenting, FixMannequinDefaultAvatarFeet, ProceduralFaceMovementFlagsAndBlendshapes, - FarGrabJoints - MigrateSkeletonURLToTraits + FarGrabJoints, + MigrateSkeletonURLToTraits, + MigrateAvatarEntitiesToTraits }; enum class DomainConnectRequestVersion : PacketVersion { From a56e9b08603e5cbacf1119a28f759d91e15ac1c2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 19:01:52 -0700 Subject: [PATCH 081/207] allow agent to create and get avatar entities from script --- assignment-client/src/Agent.cpp | 7 +++++++ libraries/avatars/src/ScriptAvatarData.cpp | 20 ++++++++++++++++++++ libraries/avatars/src/ScriptAvatarData.h | 4 ++++ 3 files changed, 31 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 049e7d0ede..a8466fa368 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -365,6 +365,8 @@ void Agent::executeScript() { // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); + scriptedAvatar->setID(getSessionUUID()); + connect(_scriptEngine.data(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(true); @@ -606,6 +608,11 @@ void Agent::setIsAvatar(bool isAvatar) { } QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); + + _entityEditSender.setMyAvatar(nullptr); + } else { + auto scriptableAvatar = DependencyManager::get(); + _entityEditSender.setMyAvatar(scriptableAvatar.data()); } } diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 8491e5368b..a716a40ad8 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -269,6 +269,26 @@ QVector ScriptAvatarData::getAttachmentData() const { // END // +#if PR_BUILD || DEV_BUILD +// +// ENTITY PROPERTIES +// START +// +AvatarEntityMap ScriptAvatarData::getAvatarEntities() const { + AvatarEntityMap scriptEntityData; + + if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { + return sharedAvatarData->getAvatarEntityData(); + } + + return scriptEntityData; +} +// +// ENTITY PROPERTIES +// END +// +#endif + // // AUDIO PROPERTIES diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 13713ff15f..91bac61728 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -116,6 +116,10 @@ public: Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QVector getAttachmentData() const; +#if DEV_BUILD || PR_BUILD + Q_INVOKABLE AvatarEntityMap getAvatarEntities() const; +#endif + // // AUDIO PROPERTIES // From f9230eca7f4f4a44eee606f6a8b927e24c4ec8c3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 22:05:15 -0700 Subject: [PATCH 082/207] don't send override avatar URL if override matches --- .../src/avatars/AvatarMixerClientData.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 34b7ec97ff..274a76d0fa 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -178,19 +178,22 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *sl } if (!inWhitelist) { - // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change - _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); + // make sure we're not unecessarily overriding the default avatar with the default avatar + if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData->skeletonReplacementURL) { + // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change + qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() + << "to replacement" << slaveSharedData->skeletonReplacementURL << "for" << sendingNode.getUUID(); + _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); - qDebug() << "Sending overwritten" << _avatar->getSkeletonModelURL() << "back to sending avatar"; + auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); - auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); - - // the returned set traits packet uses the trait version from the incoming packet - // so the client knows they should not overwrite if they have since changed the trait - _avatar->packTrait(AvatarTraits::SkeletonModelURL, *packet, traitVersion); + // the returned set traits packet uses the trait version from the incoming packet + // so the client knows they should not overwrite if they have since changed the trait + _avatar->packTrait(AvatarTraits::SkeletonModelURL, *packet, traitVersion); - auto nodeList = DependencyManager::get(); - nodeList->sendPacket(std::move(packet), sendingNode); + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(packet), sendingNode); + } } } } From de6fe43dda15804eb7e9ea8991a3850c6d589370 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 22:05:53 -0700 Subject: [PATCH 083/207] ensure joint mapping is processed for current FST url --- libraries/avatars/src/AvatarData.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 36dbe00937..4e396e2a9e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2034,6 +2034,13 @@ void AvatarData::setJointMappingsFromNetworkReply() { QNetworkReply* networkReply = static_cast(sender()); + // before we process this update, make sure that the skeleton model URL hasn't changed + // since we made the FST request + if (networkReply->url() != _skeletonModelURL) { + qCDebug(avatars) << "Refusing to set joint mappings for FST URL that does not match the current URL"; + return; + } + { QWriteLocker writeLock(&_jointDataLock); QByteArray line; From 1158ec8d50b290ab36afcc6be9faf6d87899e898 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 22:06:28 -0700 Subject: [PATCH 084/207] improve change avoidance after avatar url override --- libraries/avatars/src/ClientTraitsHandler.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index cf67304937..a31808a916 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -122,12 +122,19 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m if (traitType == AvatarTraits::SkeletonModelURL && traitVersion == _currentSkeletonVersion && _traitStatuses[AvatarTraits::SkeletonModelURL] != Updated) { + // override the skeleton URL but do not mark the trait as having changed - // so that we don't unecessarily sent a new trait packet to the mixer with the overriden URL + // so that we don't unecessarily send a new trait packet to the mixer with the overriden URL auto encodedSkeletonURL = QUrl::fromEncoded(message->readWithoutCopy(traitBinarySize)); + + auto hasChangesBefore = _hasChangedTraits; + _owningAvatar->setSkeletonModelURL(encodedSkeletonURL); + // setSkeletonModelURL will flag us for changes to the SkeletonModelURL so we reset some state here to + // avoid unnecessarily sending the overriden skeleton model URL back to the mixer _traitStatuses.erase(AvatarTraits::SkeletonModelURL); + _hasChangedTraits = hasChangesBefore; } else { message->seek(message->getPosition() + traitBinarySize); } From fc5b72e9b952c8bcd82692c1dd8d21ee6afe5c60 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 15:09:35 -0700 Subject: [PATCH 085/207] cleanup sent trait versions for removed avatars on mixer --- assignment-client/src/avatars/AvatarMixer.cpp | 3 ++- assignment-client/src/avatars/AvatarMixerClientData.cpp | 7 +++++++ assignment-client/src/avatars/AvatarMixerClientData.h | 5 +---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 228102ee53..167c1cd29c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -470,7 +470,8 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) { QMetaObject::invokeMethod(node->getLinkedData(), "cleanupKilledNode", Qt::AutoConnection, - Q_ARG(const QUuid&, QUuid(avatarNode->getUUID()))); + Q_ARG(const QUuid&, QUuid(avatarNode->getUUID())), + Q_ARG(Node::LocalID, avatarNode->getLocalID())); } ); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 274a76d0fa..cc4356fb1a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -285,3 +285,10 @@ AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherA return TraitsCheckTimestamp(); } } + +void AvatarMixerClientData::cleanupKilledNode(const QUuid& nodeUUID, Node::LocalID nodeLocalID) { + removeLastBroadcastSequenceNumber(nodeUUID); + removeLastBroadcastTime(nodeUUID); + _lastSentTraitsTimestamps.erase(nodeLocalID); + _sentTraitVersions.erase(nodeLocalID); +} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index dcbf8a6dba..02b37e08f8 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -57,10 +57,7 @@ public: void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { _lastBroadcastTimes[nodeUUID] = broadcastTime; } Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } - Q_INVOKABLE void cleanupKilledNode(const QUuid& nodeUUID) { - removeLastBroadcastSequenceNumber(nodeUUID); - removeLastBroadcastTime(nodeUUID); - } + Q_INVOKABLE void cleanupKilledNode(const QUuid& nodeUUID, Node::LocalID nodeLocalID); uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } From 0f03764c97693e026e0eed7be8ee5cdd541a8d35 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 15:10:50 -0700 Subject: [PATCH 086/207] create missing avatar when processing traits --- libraries/avatars/src/AvatarHashMap.cpp | 45 +++++++++---------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 529614b20d..0ab602e233 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -200,14 +200,8 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); // grab the avatar so we can ask it to process trait data - AvatarSharedPointer avatar; - - QReadLocker locker(&_hashLock); - auto it = _avatarHash.find(avatarID); - if (it != _avatarHash.end()) { - avatar = *it; - } - locker.unlock(); + bool isNewAvatar; + auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); // read the first trait type for this avatar AvatarTraits::TraitType traitType; @@ -223,21 +217,16 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitWireSize traitBinarySize; bool skipBinaryTrait = false; - if (!avatar) { - skipBinaryTrait = true; - } if (AvatarTraits::isSimpleTrait(traitType)) { message->readPrimitive(&traitBinarySize); - if (avatar) { - // check if this trait version is newer than what we already have for this avatar - if (packetTraitVersion > lastProcessedVersions[traitType]) { - avatar->processTrait(traitType, message->read(traitBinarySize)); - lastProcessedVersions[traitType] = packetTraitVersion; - } else { - skipBinaryTrait = true; - } + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + avatar->processTrait(traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } else { + skipBinaryTrait = true; } } else { AvatarTraits::TraitInstanceID traitInstanceID = @@ -245,18 +234,16 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess message->readPrimitive(&traitBinarySize); - if (avatar) { - auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); - if (packetTraitVersion > processedInstanceVersion) { - if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { - avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } else { - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - } - processedInstanceVersion = packetTraitVersion; + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); } else { - skipBinaryTrait = true; + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); } + processedInstanceVersion = packetTraitVersion; + } else { + skipBinaryTrait = true; } } From e33f349d5390ec7f4535d1fca9b050a6fd99bdfa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 16:06:09 -0700 Subject: [PATCH 087/207] fix flagging for avatar entity update/delete --- interface/src/Application.cpp | 18 +++----- interface/src/avatar/MyAvatar.cpp | 1 - .../src/avatars-renderer/Avatar.cpp | 20 ++++++--- libraries/avatars/src/AvatarData.cpp | 41 +++++++++++-------- libraries/avatars/src/AvatarData.h | 6 +-- .../entities/src/EntityEditPacketSender.cpp | 7 +--- .../entities/src/EntityEditPacketSender.h | 1 - .../entities/src/EntityScriptingInterface.cpp | 13 ++++-- 8 files changed, 56 insertions(+), 51 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e556fd734f..60af79bfda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1013,7 +1013,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // This is done so as not break previous command line scripts if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { - + setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath)); } else if (QFileInfo(testScriptPath).exists()) { setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); @@ -1830,14 +1830,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); - connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [](const EntityItemID& entityItemID) { - auto avatarManager = DependencyManager::get(); - auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; - if (myAvatar) { - myAvatar->clearAvatarEntity(entityItemID); - } - }); - EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) { // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); @@ -2617,7 +2609,7 @@ Application::~Application() { // Can't log to file passed this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); - + _renderEventHandler->deleteLater(); } @@ -5498,8 +5490,8 @@ void Application::update(float deltaTime) { quint64 now = usecTimestampNow(); // Check for flagged EntityData having arrived. auto entityTreeRenderer = getEntities(); - if (isServerlessMode() || - (_octreeProcessor.isLoadSequenceComplete() )) { + if (isServerlessMode() || + (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; @@ -6393,7 +6385,7 @@ void Application::nodeActivated(SharedNodePointer node) { if (_avatarOverrideUrl.isValid()) { getMyAvatar()->useFullAvatarURL(_avatarOverrideUrl); } - + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->getSkeletonModelURL()) { getMyAvatar()->resetFullAvatarURL(); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2c88f917a1..deef69d980 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1292,7 +1292,6 @@ void MyAvatar::loadData() { // HACK: manually remove empty 'avatarEntityData' else legacy data may persist in settings file settings.remove("avatarEntityData"); } - setAvatarEntityDataChanged(true); // Flying preferences must be loaded before calling setFlyingEnabled() Setting::Handle firstRunVal { Settings::firstRun, true }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0b43fd5433..150ecb6d44 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -224,14 +224,24 @@ void Avatar::setAvatarEntityDataChanged(bool value) { void Avatar::updateAvatarEntities() { PerformanceTimer perfTimer("attachments"); + + // AVATAR ENTITY UPDATE FLOW // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() - // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited - // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket - // - sendIdentityPacket sends the entity bytes to the server which relays them to other interfaces - // - AvatarHashMap::processAvatarIdentityPacket on other interfaces call avatar->setAvatarEntityData() - // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true + // - updateAvatarEntity saves the bytes and flags the trait instance for the entity as updated + // - ClientTraitsHandler::sendChangedTraitsToMixer sends the entity bytes to the mixer which relays them to other interfaces + // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processTraitInstace + // - AvatarData::processTraitInstance calls updateAvatarEntity, which sets _avatarEntityDataChanged = true // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... + // AVATAR ENTITY DELETE FLOW + // - EntityScriptingInterface::deleteEntity calls _myAvatar->clearAvatarEntity() for deleted avatar entities + // - clearAvatarEntity removes the avatar entity and flags the trait instance for the entity as deleted + // - ClientTraitsHandler::sendChangedTraitsToMixer sends a deletion to the mixer which relays to other interfaces + // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processDeletedTraitInstace + // - AvatarData::processDeletedTraitInstance calls clearAvatarEntity + // - AvatarData::clearAvatarEntity sets _avatarEntityDataChanged = true and adds the ID to the detached list + // - Avatar::simulate notices _avatarEntityDataChanged and here we are... + if (!_avatarEntityDataChanged) { return; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4e396e2a9e..f1b9986186 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1898,7 +1898,7 @@ void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType, void AvatarData::processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { if (traitType == AvatarTraits::AvatarEntity) { - removeAvatarEntityAndDetach(instanceID); + clearAvatarEntity(instanceID); } } @@ -2687,7 +2687,7 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { const int MAX_NUM_AVATAR_ENTITIES = 42; -void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate) { _avatarEntitiesLock.withWriteLock([&] { AvatarEntityMap::iterator itr = _avatarEntityData.find(entityID); if (itr == _avatarEntityData.end()) { @@ -2699,6 +2699,10 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } }); + if (requiresTreeUpdate) { + _avatarEntityDataChanged = true; + } + if (_clientTraitsHandler) { // we have a client traits handler, so we need to mark this instanced trait as changed // so that changes will be sent next frame @@ -2706,26 +2710,27 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } } -void AvatarData::clearAvatarEntity(const QUuid& entityID) { - _avatarEntitiesLock.withWriteLock([&] { - _avatarEntityData.remove(entityID); +void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) { + + bool removedEntity = false; + + _avatarEntitiesLock.withWriteLock([this, &removedEntity, &entityID] { + removedEntity = _avatarEntityData.remove(entityID); }); - if (_clientTraitsHandler) { - // we have a client traits handler, so we need to mark this removed instance trait as changed - // so that changes are sent next frame - _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + if (removedEntity) { + if (requiresRemovalFromTree) { + insertDetachedEntityID(entityID); + } + + if (_clientTraitsHandler) { + // we have a client traits handler, so we need to mark this removed instance trait as changed + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + } } } -void AvatarData::removeAvatarEntityAndDetach(const QUuid &entityID) { - _avatarEntitiesLock.withWriteLock([this, &entityID]{ - _avatarEntityData.remove(entityID); - }); - - insertDetachedEntityID(entityID); -} - AvatarEntityMap AvatarData::getAvatarEntityData() const { AvatarEntityMap result; _avatarEntitiesLock.withReadLock([&] { @@ -2738,6 +2743,8 @@ void AvatarData::insertDetachedEntityID(const QUuid entityID) { _avatarEntitiesLock.withWriteLock([&] { _avatarEntityDetached.insert(entityID); }); + + _avatarEntityDataChanged = true; } void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 97ae90f694..6f1871974d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -925,13 +925,13 @@ public: * @param {Uuid} entityID * @param {string} entityData */ - Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate = true); /**jsdoc * @function MyAvatar.clearAvatarEntity * @param {Uuid} entityID */ - Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID); + Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); /**jsdoc @@ -1318,8 +1318,6 @@ protected: virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer - void removeAvatarEntityAndDetach(const QUuid& entityID); - // Body scale float _targetScale; float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 0982775b09..d288126348 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -74,7 +74,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, jsonProperties = QJsonDocument(jsonObject); QByteArray binaryProperties = jsonProperties.toBinaryData(); - _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties, false); entity->setLastBroadcast(usecTimestampNow()); } @@ -149,11 +149,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) { - // in case this was a clientOnly entity: - if(_myAvatar) { - _myAvatar->clearAvatarEntity(entityItemID); - } - QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityErase), 0); if (EntityItemProperties::encodeEraseEntityMessage(entityItemID, bufferOut)) { diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 31f91707b8..9bf9095f7f 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -27,7 +27,6 @@ public: void setMyAvatar(AvatarData* myAvatar) { _myAvatar = myAvatar; } AvatarData* getMyAvatar() { return _myAvatar; } - void clearAvatarEntity(QUuid entityID) { assert(_myAvatar); _myAvatar->clearAvatarEntity(entityID); } /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c080fbbb88..d9924cb9fd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -574,7 +574,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { _activityTracking.deletedEntityCount++; EntityItemID entityID(id); - bool shouldDelete = true; + bool shouldSendDeleteToServer = true; // If we have a local entity tree set, then also update it. if (_entityTree) { @@ -591,16 +591,21 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { auto avatarHashMap = DependencyManager::get(); AvatarSharedPointer myAvatar = avatarHashMap->getAvatarBySessionID(myNodeID); myAvatar->insertDetachedEntityID(id); - shouldDelete = false; + shouldSendDeleteToServer = false; return; } if (entity->getLocked()) { - shouldDelete = false; + shouldSendDeleteToServer = false; } else { // only delete local entities, server entities will round trip through the server filters if (entity->getClientOnly() || _entityTree->isServerlessMode()) { + shouldSendDeleteToServer = false; _entityTree->deleteEntity(entityID); + + if (entity->getClientOnly() && getEntityPacketSender()->getMyAvatar()) { + getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entityID, false); + } } } } @@ -608,7 +613,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } // if at this point, we know the id, and we should still delete the entity, send the update to the entity server - if (shouldDelete) { + if (shouldSendDeleteToServer) { getEntityPacketSender()->queueEraseEntityMessage(entityID); } } From 6c204b682d5862d8c1bfa007e69089768e64a97c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 16:47:28 -0700 Subject: [PATCH 088/207] include trait bytes written in over budget calculation --- .../src/avatars/AvatarMixerSlave.cpp | 25 +++++++++------ .../src/avatars/AvatarMixerSlave.h | 6 ++-- libraries/avatars/src/AvatarData.cpp | 31 ++++++++++++------- libraries/avatars/src/AvatarData.h | 8 ++--- libraries/avatars/src/AvatarTraits.h | 13 +++++--- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index ebbaeb7a35..14818870d1 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -79,9 +79,9 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, } } -void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, - const AvatarMixerClientData* sendingNodeData, - NLPacketList& traitsPacketList) { +qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList) { auto otherNodeLocalID = sendingNodeData->getNodeLocalID(); @@ -90,11 +90,13 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste auto timeOfLastTraitsSent = listeningNodeData->getLastOtherAvatarTraitsSendPoint(otherNodeLocalID); auto timeOfLastTraitsChange = sendingNodeData->getLastReceivedTraitsChange(); + qint64 bytesWritten = 0; + if (timeOfLastTraitsChange > timeOfLastTraitsSent) { // there is definitely new traits data to send // add the avatar ID to mark the beginning of traits for this avatar - traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122()); + bytesWritten += traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122()); auto sendingAvatar = sendingNodeData->getAvatarSharedPointer(); @@ -115,7 +117,7 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste if (lastReceivedVersions[traitType] > lastSentVersionRef) { // there is an update to this trait, add it to the traits packet - sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); + bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); // update the last sent version lastSentVersionRef = lastReceivedVersion; @@ -152,7 +154,7 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) { // this instance version exists and has never been sent or is newer so we need to send it - sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion); + bytesWritten += sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion); if (sentInstanceIt != sentIDValuePairs.end()) { sentInstanceIt->value = receivedVersion; @@ -161,7 +163,7 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste } } else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) { // this instance version was deleted and we haven't sent the delete to this client yet - AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion); + bytesWritten += AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion); // update the last sent version for this trait instance to the absolute value of the deleted version sentInstanceIt->value = absoluteReceivedVersion; @@ -172,12 +174,14 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste } // write a null trait type to mark the end of trait data for this avatar - traitsPacketList.writePrimitive(AvatarTraits::NullTrait); + bytesWritten += traitsPacketList.writePrimitive(AvatarTraits::NullTrait); // since we send all traits for this other avatar, update the time of last traits sent // to match the time of last traits change listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange); } + + return bytesWritten; } int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) { @@ -239,6 +243,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; int identityBytesSent = 0; + int traitBytesSent = 0; // max number of avatarBytes per frame auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; @@ -550,7 +555,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); // use helper to add any changed traits to our packet list - addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + + traitsPacketList->getDataSize(); } quint64 startPacketSending = usecTimestampNow(); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 5112faae29..bcb70f8743 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -104,9 +104,9 @@ private: int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode); - void addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, - const AvatarMixerClientData* sendingNodeData, - NLPacketList& traitsPacketList); + qint64 addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList); void broadcastAvatarDataToAgent(const SharedNodePointer& node); void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f1b9986186..d63fde4cbe 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1828,35 +1828,40 @@ QUrl AvatarData::getWireSafeSkeletonModelURL() const { } } -void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, +qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { - destination.writePrimitive(traitType); + qint64 bytesWritten = 0; + bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; - destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(typedVersion); } if (traitType == AvatarTraits::SkeletonModelURL) { QByteArray encodedSkeletonURL = getWireSafeSkeletonModelURL().toEncoded(); AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); - destination.writePrimitive(encodedURLSize); + bytesWritten += destination.writePrimitive(encodedURLSize); - destination.write(encodedSkeletonURL); + bytesWritten += destination.write(encodedSkeletonURL); } + + return bytesWritten; } -void AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, +qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { - destination.writePrimitive(traitType); + qint64 bytesWritten = 0; + + bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; - destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(typedVersion); } - destination.write(traitInstanceID.toRfc4122()); + bytesWritten += destination.write(traitInstanceID.toRfc4122()); if (traitType == AvatarTraits::AvatarEntity) { // grab a read lock on the avatar entities and check for entity data for the given ID @@ -1873,12 +1878,14 @@ void AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTrai qDebug() << QJsonDocument::fromBinaryData(entityBinaryData).toJson(); - destination.writePrimitive(entityBinarySize); - destination.write(entityBinaryData); + bytesWritten += destination.writePrimitive(entityBinarySize); + bytesWritten += destination.write(entityBinaryData); } else { - destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); + bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); } } + + return bytesWritten; } void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 6f1871974d..0f5e1d9ee4 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -958,10 +958,10 @@ public: // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); - void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, - AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); - void packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, - ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + qint64 packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, + AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + qint64 packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); void processTraitInstance(AvatarTraits::TraitType traitType, diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index acac215799..16e897319b 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -39,19 +39,22 @@ namespace AvatarTraits { using TraitWireSize = int16_t; const TraitWireSize DELETED_TRAIT_SIZE = -1; - inline void packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination, + inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination, TraitVersion traitVersion = NULL_TRAIT_VERSION) { - destination.writePrimitive(traitType); + qint64 bytesWritten = 0; + + bytesWritten += destination.writePrimitive(traitType); if (traitVersion > DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; - destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(typedVersion); } - destination.write(instanceID.toRfc4122()); + bytesWritten += destination.write(instanceID.toRfc4122()); - destination.writePrimitive(DELETED_TRAIT_SIZE); + bytesWritten += destination.writePrimitive(DELETED_TRAIT_SIZE); + return bytesWritten; } }; From f0ba61ff0522b8aca88b3a9fc5014e4a219dbdea Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 12:29:28 -0700 Subject: [PATCH 089/207] add missing local ID, reset client processed trait versions --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 1 - libraries/avatars/src/AvatarHashMap.cpp | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index cc4356fb1a..5c84e3f755 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -19,7 +19,7 @@ #include "AvatarMixerSlave.h" AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID) + NodeData(nodeID, nodeLocalID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8569aaf05a..0d180bc40d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -14,7 +14,6 @@ #include #include -#include #include "AvatarLogging.h" diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 0ab602e233..64b26131be 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -279,6 +279,9 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { + // remove any information about processed traits for this avatar + _processedTraitVersions.erase(removedAvatar->getID()); + qCDebug(avatars) << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) << "from AvatarHashMap" << removalReason; emit avatarRemovedEvent(removedAvatar->getSessionUUID()); From cd05d9335a8ea0d433e9ef0fdc3c365295fb25fc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 13:29:06 -0700 Subject: [PATCH 090/207] remove debug of entity json document --- libraries/avatars/src/AvatarData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d63fde4cbe..8677d31cb5 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1876,8 +1876,6 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr if (!entityBinaryData.isNull()) { AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size(); - qDebug() << QJsonDocument::fromBinaryData(entityBinaryData).toJson(); - bytesWritten += destination.writePrimitive(entityBinarySize); bytesWritten += destination.write(entityBinaryData); } else { From 7e127749f789ce882bc76455f38688ef85071a70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 14:27:11 -0700 Subject: [PATCH 091/207] move AssignmentDynamicFactory to entity-server only --- assignment-client/src/Agent.cpp | 4 ---- assignment-client/src/entities/EntityServer.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index a8466fa368..06a14927d3 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -52,7 +52,6 @@ #include #include // TODO: consider moving to scriptengine.h -#include "AssignmentDynamicFactory.h" #include "entities/AssignmentParentFinder.h" #include "RecordingScriptingInterface.h" #include "AbstractAudioInterface.h" @@ -72,9 +71,6 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(false); - DependencyManager::registerInheritance(); - DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 868e570e0c..089fb3e52f 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -24,6 +24,7 @@ #include #include +#include "../AssignmentDynamicFactory.h" #include "AssignmentParentFinder.h" #include "EntityNodeData.h" #include "EntityServerConsts.h" @@ -42,6 +43,9 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::registerInheritance(); + DependencyManager::set(); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityClone, @@ -71,6 +75,8 @@ EntityServer::~EntityServer() { void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); + DependencyManager::destroy(); + OctreeServer::aboutToFinish(); } From eda84d281600a856f5b0ce5f75d3aea4253512a1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 14:29:42 -0700 Subject: [PATCH 092/207] flag deleted avatar entities when replacing full map --- libraries/avatars/src/AvatarData.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8677d31cb5..5094327c9a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2729,7 +2729,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFr } if (_clientTraitsHandler) { - // we have a client traits handler, so we need to mark this removed instance trait as changed + // we have a client traits handler, so we need to mark this removed instance trait as deleted // so that changes are sent next frame _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); } @@ -2769,6 +2769,12 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { foreach (auto entityID, previousAvatarEntityIDs) { if (!_avatarEntityData.contains(entityID)) { _avatarEntityDetached.insert(entityID); + + if (_clientTraitsHandler) { + // we have a client traits handler, so we flag this removed entity as deleted + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + } } } } From 25876bca6397ed1b9036ef5a444beee08e78c02f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 14:55:07 -0700 Subject: [PATCH 093/207] send attachment clears through setAvatarEntityData --- interface/src/AvatarBookmarks.cpp | 15 ++------------- interface/src/avatar/MyAvatar.cpp | 19 +++---------------- interface/src/avatar/MyAvatar.h | 2 +- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 6afab71c90..c7c87aee3e 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -151,13 +151,7 @@ bool isWearableEntity(const EntityItemPointer& entity) { void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); - + myAvatar->clearAvatarEntities(); addAvatarEntities(avatarEntities); } @@ -180,12 +174,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { if (!bookmark.empty()) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); + myAvatar->clearAvatarEntities(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index deef69d980..709ddfd17a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,21 +1703,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } -void MyAvatar::removeAvatarEntities(const std::function& condition) { - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (entityTree) { - entityTree->withWriteLock([&] { - AvatarEntityMap avatarEntities = getAvatarEntityData(); - for (auto entityID : avatarEntities.keys()) { - if (!condition || condition(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } - }); - } -} - QVariantList MyAvatar::getAvatarEntitiesVariant() { QVariantList avatarEntitiesData; QScriptEngine scriptEngine; @@ -2114,7 +2099,9 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) attachmentDataToEntityProperties(data, properties); newEntitiesProperties.push_back(properties); } - removeAvatarEntities(); + + clearAvatarEntities(); + for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ba6348cc22..247a0c79c8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,7 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void removeAvatarEntities(const std::function& condition = {}); + void clearAvatarEntities() { setAvatarEntityData(AvatarEntityMap()); } /**jsdoc * @function MyAvatar.isFlying From 99f532a20e0d7119dfa2a0a68b103cf73eb088a1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 16:40:20 -0700 Subject: [PATCH 094/207] always check added avatar entities once in updateAvatarEntities --- libraries/avatars/src/AvatarData.cpp | 6 ++---- libraries/avatars/src/AvatarData.h | 2 +- libraries/entities/src/EntityEditPacketSender.cpp | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5094327c9a..c7cc32ee3e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2692,7 +2692,7 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { const int MAX_NUM_AVATAR_ENTITIES = 42; -void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate) { +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { _avatarEntitiesLock.withWriteLock([&] { AvatarEntityMap::iterator itr = _avatarEntityData.find(entityID); if (itr == _avatarEntityData.end()) { @@ -2704,9 +2704,7 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } }); - if (requiresTreeUpdate) { - _avatarEntityDataChanged = true; - } + _avatarEntityDataChanged = true; if (_clientTraitsHandler) { // we have a client traits handler, so we need to mark this instanced trait as changed diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0f5e1d9ee4..53a2a69119 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -925,7 +925,7 @@ public: * @param {Uuid} entityID * @param {string} entityData */ - Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate = true); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); /**jsdoc * @function MyAvatar.clearAvatarEntity diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index d288126348..4aa66db227 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -74,7 +74,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, jsonProperties = QJsonDocument(jsonObject); QByteArray binaryProperties = jsonProperties.toBinaryData(); - _myAvatar->updateAvatarEntity(entityItemID, binaryProperties, false); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); entity->setLastBroadcast(usecTimestampNow()); } From 4875738a05242ef57f9b3743b1d06d6ef6e09a49 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 17:06:13 -0700 Subject: [PATCH 095/207] always add to detached, flag update on full replace --- .../src/avatars-renderer/Avatar.cpp | 8 +++---- libraries/avatars/src/AvatarData.cpp | 22 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 150ecb6d44..1ec5ff595a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -355,21 +355,21 @@ void Avatar::updateAvatarEntities() { stateItr.value().success = success; } - AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); - if (!recentlyDettachedAvatarEntities.empty()) { + AvatarEntityIDs recentlyDetachedAvatarEntities = getAndClearRecentlyDetachedIDs(); + if (!recentlyDetachedAvatarEntities.empty()) { // only lock this thread when absolutely necessary AvatarEntityMap avatarEntityData; _avatarEntitiesLock.withReadLock([&] { avatarEntityData = _avatarEntityData; }); - foreach (auto entityID, recentlyDettachedAvatarEntities) { + foreach (auto entityID, recentlyDetachedAvatarEntities) { if (!avatarEntityData.contains(entityID)) { entityTree->deleteEntity(entityID, true, true); } } // remove stale data hashes - foreach (auto entityID, recentlyDettachedAvatarEntities) { + foreach (auto entityID, recentlyDetachedAvatarEntities) { MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID); if (stateItr != _avatarEntityDataHashes.end()) { _avatarEntityDataHashes.erase(stateItr); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c7cc32ee3e..f32f883a7e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2721,16 +2721,12 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFr removedEntity = _avatarEntityData.remove(entityID); }); - if (removedEntity) { - if (requiresRemovalFromTree) { - insertDetachedEntityID(entityID); - } + insertDetachedEntityID(entityID); - if (_clientTraitsHandler) { - // we have a client traits handler, so we need to mark this removed instance trait as deleted - // so that changes are sent next frame - _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); - } + if (removedEntity && _clientTraitsHandler) { + // we have a client traits handler, so we need to mark this removed instance trait as deleted + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); } } @@ -2775,6 +2771,14 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { } } } + + if (_clientTraitsHandler) { + // if we have a client traits handler, flag any updated or created entities + // so that we send changes for them next frame + foreach (auto entityID, _avatarEntityData) { + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); + } + } } }); } From 3e2d4dc69618468395a4a80241353956813aafa5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 17:47:32 -0700 Subject: [PATCH 096/207] fixes and improvments addressing CR comments --- .../src/avatars/AvatarMixerClientData.cpp | 15 +++++++------- .../src/avatars/AvatarMixerClientData.h | 6 +++--- .../src/avatars/AvatarMixerSlave.cpp | 20 ++++++++----------- interface/src/Application.cpp | 5 +---- libraries/avatars/src/AssociatedTraitValues.h | 2 +- libraries/avatars/src/AvatarData.cpp | 6 ++---- libraries/avatars/src/AvatarTraits.h | 8 ++++---- libraries/avatars/src/ClientTraitsHandler.cpp | 12 +++++------ libraries/networking/src/NodeData.cpp | 2 -- libraries/networking/src/NodeData.h | 2 +- libraries/networking/src/udt/BasePacket.cpp | 6 ------ libraries/networking/src/udt/BasePacket.h | 2 +- 12 files changed, 34 insertions(+), 52 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 5c84e3f755..b2490fc7b4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -49,7 +49,7 @@ void AvatarMixerClientData::queuePacket(QSharedPointer message, _packetQueue.push(message); } -int AvatarMixerClientData::processPackets(SlaveSharedData* slaveSharedData) { +int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData) { int packetsProcessed = 0; SharedNodePointer node = _packetQueue.node; assert(_packetQueue.empty() || node); @@ -93,7 +93,8 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead())); } -void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode) { +void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, + const SlaveSharedData& slaveSharedData, Node& sendingNode) { // pull the trait version from the message AvatarTraits::TraitVersion packetTraitVersion; message.readPrimitive(&packetTraitVersion); @@ -155,9 +156,9 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, Sl } } -void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *slaveSharedData, Node& sendingNode, +void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData &slaveSharedData, Node& sendingNode, AvatarTraits::TraitVersion traitVersion) { - const auto& whitelist = slaveSharedData->skeletonURLWhitelist; + const auto& whitelist = slaveSharedData.skeletonURLWhitelist; if (!whitelist.isEmpty()) { bool inWhitelist = false; @@ -179,11 +180,11 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *sl if (!inWhitelist) { // make sure we're not unecessarily overriding the default avatar with the default avatar - if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData->skeletonReplacementURL) { + if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) { // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() - << "to replacement" << slaveSharedData->skeletonReplacementURL << "for" << sendingNode.getUUID(); - _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); + << "to replacement" << slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID(); + _avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL); auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 02b37e08f8..1c2694af48 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -116,10 +116,10 @@ public: QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } void queuePacket(QSharedPointer message, SharedNodePointer node); - int processPackets(SlaveSharedData* slaveSharedData); // returns number of packets processed + int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed - void processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode); - void checkSkeletonURLAgainstWhitelist(SlaveSharedData* slaveSharedData, Node& sendingNode, + void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode); + void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode, AvatarTraits::TraitVersion traitVersion); using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 14818870d1..f347ff1f10 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -59,7 +59,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { auto nodeData = dynamic_cast(node->getLinkedData()); if (nodeData) { _stats.nodesProcessed++; - _stats.packetsProcessed += nodeData->processPackets(_sharedData); + _stats.packetsProcessed += nodeData->processPackets(*_sharedData); } auto end = usecTimestampNow(); _stats.processIncomingPacketsElapsedTime += (end - start); @@ -109,19 +109,15 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis auto traitType = static_cast(std::distance(lastReceivedVersions.simpleCBegin(), simpleReceivedIt)); - // we need to double check that this is actually a simple trait type, since the instanced - // trait types are in the simple vector for access efficiency - if (AvatarTraits::isSimpleTrait(traitType)) { - auto lastReceivedVersion = *simpleReceivedIt; - auto& lastSentVersionRef = lastSentVersions[traitType]; + auto lastReceivedVersion = *simpleReceivedIt; + auto& lastSentVersionRef = lastSentVersions[traitType]; - if (lastReceivedVersions[traitType] > lastSentVersionRef) { - // there is an update to this trait, add it to the traits packet - bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); + if (lastReceivedVersions[traitType] > lastSentVersionRef) { + // there is an update to this trait, add it to the traits packet + bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); - // update the last sent version - lastSentVersionRef = lastReceivedVersion; - } + // update the last sent version + lastSentVersionRef = lastReceivedVersion; } ++simpleReceivedIt; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 60af79bfda..2d76791a5e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5488,10 +5488,7 @@ void Application::update(float deltaTime) { // we haven't yet enabled physics. we wait until we think we have all the collision information // for nearby entities before starting bullet up. quint64 now = usecTimestampNow(); - // Check for flagged EntityData having arrived. - auto entityTreeRenderer = getEntities(); - if (isServerlessMode() || - (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) { + if (isServerlessMode() || _octreeProcessor.isLoadSequenceComplete()) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h index b2c0197e5c..e8bbce891d 100644 --- a/libraries/avatars/src/AssociatedTraitValues.h +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -18,7 +18,7 @@ namespace AvatarTraits { template class AssociatedTraitValues { public: - AssociatedTraitValues() : _simpleTypes(TotalTraitTypes, defaultValue) {} + AssociatedTraitValues() : _simpleTypes(FirstInstancedTrait, defaultValue) {} void insert(TraitType type, T value) { _simpleTypes[type] = value; } void erase(TraitType type) { _simpleTypes[type] = defaultValue; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f32f883a7e..c1ec19b307 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1834,8 +1834,7 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { - AvatarTraits::TraitVersion typedVersion = traitVersion; - bytesWritten += destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(traitVersion); } if (traitType == AvatarTraits::SkeletonModelURL) { @@ -1857,8 +1856,7 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { - AvatarTraits::TraitVersion typedVersion = traitVersion; - bytesWritten += destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(traitVersion); } bytesWritten += destination.write(traitInstanceID.toRfc4122()); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 16e897319b..f0c807a432 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -22,14 +22,15 @@ namespace AvatarTraits { enum TraitType : int8_t { NullTrait = -1, SkeletonModelURL, - AvatarEntity, + FirstInstancedTrait, + AvatarEntity = FirstInstancedTrait, TotalTraitTypes }; using TraitInstanceID = QUuid; inline bool isSimpleTrait(TraitType traitType) { - return traitType == SkeletonModelURL; + return traitType > NullTrait && traitType < FirstInstancedTrait; } using TraitVersion = int32_t; @@ -46,8 +47,7 @@ namespace AvatarTraits { bytesWritten += destination.writePrimitive(traitType); if (traitVersion > DEFAULT_TRAIT_VERSION) { - AvatarTraits::TraitVersion typedVersion = traitVersion; - bytesWritten += destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(traitVersion); } bytesWritten += destination.write(instanceID.toRfc4122()); diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index a31808a916..151b2a3809 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -68,14 +68,12 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { // we double check that it is a simple iterator here auto traitType = static_cast(std::distance(traitStatusesCopy.simpleCBegin(), simpleIt)); - if (AvatarTraits::isSimpleTrait(traitType)) { - if (_shouldPerformInitialSend || *simpleIt == Updated) { - if (traitType == AvatarTraits::SkeletonModelURL) { - _owningAvatar->packTrait(traitType, *traitsPacketList); + if (_shouldPerformInitialSend || *simpleIt == Updated) { + if (traitType == AvatarTraits::SkeletonModelURL) { + _owningAvatar->packTrait(traitType, *traitsPacketList); - // keep track of our skeleton version in case we get an override back - _currentSkeletonVersion = _currentTraitVersion; - } + // keep track of our skeleton version in case we get an override back + _currentSkeletonVersion = _currentTraitVersion; } } diff --git a/libraries/networking/src/NodeData.cpp b/libraries/networking/src/NodeData.cpp index dd926852cd..d22b970154 100644 --- a/libraries/networking/src/NodeData.cpp +++ b/libraries/networking/src/NodeData.cpp @@ -18,5 +18,3 @@ NodeData::NodeData(const QUuid& nodeID, NetworkPeer::LocalID nodeLocalID) : { } - -NodeData::~NodeData() {} diff --git a/libraries/networking/src/NodeData.h b/libraries/networking/src/NodeData.h index ffbc0c2376..b4cb87d0c2 100644 --- a/libraries/networking/src/NodeData.h +++ b/libraries/networking/src/NodeData.h @@ -26,7 +26,7 @@ class NodeData : public QObject { Q_OBJECT public: NodeData(const QUuid& nodeID = QUuid(), NetworkPeer::LocalID localID = NetworkPeer::NULL_LOCAL_ID); - virtual ~NodeData() = 0; + virtual ~NodeData() = default; virtual int parseData(ReceivedMessage& message) { return 0; } const QUuid& getNodeID() const { return _nodeID; } diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 92ccdd6117..12a174b7d3 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -77,12 +77,6 @@ BasePacket::BasePacket(std::unique_ptr data, qint64 size, const HifiSock } -BasePacket::BasePacket(const BasePacket& other) : - ExtendedIODevice() -{ - *this = other; -} - BasePacket& BasePacket::operator=(const BasePacket& other) { _packetSize = other._packetSize; _packet = std::unique_ptr(new char[_packetSize]); diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 9c3244e08b..9054131337 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -88,7 +88,7 @@ public: protected: BasePacket(qint64 size); BasePacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); - BasePacket(const BasePacket& other); + BasePacket(const BasePacket& other) { *this = other; } BasePacket& operator=(const BasePacket& other); BasePacket(BasePacket&& other); BasePacket& operator=(BasePacket&& other); From bfb9b679687ae4a0e4a5dac2e167b162d5101cd1 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 11:17:33 -0700 Subject: [PATCH 097/207] more debug statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 6 ++++++ scripts/system/menu.js | 1 + scripts/system/tablet-ui/tabletUI.js | 6 +++--- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 1922b02f93..10a9f01337 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -117,6 +117,7 @@ Item { id: pageRepeater model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { + console.log("onItemAdded: " + tabletProxy.buttons.rowCount()); item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } @@ -208,6 +209,7 @@ Item { Component.onCompleted: updateProperties() function updateProperties() { + console.log("updateProperties: " + tabletProxy.buttons.rowCount()); var keys = Object.keys(modelData.properties).forEach(function (key) { if (tabletButton[key] !== modelData.properties[key]) { tabletButton[key] = modelData.properties[key]; @@ -269,6 +271,10 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count + + Component.onCompleted: { + console.log("pageIndicator: " + pageIndicator.count); + } } } diff --git a/scripts/system/menu.js b/scripts/system/menu.js index d669d3d918..1db210216a 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -52,6 +52,7 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- if (isHMDMode) { button = tablet.addButton(buttonProperties); button.clicked.connect(onClicked); + print("Adding menu button"); } else if (button) { button.clicked.disconnect(onClicked); tablet.removeButton(button); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index f339475f72..c28040000b 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -22,7 +22,7 @@ var DEFAULT_TABLET_SCALE = 70; var preMakeTime = Date.now(); var validCheckTime = Date.now(); - var debugTablet = false; + var debugTablet = true; var tabletScalePercentage = 70.0; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; @@ -298,7 +298,7 @@ } wantsMenu = clicked; }); - + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -306,7 +306,7 @@ } wantsMenu = clicked; }); - + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 25ed166f07631f2b6afdce3b2ed625b0cdb2f771 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 11:49:48 -0700 Subject: [PATCH 098/207] fix logic for initial send of deleted trait instance --- libraries/avatars/src/ClientTraitsHandler.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 151b2a3809..c4073cb86a 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -83,10 +83,12 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto instancedIt = traitStatusesCopy.instancedCBegin(); while (instancedIt != traitStatusesCopy.instancedCEnd()) { for (auto& instanceIDValuePair : instancedIt->instances) { - if (_shouldPerformInitialSend || instanceIDValuePair.value == Updated) { - // this is a changed trait we need to send, ask the owning avatar to pack it + if ((_shouldPerformInitialSend && instanceIDValuePair.value != Deleted) + || instanceIDValuePair.value == Updated) { + // this is a changed trait we need to send or we haven't send out trait information yet + // ask the owning avatar to pack it _owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); - } else if (instanceIDValuePair.value == Deleted) { + } else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) { // pack delete for this trait instance AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); From 8c58ae4e60b3628077cd72e70568254611e95eb7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 11:56:28 -0700 Subject: [PATCH 099/207] fix insert of second trait instance --- libraries/avatars/src/AssociatedTraitValues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h index e8bbce891d..fb780e252e 100644 --- a/libraries/avatars/src/AssociatedTraitValues.h +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -123,7 +123,7 @@ namespace AvatarTraits { auto it = instancesForTrait(traitType); if (it != _instancedTypes.end()) { - auto instancesVector = it->instances; + auto& instancesVector = it->instances; auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(), [instanceID](InstanceIDValuePair& idValuePair){ return idValuePair.id == instanceID; From 8226ffb916a111bbc255bfdc6547502616a2a397 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 11:57:21 -0700 Subject: [PATCH 100/207] fix ref to instances vector for instance erase --- libraries/avatars/src/AssociatedTraitValues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h index fb780e252e..cf1edef7f7 100644 --- a/libraries/avatars/src/AssociatedTraitValues.h +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -143,7 +143,7 @@ namespace AvatarTraits { auto it = instancesForTrait(traitType); if (it != _instancedTypes.end()) { - auto instancesVector = it->instances; + auto& instancesVector = it->instances; instancesVector.erase(std::remove_if(instancesVector.begin(), instancesVector.end(), [&instanceID](InstanceIDValuePair& idValuePair){ From 5ae604f341510caba2efb639e774118eda59fc97 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 12:14:32 -0700 Subject: [PATCH 101/207] REmove the texture Transform for deferred_light slv which then makes it work correctly on AMD --- .../gpu-gl-common/src/gpu/gl/GLBackendShader.cpp | 4 +++- .../render-utils/src/DeferredLightingEffect.cpp | 14 ++++++++------ libraries/render-utils/src/deferred_light.slv | 6 +++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 88d2e8609f..4e00fb4871 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -212,6 +212,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: glprogram = ::gl::buildProgram(shaderGLObjects); if (!::gl::linkProgram(glprogram, compilationLogs[version].message)) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + compilationLogs[version].compiled = false; glDeleteProgram(glprogram); glprogram = 0; return nullptr; @@ -254,7 +256,7 @@ GLint GLBackend::getRealUniformLocation(GLint location) const { // uniforms. If someone is requesting a uniform that isn't in the remapping structure // that's a bug from the calling code, because it means that location wasn't in the // reflection - qWarning() << "Unexpected location requested for shader"; + qWarning() << "Unexpected location requested for shader: #" << location; return INVALID_UNIFORM_INDEX; } return itr->second; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 2995d17f03..fb73ee0dfc 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -135,7 +135,7 @@ static void loadLightProgram(int programId, bool lightVolume, gpu::PipelinePoint if (lightVolume) { PrepareStencil::testShape(*state); - state->setCullMode(gpu::State::CULL_BACK); + state->setCullMode(gpu::State::CULL_NONE); //state->setCullMode(gpu::State::CULL_FRONT); //state->setDepthTest(true, false, gpu::GREATER_EQUAL); //state->setDepthClampEnable(true); @@ -496,9 +496,10 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, batch.setPipeline(program); } + // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) // Adjust the texcoordTransform in the case we are rendeirng a sub region(mini mirror) - auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport); - batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); + // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport); + // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); // Setup the global lighting deferredLightingEffect->setupKeyLightBatch(args, batch); @@ -560,7 +561,8 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext batch.setViewportTransform(viewport); batch.setStateScissorRect(viewport); - auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport); + // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) + // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport); auto& lightIndices = lightClusters->_visibleLightIndices; @@ -569,14 +571,14 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext // Local light pipeline batch.setPipeline(deferredLightingEffect->_localLight); - batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); + // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); // Draw outline as well ? if (lightingModel->isShowLightContourEnabled()) { batch.setPipeline(deferredLightingEffect->_localLightOutline); - batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); + // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); } diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 654e9a69b2..7468be9655 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,7 +16,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; -layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; +//layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; void main(void) { const float depth = 1.0; @@ -30,8 +30,8 @@ void main(void) { _texCoord01.xy = (pos.xy + 1.0) * 0.5; - _texCoord01.xy *= texcoordFrameTransform.zw; - _texCoord01.xy += texcoordFrameTransform.xy; + // _texCoord01.xy *= texcoordFrameTransform.zw; + // _texCoord01.xy += texcoordFrameTransform.xy; gl_Position = pos; } From a177e49877cb97bea92106841b67d90ea26be484 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 12:18:11 -0700 Subject: [PATCH 102/207] Revert "send attachment clears through setAvatarEntityData" This reverts commit 25876bca6397ed1b9036ef5a444beee08e78c02f. --- interface/src/AvatarBookmarks.cpp | 15 +++++++++++++-- interface/src/avatar/MyAvatar.cpp | 19 ++++++++++++++++--- interface/src/avatar/MyAvatar.h | 2 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index c7c87aee3e..6afab71c90 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -151,7 +151,13 @@ bool isWearableEntity(const EntityItemPointer& entity) { void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->clearAvatarEntities(); + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + myAvatar->removeAvatarEntities([&](const QUuid& entityID) { + auto entity = entityTree->findEntityByID(entityID); + return entity && isWearableEntity(entity); + }); + addAvatarEntities(avatarEntities); } @@ -174,7 +180,12 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { if (!bookmark.empty()) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->clearAvatarEntities(); + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + myAvatar->removeAvatarEntities([&](const QUuid& entityID) { + auto entity = entityTree->findEntityByID(entityID); + return entity && isWearableEntity(entity); + }); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 709ddfd17a..deef69d980 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,6 +1703,21 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } +void MyAvatar::removeAvatarEntities(const std::function& condition) { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withWriteLock([&] { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + if (!condition || condition(entityID)) { + entityTree->deleteEntity(entityID, true, true); + } + } + }); + } +} + QVariantList MyAvatar::getAvatarEntitiesVariant() { QVariantList avatarEntitiesData; QScriptEngine scriptEngine; @@ -2099,9 +2114,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) attachmentDataToEntityProperties(data, properties); newEntitiesProperties.push_back(properties); } - - clearAvatarEntities(); - + removeAvatarEntities(); for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 247a0c79c8..ba6348cc22 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,7 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void clearAvatarEntities() { setAvatarEntityData(AvatarEntityMap()); } + void removeAvatarEntities(const std::function& condition = {}); /**jsdoc * @function MyAvatar.isFlying From 1723f3d3d88b94e4de62d62e960437220cf670b9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 12:21:29 -0700 Subject: [PATCH 103/207] don't use entity tree to clear all avatar entities --- interface/src/avatar/MyAvatar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index deef69d980..b30f05225e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2114,7 +2114,10 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) attachmentDataToEntityProperties(data, properties); newEntitiesProperties.push_back(properties); } - removeAvatarEntities(); + + // clear any existing avatar entities + setAvatarEntityData(AvatarEntityMap()); + for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); } From b7d8b92fb5e2795402401ae35eb5618873d067e7 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 12:25:27 -0700 Subject: [PATCH 104/207] Add comment in shader and fix bad cull mode --- libraries/render-utils/src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/deferred_light.slv | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index fb73ee0dfc..91d196f896 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -135,7 +135,7 @@ static void loadLightProgram(int programId, bool lightVolume, gpu::PipelinePoint if (lightVolume) { PrepareStencil::testShape(*state); - state->setCullMode(gpu::State::CULL_NONE); + state->setCullMode(gpu::State::CULL_BACK); //state->setCullMode(gpu::State::CULL_FRONT); //state->setDepthTest(true, false, gpu::GREATER_EQUAL); //state->setDepthClampEnable(true); diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 7468be9655..0bc7a0f807 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,6 +16,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; +// NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) //layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; void main(void) { From 8c5aab7fa04577836fdcbfed4cd2564b6091a574 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 12:30:31 -0700 Subject: [PATCH 105/207] tabs! --- libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 4e00fb4871..7267e29be2 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -256,7 +256,7 @@ GLint GLBackend::getRealUniformLocation(GLint location) const { // uniforms. If someone is requesting a uniform that isn't in the remapping structure // that's a bug from the calling code, because it means that location wasn't in the // reflection - qWarning() << "Unexpected location requested for shader: #" << location; + qWarning() << "Unexpected location requested for shader: #" << location; return INVALID_UNIFORM_INDEX; } return itr->second; From af5c7a7a674618f16d20f69ccf980c9c2476d63c Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 21 Aug 2018 16:35:47 -0300 Subject: [PATCH 106/207] Android - Domains List - cache in memory only on this instance (deleted if app restarts) allows for fast list display. Showing again also asks for update so in some seconds the list gets updated in case of changes --- .../hifiinterface/fragment/HomeFragment.java | 25 ++++++++---- .../hifiinterface/view/DomainAdapter.java | 38 +++++++++++++++---- 2 files changed, 47 insertions(+), 16 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java index 7bd373cf1d..86b8625cfe 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/HomeFragment.java @@ -76,18 +76,22 @@ public class HomeFragment extends Fragment { }); mDomainAdapter.setListener(new DomainAdapter.AdapterListener() { @Override - public void onEmptyAdapter() { + public void onEmptyAdapter(boolean shouldStopRefreshing) { searchNoResultsView.setText(R.string.search_no_results); searchNoResultsView.setVisibility(View.VISIBLE); mDomainsView.setVisibility(View.GONE); - mSwipeRefreshLayout.setRefreshing(false); + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override - public void onNonEmptyAdapter() { + public void onNonEmptyAdapter(boolean shouldStopRefreshing) { searchNoResultsView.setVisibility(View.GONE); mDomainsView.setVisibility(View.VISIBLE); - mSwipeRefreshLayout.setRefreshing(false); + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override @@ -96,11 +100,20 @@ public class HomeFragment extends Fragment { } }); mDomainsView.setAdapter(mDomainAdapter); + mDomainAdapter.startLoad(); mSearchView = rootView.findViewById(R.id.searchView); mSearchIconView = rootView.findViewById(R.id.search_mag_icon); mClearSearch = rootView.findViewById(R.id.search_clear); + getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); + + return rootView; + } + + @Override + public void onStart() { + super.onStart(); mSearchView.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {} @@ -142,10 +155,6 @@ public class HomeFragment extends Fragment { mDomainAdapter.loadDomains(mSearchView.getText().toString(), true); } }); - - getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); - - return rootView; } @Override diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 71d634e9ea..6860742a0d 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -12,6 +12,7 @@ import android.widget.TextView; import com.squareup.picasso.Picasso; +import java.util.Arrays; import java.util.List; import io.highfidelity.hifiinterface.R; @@ -36,19 +37,39 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { + mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mDomains.length == 0) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } + } + } + } + public void loadDomains(String filterText, boolean forceRefresh) { domainProvider.retrieve(filterText, new DomainProvider.DomainCallback() { @Override @@ -60,13 +81,16 @@ public class DomainAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 13:21:21 -0700 Subject: [PATCH 107/207] put back ExtendedIODevice ctor for ubuntu warning --- libraries/networking/src/udt/BasePacket.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 9054131337..4981cb4720 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -88,7 +88,7 @@ public: protected: BasePacket(qint64 size); BasePacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); - BasePacket(const BasePacket& other) { *this = other; } + BasePacket(const BasePacket& other) : ExtendedIODevice() { *this = other; } BasePacket& operator=(const BasePacket& other); BasePacket(BasePacket&& other); BasePacket& operator=(BasePacket&& other); From ed965babdf72afd5e07dc3c3af579b07040c7c9c Mon Sep 17 00:00:00 2001 From: Cristian Luis Duarte Date: Tue, 21 Aug 2018 17:47:40 -0300 Subject: [PATCH 108/207] Android - People List - cache in memory only on this instance (deleted if app restarts) allows for fast list display. Showing again also asks for update so in some ms the list gets updated in case of changes --- .../fragment/FriendsFragment.java | 14 +++++--- .../hifiinterface/view/UserListAdapter.java | 35 ++++++++++++++++--- 2 files changed, 40 insertions(+), 9 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java index 2a008d7950..2475c4d887 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/fragment/FriendsFragment.java @@ -98,13 +98,17 @@ public class FriendsFragment extends Fragment { mUsersAdapter.setListener(new UserListAdapter.AdapterListener() { @Override - public void onEmptyAdapter() { - mSwipeRefreshLayout.setRefreshing(false); + public void onEmptyAdapter(boolean shouldStopRefreshing) { + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override - public void onNonEmptyAdapter() { - mSwipeRefreshLayout.setRefreshing(false); + public void onNonEmptyAdapter(boolean shouldStopRefreshing) { + if (shouldStopRefreshing) { + mSwipeRefreshLayout.setRefreshing(false); + } } @Override @@ -115,6 +119,8 @@ public class FriendsFragment extends Fragment { mUsersView.setAdapter(mUsersAdapter); + mUsersAdapter.startLoad(); + mSlidingUpPanelLayout.setFadeOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java index 9f62b21250..a34de5b34e 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/UserListAdapter.java @@ -37,28 +37,53 @@ public class UserListAdapter extends RecyclerView.Adapter USERS_TMP_CACHE; + public UserListAdapter(Context c, UsersProvider usersProvider) { mContext = c; mInflater = LayoutInflater.from(mContext); mProvider = usersProvider; - loadUsers(); } public void setListener(AdapterListener adapterListener) { mAdapterListener = adapterListener; } + public void startLoad() { + useTmpCachedUsers(); + loadUsers(); + } + + private void useTmpCachedUsers() { + if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) { + mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); + mUsers.addAll(USERS_TMP_CACHE); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } + } + } + } + public void loadUsers() { mProvider.retrieve(new UsersProvider.UsersCallback() { @Override public void retrieveOk(List users) { mUsers = new ArrayList<>(users); notifyDataSetChanged(); + + USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); + USERS_TMP_CACHE.addAll(mUsers); + if (mAdapterListener != null) { if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(); + mAdapterListener.onEmptyAdapter(true); } else { - mAdapterListener.onNonEmptyAdapter(); + mAdapterListener.onNonEmptyAdapter(true); } } } @@ -240,8 +265,8 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Tue, 21 Aug 2018 18:40:10 -0300 Subject: [PATCH 109/207] Android - People and Domain list - synchronize cache and request code --- .../hifiinterface/view/DomainAdapter.java | 40 ++++++++++--------- .../hifiinterface/view/UserListAdapter.java | 37 +++++++++-------- 2 files changed, 43 insertions(+), 34 deletions(-) diff --git a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java index 6860742a0d..78251ac4a4 100644 --- a/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java +++ b/android/app/src/main/java/io/highfidelity/hifiinterface/view/DomainAdapter.java @@ -57,14 +57,16 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { - mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); - notifyDataSetChanged(); - if (mAdapterListener != null) { - if (mDomains.length == 0) { - mAdapterListener.onEmptyAdapter(false); - } else { - mAdapterListener.onNonEmptyAdapter(false); + synchronized (this) { + if (DOMAINS_TMP_CACHE != null && DOMAINS_TMP_CACHE.length > 0) { + mDomains = Arrays.copyOf(DOMAINS_TMP_CACHE, DOMAINS_TMP_CACHE.length); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mDomains.length == 0) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } } } } @@ -81,16 +83,18 @@ public class DomainAdapter extends RecyclerView.Adapter 0) { - mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); - mUsers.addAll(USERS_TMP_CACHE); - notifyDataSetChanged(); - if (mAdapterListener != null) { - if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(false); - } else { - mAdapterListener.onNonEmptyAdapter(false); + synchronized (this) { + if (USERS_TMP_CACHE != null && USERS_TMP_CACHE.size() > 0) { + mUsers = new ArrayList<>(USERS_TMP_CACHE.size()); + mUsers.addAll(USERS_TMP_CACHE); + notifyDataSetChanged(); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(false); + } else { + mAdapterListener.onNonEmptyAdapter(false); + } } } } @@ -76,14 +78,16 @@ public class UserListAdapter extends RecyclerView.Adapter(users); notifyDataSetChanged(); - USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); - USERS_TMP_CACHE.addAll(mUsers); + synchronized (this) { + USERS_TMP_CACHE = new ArrayList<>(mUsers.size()); + USERS_TMP_CACHE.addAll(mUsers); - if (mAdapterListener != null) { - if (mUsers.isEmpty()) { - mAdapterListener.onEmptyAdapter(true); - } else { - mAdapterListener.onNonEmptyAdapter(true); + if (mAdapterListener != null) { + if (mUsers.isEmpty()) { + mAdapterListener.onEmptyAdapter(true); + } else { + mAdapterListener.onNonEmptyAdapter(true); + } } } } @@ -269,4 +273,5 @@ public class UserListAdapter extends RecyclerView.Adapter Date: Wed, 22 Aug 2018 00:42:01 +0300 Subject: [PATCH 110/207] bulk adjusting to coding guidelines --- interface/resources/qml/hifi/AvatarApp.qml | 54 +++++++------- .../qml/hifi/avatarapp/AdjustWearables.qml | 50 ++++++------- .../qml/hifi/avatarapp/AvatarsModel.qml | 74 ++++++++++--------- .../hifi/avatarapp/CreateFavoriteDialog.qml | 8 +- .../qml/hifi/avatarapp/DialogButtons.qml | 2 +- .../qml/hifi/avatarapp/MessageBox.qml | 6 +- .../qml/hifi/avatarapp/MessageBoxes.qml | 27 ++++--- .../resources/qml/hifi/avatarapp/Settings.qml | 6 +- .../qml/hifi/avatarapp/TransparencyMask.qml | 2 +- 9 files changed, 123 insertions(+), 106 deletions(-) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index 217525498d..d8eae311d8 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -57,7 +57,7 @@ Rectangle { try { var marketResponse = JSON.parse(xmlhttp.responseText.trim()) - if(marketResponse.status === 'success') { + if (marketResponse.status === 'success') { avatar.modelName = marketResponse.data.title; } } @@ -72,14 +72,14 @@ Rectangle { function getAvatarModelName() { - if(currentAvatar === null) { + if (currentAvatar === null) { return ''; } - if(currentAvatar.modelName !== undefined) { + if (currentAvatar.modelName !== undefined) { return currentAvatar.modelName; } else { var marketId = allAvatars.extractMarketId(currentAvatar.avatarUrl); - if(marketId !== '') { + if (marketId !== '') { fetchAvatarModelName(marketId, currentAvatar); } } @@ -103,51 +103,51 @@ Rectangle { property url externalAvatarThumbnailUrl: '../../images/avatarapp/guy-in-circle.svg' function fromScript(message) { - if(message.method === 'initialize') { + if (message.method === 'initialize') { jointNames = message.data.jointNames; emitSendToScript({'method' : getAvatarsMethod}); - } else if(message.method === 'wearableUpdated') { + } else if (message.method === 'wearableUpdated') { adjustWearables.refreshWearable(message.entityID, message.wearableIndex, message.properties, message.updateUI); - } else if(message.method === 'wearablesUpdated') { + } else if (message.method === 'wearablesUpdated') { var wearablesModel = currentAvatar.wearables; wearablesModel.clear(); message.wearables.forEach(function(wearable) { wearablesModel.append(wearable); }); adjustWearables.refresh(currentAvatar); - } else if(message.method === 'scaleChanged') { + } else if (message.method === 'scaleChanged') { currentAvatar.avatarScale = message.value; updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'externalAvatarApplied') { + } else if (message.method === 'externalAvatarApplied') { currentAvatar.avatarUrl = message.avatarURL; currentAvatar.thumbnailUrl = allAvatars.makeThumbnailUrl(message.avatarURL); currentAvatar.entry.avatarUrl = currentAvatar.avatarUrl; currentAvatar.modelName = undefined; updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'settingChanged') { + } else if (message.method === 'settingChanged') { currentAvatarSettings[message.name] = message.value; - } else if(message.method === 'changeSettings') { + } else if (message.method === 'changeSettings') { currentAvatarSettings = message.settings; - } else if(message.method === 'bookmarkLoaded') { + } else if (message.method === 'bookmarkLoaded') { setCurrentAvatar(message.data.currentAvatar, message.data.name); var avatarIndex = allAvatars.findAvatarIndex(currentAvatar.name); allAvatars.move(avatarIndex, 0, 1); view.setPage(0); - } else if(message.method === 'bookmarkAdded') { + } else if (message.method === 'bookmarkAdded') { var avatar = allAvatars.findAvatar(message.bookmarkName); - if(avatar !== undefined) { + if (avatar !== undefined) { var avatarObject = allAvatars.makeAvatarObject(message.bookmark, message.bookmarkName); for(var prop in avatarObject) { avatar[prop] = avatarObject[prop]; } - if(currentAvatar.name === message.bookmarkName) { + if (currentAvatar.name === message.bookmarkName) { currentAvatar = currentAvatarModel.makeAvatarEntry(avatarObject); } } else { allAvatars.addAvatarEntry(message.bookmark, message.bookmarkName); } updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'bookmarkDeleted') { + } else if (message.method === 'bookmarkDeleted') { pageOfAvatars.isUpdating = true; var index = pageOfAvatars.findAvatarIndex(message.name); @@ -159,15 +159,16 @@ Rectangle { var itemsOnPage = pageOfAvatars.count; var newItemIndex = view.currentPage * view.itemsPerPage + itemsOnPage; - if(newItemIndex <= (allAvatars.count - 1)) { + if (newItemIndex <= (allAvatars.count - 1)) { pageOfAvatars.append(allAvatars.get(newItemIndex)); } else { - if(!pageOfAvatars.hasGetAvatars()) + if (!pageOfAvatars.hasGetAvatars()) { pageOfAvatars.appendGetAvatars(); + } } pageOfAvatars.isUpdating = false; - } else if(message.method === getAvatarsMethod) { + } else if (message.method === getAvatarsMethod) { var getAvatarsData = message.data; allAvatars.populate(getAvatarsData.bookmarks); setCurrentAvatar(getAvatarsData.currentAvatar, ''); @@ -175,16 +176,16 @@ Rectangle { currentAvatarSettings = getAvatarsData.currentAvatarSettings; updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'updateAvatarInBookmarks') { + } else if (message.method === 'updateAvatarInBookmarks') { updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'selectAvatarEntity') { + } else if (message.method === 'selectAvatarEntity') { adjustWearables.selectWearableByID(message.entityID); } } function updateCurrentAvatarInBookmarks(avatar) { var bookmarkAvatarIndex = allAvatars.findAvatarIndexByValue(avatar); - if(bookmarkAvatarIndex === -1) { + if (bookmarkAvatarIndex === -1) { avatar.name = ''; view.setPage(0); } else { @@ -597,8 +598,9 @@ Rectangle { pageOfAvatars.append(avatarItem); } - if(pageOfAvatars.count !== itemsPerPage) + if (pageOfAvatars.count !== itemsPerPage) { pageOfAvatars.appendGetAvatars(); + } currentPage = pageIndex; pageOfAvatars.isUpdating = false; @@ -619,7 +621,7 @@ Rectangle { } function removeGetAvatars() { - if(hasGetAvatars()) { + if (hasGetAvatars()) { remove(count - 1) } } @@ -687,13 +689,13 @@ Rectangle { hoverEnabled: enabled onClicked: { - if(isInManageState) { + if (isInManageState) { var currentItem = delegateRoot.GridView.view.model.get(index); popup.showDeleteFavorite(currentItem.name, function() { view.deleteAvatar(currentItem); }); } else { - if(delegateRoot.GridView.view.currentIndex !== index) { + if (delegateRoot.GridView.view.currentIndex !== index) { var currentItem = delegateRoot.GridView.view.model.get(index); popup.showLoadFavorite(currentItem.name, function() { view.selectAvatar(currentItem); diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index bec037fdf2..e80dab60c2 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -43,10 +43,10 @@ Rectangle { wearablesCombobox.model.clear(); wearablesCombobox.currentIndex = -1; - for(var i = 0; i < avatar.wearables.count; ++i) { + for (var i = 0; i < avatar.wearables.count; ++i) { var wearable = avatar.wearables.get(i).properties; - for(var j = (wearable.modelURL.length - 1); j >= 0; --j) { - if(wearable.modelURL[j] === '/') { + for (var j = (wearable.modelURL.length - 1); j >= 0; --j) { + if (wearable.modelURL[j] === '/') { wearable.text = wearable.modelURL.substring(j + 1); break; } @@ -54,34 +54,34 @@ Rectangle { wearablesCombobox.model.append(wearable); } - if(wearablesCombobox.model.count !== 0) { + if (wearablesCombobox.model.count !== 0) { wearablesCombobox.currentIndex = 0; } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { - if(wearableIndex === -1) { + if (wearableIndex === -1) { wearableIndex = wearablesCombobox.model.findIndexById(wearableID); } var wearable = wearablesCombobox.model.get(wearableIndex); - if(!wearable) { + if (!wearable) { return; } var wearableModelItemProperties = wearablesModel.get(wearableIndex).properties; - for(var prop in properties) { + for (var prop in properties) { wearable[prop] = properties[prop]; wearableModelItemProperties[prop] = wearable[prop]; - if(updateUI) { - if(prop === 'localPosition') { + if (updateUI) { + if (prop === 'localPosition') { positionVector.set(wearable[prop]); - } else if(prop === 'localRotationAngles') { + } else if (prop === 'localRotationAngles') { rotationVector.set(wearable[prop]); - } else if(prop === 'dimensions') { + } else if (prop === 'dimensions') { scalespinner.set(wearable[prop].x / wearable.naturalDimensions.x); } } @@ -95,9 +95,9 @@ Rectangle { } function selectWearableByID(entityID) { - for(var i = 0; i < wearablesCombobox.model.count; ++i) { + for (var i = 0; i < wearablesCombobox.model.count; ++i) { var wearable = wearablesCombobox.model.get(i); - if(wearable.id === entityID) { + if (wearable.id === entityID) { wearablesCombobox.currentIndex = i; break; } @@ -220,9 +220,9 @@ Rectangle { model: ListModel { function findIndexById(id) { - for(var i = 0; i < count; ++i) { + for (var i = 0; i < count; ++i) { var wearable = get(i); - if(wearable.id === id) { + if (wearable.id === id) { return i; } } @@ -245,7 +245,7 @@ Rectangle { jointsCombobox.set(joint); isSoft.set(soft); - if(currentWearable) { + if (currentWearable) { wearableSelected(currentWearable.id); } } @@ -298,7 +298,7 @@ Rectangle { } onCurrentIndexChanged: { - if(notify) notifyJointChanged(); + if (notify) notifyJointChanged(); } } } @@ -352,9 +352,9 @@ Rectangle { property bool notify: false; - onXvalueChanged: if(notify) notifyPositionChanged(); - onYvalueChanged: if(notify) notifyPositionChanged(); - onZvalueChanged: if(notify) notifyPositionChanged(); + onXvalueChanged: if (notify) notifyPositionChanged(); + onYvalueChanged: if (notify) notifyPositionChanged(); + onZvalueChanged: if (notify) notifyPositionChanged(); decimals: 2 realFrom: -10 @@ -412,9 +412,9 @@ Rectangle { property bool notify: false; - onXvalueChanged: if(notify) notifyRotationChanged(); - onYvalueChanged: if(notify) notifyRotationChanged(); - onZvalueChanged: if(notify) notifyRotationChanged(); + onXvalueChanged: if (notify) notifyRotationChanged(); + onYvalueChanged: if (notify) notifyRotationChanged(); + onZvalueChanged: if (notify) notifyRotationChanged(); decimals: 0 realFrom: -180 @@ -453,7 +453,7 @@ Rectangle { property bool notify: false; - onCheckedChanged: if(notify) notifyIsSoftChanged(); + onCheckedChanged: if (notify) notifyIsSoftChanged(); } Column { @@ -482,7 +482,7 @@ Rectangle { colorScheme: hifi.colorSchemes.light property bool notify: false; - onRealValueChanged: if(notify) notifyScaleChanged(); + onRealValueChanged: if (notify) notifyScaleChanged(); function set(value) { notify = false; diff --git a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml index f9a231ec48..bfd66f1a30 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml @@ -24,8 +24,9 @@ ListModel { function makeThumbnailUrl(avatarUrl) { var marketId = extractMarketId(avatarUrl); - if(marketId === '') + if (marketId === '') { return ''; + } var avatarThumbnailUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/%marketId%/large/hifi-mp-%marketId%.jpg" .split('%marketId%').join(marketId); @@ -57,7 +58,7 @@ ListModel { function populate(bookmarks) { clear(); - for(var avatarName in bookmarks) { + for (var avatarName in bookmarks) { var avatar = bookmarks[avatarName]; var avatarEntry = makeAvatarObject(avatar, avatarName); @@ -66,19 +67,19 @@ ListModel { } function arraysAreEqual(a1, a2, comparer) { - if(Array.isArray(a1) && Array.isArray(a2)) { - if(a1.length !== a2.length) { + if (Array.isArray(a1) && Array.isArray(a2)) { + if (a1.length !== a2.length) { return false; } - for(var i = 0; i < a1.length; ++i) { - if(!comparer(a1[i], a2[i])) { + for (var i = 0; i < a1.length; ++i) { + if (!comparer(a1[i], a2[i])) { return false; } } - } else if(Array.isArray(a1)) { + } else if (Array.isArray(a1)) { return a1.length === 0; - } else if(Array.isArray(a2)) { + } else if (Array.isArray(a2)) { return a2.length === 0; } @@ -86,26 +87,26 @@ ListModel { } function modelsAreEqual(m1, m2, comparer) { - if(m1.count !== m2.count) { + if (m1.count !== m2.count) { return false; } - for(var i = 0; i < m1.count; ++i) { + for (var i = 0; i < m1.count; ++i) { var e1 = m1.get(i); var allDifferent = true; // it turns out order of wearables can randomly change so make position-independent comparison here - for(var j = 0; j < m2.count; ++j) { + for (var j = 0; j < m2.count; ++j) { var e2 = m2.get(j); - if(comparer(e1, e2)) { + if (comparer(e1, e2)) { allDifferent = false; break; } } - if(allDifferent) { + if (allDifferent) { return false; } } @@ -114,18 +115,20 @@ ListModel { } function compareNumericObjects(o1, o2) { - if(o1 === undefined && o2 !== undefined) + if (o1 === undefined && o2 !== undefined) { return false; - if(o1 !== undefined && o2 === undefined) + } + if (o1 !== undefined && o2 === undefined) { return false; + } - for(var prop in o1) { - if(o1.hasOwnProperty(prop) && o2.hasOwnProperty(prop)) { + for (var prop in o1) { + if (o1.hasOwnProperty(prop) && o2.hasOwnProperty(prop)) { var v1 = o1[prop]; var v2 = o2[prop]; - if(v1 !== v2 && Math.round(v1 * 500) != Math.round(v2 * 500)) { + if (v1 !== v2 && Math.round(v1 * 500) != Math.round(v2 * 500)) { return false; } } @@ -135,7 +138,7 @@ ListModel { } function compareObjects(o1, o2, props, arrayProp) { - for(var i = 0; i < props.length; ++i) { + for (var i = 0; i < props.length; ++i) { var prop = props[i]; var propertyName = prop.propertyName; var comparer = prop.comparer; @@ -143,12 +146,12 @@ ListModel { var o1Value = arrayProp ? o1[arrayProp][propertyName] : o1[propertyName]; var o2Value = arrayProp ? o2[arrayProp][propertyName] : o2[propertyName]; - if(comparer) { - if(comparer(o1Value, o2Value) === false) { + if (comparer) { + if (comparer(o1Value, o2Value) === false) { return false; } } else { - if(JSON.stringify(o1Value) !== JSON.stringify(o2Value)) { + if (JSON.stringify(o1Value) !== JSON.stringify(o2Value)) { return false; } } @@ -174,21 +177,23 @@ ListModel { var index = -1; // 2DO: find better way of determining selected avatar in bookmarks - for(var i = 0; i < allAvatars.count; ++i) { + for (var i = 0; i < allAvatars.count; ++i) { var thesame = true; var bookmarkedAvatar = allAvatars.get(i); - if(bookmarkedAvatar.avatarUrl !== avatar.avatarUrl) - continue; - - if(bookmarkedAvatar.avatarScale !== avatar.avatarScale) - continue; - - if(!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) { + if (bookmarkedAvatar.avatarUrl !== avatar.avatarUrl) { continue; } - if(thesame) { + if (bookmarkedAvatar.avatarScale !== avatar.avatarScale) { + continue; + } + + if (!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) { + continue; + } + + if (thesame) { index = i; break; } @@ -198,8 +203,8 @@ ListModel { } function findAvatarIndex(avatarName) { - for(var i = 0; i < count; ++i) { - if(get(i).name === avatarName) { + for (var i = 0; i < count; ++i) { + if (get(i).name === avatarName) { return i; } } @@ -208,8 +213,9 @@ ListModel { function findAvatar(avatarName) { var avatarIndex = findAvatarIndex(avatarName); - if(avatarIndex === -1) + if (avatarIndex === -1) { return undefined; + } return get(avatarIndex); } diff --git a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml index ab995e79e8..1387c0791a 100644 --- a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml +++ b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml @@ -125,9 +125,9 @@ Rectangle { size: 15 color: 'red' visible: { - for(var i = 0; i < avatars.count; ++i) { + for (var i = 0; i < avatars.count; ++i) { var avatarName = avatars.get(i).name; - if(avatarName === favoriteName.text) { + if (avatarName === favoriteName.text) { return true; } } @@ -165,7 +165,7 @@ Rectangle { } onYesClicked: function() { - if(onSaveClicked) { + if (onSaveClicked) { onSaveClicked(); } else { root.close(); @@ -173,7 +173,7 @@ Rectangle { } onNoClicked: function() { - if(onCancelClicked) { + if (onCancelClicked) { onCancelClicked(); } else { root.close(); diff --git a/interface/resources/qml/hifi/avatarapp/DialogButtons.qml b/interface/resources/qml/hifi/avatarapp/DialogButtons.qml index 46c17bb4dc..d6eb0139ed 100644 --- a/interface/resources/qml/hifi/avatarapp/DialogButtons.qml +++ b/interface/resources/qml/hifi/avatarapp/DialogButtons.qml @@ -33,7 +33,7 @@ Row { onClicked: { console.debug('whitebutton.clicked', onNoClicked); - if(onNoClicked) { + if (onNoClicked) { onNoClicked(); } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBox.qml b/interface/resources/qml/hifi/avatarapp/MessageBox.qml index e4aa0847c5..f111303214 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBox.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBox.qml @@ -128,7 +128,7 @@ Rectangle { wrapMode: Text.WordWrap; onLinkActivated: { - if(onLinkClicked) + if (onLinkClicked) onLinkClicked(link); } } @@ -166,7 +166,7 @@ Rectangle { noText: root.button1text onYesClicked: function() { - if(onButton2Clicked) { + if (onButton2Clicked) { onButton2Clicked(); } else { root.close(); @@ -174,7 +174,7 @@ Rectangle { } onNoClicked: function() { - if(onButton1Clicked) { + if (onButton1Clicked) { onButton1Clicked(); } else { root.close(); diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index f78c973bd6..b7782c697d 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -18,15 +18,17 @@ MessageBox { popup.button2text = 'CONFIRM'; popup.onButton2Clicked = function() { - if(callback) + if (callback) { callback(); + } popup.close(); } popup.onLinkClicked = function(link) { - if(linkCallback) + if (linkCallback) { linkCallback(link); + } } popup.open(); @@ -43,8 +45,9 @@ MessageBox { popup.inputText.placeholderText = 'Enter Wearable URL'; popup.onButton2Clicked = function() { - if(callback) + if (callback) { callback(popup.inputText.text); + } popup.close(); } @@ -68,15 +71,17 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.onLinkClicked = function(link) { popup.close(); - if(linkCallback) + if (linkCallback) { linkCallback(link); + } } popup.open(); @@ -92,8 +97,9 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.open(); } @@ -107,8 +113,9 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.open(); } @@ -129,15 +136,17 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.onLinkClicked = function(link) { popup.close(); - if(linkCallback) + if (linkCallback) { linkCallback(link); + } } popup.open(); diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index e1b55866c2..71bfbb084d 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -33,13 +33,13 @@ Rectangle { scaleSlider.value = Math.round(avatarScale * 10); scaleSlider.notify = true;; - if(settings.dominantHand === 'left') { + if (settings.dominantHand === 'left') { leftHandRadioButton.checked = true; } else { rightHandRadioButton.checked = true; } - if(settings.collisionsEnabled) { + if (settings.collisionsEnabled) { collisionsEnabledRadiobutton.checked = true; } else { collisionsDisabledRadioButton.checked = true; @@ -113,7 +113,7 @@ Rectangle { onValueChanged: { console.debug('value changed: ', value); - if(notify) { + if (notify) { console.debug('notifying.. '); root.scaleChanged(value / 10); } diff --git a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml index 4884d1e1ad..6c50a6093a 100644 --- a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml +++ b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml @@ -32,7 +32,7 @@ Item { highp vec4 maskColor = texture2D(mask, vec2(qt_TexCoord0.x, qt_TexCoord0.y)); highp vec4 sourceColor = texture2D(source, vec2(qt_TexCoord0.x, qt_TexCoord0.y)); - if(maskColor.a > 0.0) + if (maskColor.a > 0.0) gl_FragColor = sourceColor; else gl_FragColor = maskColor; From 6b994ba39a27cbc4ff895947973d05a7433f865e Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 14:51:54 -0700 Subject: [PATCH 111/207] Completely remove the texcoord frame transform as we don;t use it at all --- libraries/render-utils/src/DeferredLightingEffect.cpp | 11 ----------- libraries/render-utils/src/deferred_light.slv | 10 +++++++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 91d196f896..af5a3f3e63 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -496,11 +496,6 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, batch.setPipeline(program); } - // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) - // Adjust the texcoordTransform in the case we are rendeirng a sub region(mini mirror) - // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport); - // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); - // Setup the global lighting deferredLightingEffect->setupKeyLightBatch(args, batch); @@ -561,24 +556,18 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext batch.setViewportTransform(viewport); batch.setStateScissorRect(viewport); - // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) - // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport); - - auto& lightIndices = lightClusters->_visibleLightIndices; if (!lightIndices.empty() && lightIndices[0] > 0) { deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); // Local light pipeline batch.setPipeline(deferredLightingEffect->_localLight); - // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); // Draw outline as well ? if (lightingModel->isShowLightContourEnabled()) { batch.setPipeline(deferredLightingEffect->_localLightOutline); - // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); } diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 0bc7a0f807..4ea3133718 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,8 +16,10 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; +#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) -//layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; +layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; +#endif void main(void) { const float depth = 1.0; @@ -31,8 +33,10 @@ void main(void) { _texCoord01.xy = (pos.xy + 1.0) * 0.5; - // _texCoord01.xy *= texcoordFrameTransform.zw; - // _texCoord01.xy += texcoordFrameTransform.xy; +#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM + _texCoord01.xy *= texcoordFrameTransform.zw; + _texCoord01.xy += texcoordFrameTransform.xy; +#endif gl_Position = pos; } From 30c0c44822fe0817e93d1007465a9792441cce84 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 14:52:42 -0700 Subject: [PATCH 112/207] Completely remove the texcoord frame transform as we don;t use it at all --- libraries/render-utils/src/deferred_light.slv | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 4ea3133718..164fd9fb3b 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,11 +16,6 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; -#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM -// NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) -layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; -#endif - void main(void) { const float depth = 1.0; const vec4 UNIT_QUAD[4] = vec4[4]( @@ -33,10 +28,5 @@ void main(void) { _texCoord01.xy = (pos.xy + 1.0) * 0.5; -#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM - _texCoord01.xy *= texcoordFrameTransform.zw; - _texCoord01.xy += texcoordFrameTransform.xy; -#endif - gl_Position = pos; } From 54aeac5d1f8b27717557ce546f020ef265a4505e Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 15:55:00 -0700 Subject: [PATCH 113/207] adding fix --- libraries/ui/src/ui/TabletScriptingInterface.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/libraries/ui/src/ui/TabletScriptingInterface.cpp b/libraries/ui/src/ui/TabletScriptingInterface.cpp index 4e920e430b..1081f8c4e7 100644 --- a/libraries/ui/src/ui/TabletScriptingInterface.cpp +++ b/libraries/ui/src/ui/TabletScriptingInterface.cpp @@ -140,8 +140,7 @@ int TabletButtonsProxyModel::buttonIndex(const QString &uuid) { return -1; } -void TabletButtonsProxyModel::setPageIndex(int pageIndex) -{ +void TabletButtonsProxyModel::setPageIndex(int pageIndex) { if (_pageIndex == pageIndex) return; @@ -465,6 +464,9 @@ void TabletProxy::onTabletShown() { _showRunningScripts = false; pushOntoStack("hifi/dialogs/TabletRunningScripts.qml"); } + if (_currentPathLoaded == TABLET_HOME_SOURCE_URL) { + loadHomeScreen(true); + } } } From 81d0e212dd737b34f43f2f0fff845855a06ee0f9 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 15:57:17 -0700 Subject: [PATCH 114/207] removing print statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index 10a9f01337..cccd37ad95 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -117,7 +117,6 @@ Item { id: pageRepeater model: Math.ceil(tabletProxy.buttons.rowCount() / TabletEnums.ButtonsOnPage) onItemAdded: { - console.log("onItemAdded: " + tabletProxy.buttons.rowCount()); item.proxyModel.sourceModel = tabletProxy.buttons; item.proxyModel.pageIndex = index; } @@ -209,7 +208,6 @@ Item { Component.onCompleted: updateProperties() function updateProperties() { - console.log("updateProperties: " + tabletProxy.buttons.rowCount()); var keys = Object.keys(modelData.properties).forEach(function (key) { if (tabletButton[key] !== modelData.properties[key]) { tabletButton[key] = modelData.properties[key]; @@ -272,9 +270,6 @@ Item { anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count - Component.onCompleted: { - console.log("pageIndicator: " + pageIndicator.count); - } } } From bb176eb22834b01d6c1e89d38f18533240f5cef7 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 16:00:01 -0700 Subject: [PATCH 115/207] trailing debug statements --- interface/resources/qml/hifi/tablet/TabletHome.qml | 1 - scripts/system/menu.js | 1 - scripts/system/tablet-ui/tabletUI.js | 4 +--- 3 files changed, 1 insertion(+), 5 deletions(-) diff --git a/interface/resources/qml/hifi/tablet/TabletHome.qml b/interface/resources/qml/hifi/tablet/TabletHome.qml index cccd37ad95..1922b02f93 100644 --- a/interface/resources/qml/hifi/tablet/TabletHome.qml +++ b/interface/resources/qml/hifi/tablet/TabletHome.qml @@ -269,7 +269,6 @@ Item { anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter count: swipeView.count - } } diff --git a/scripts/system/menu.js b/scripts/system/menu.js index 1db210216a..d669d3d918 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -52,7 +52,6 @@ var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet- if (isHMDMode) { button = tablet.addButton(buttonProperties); button.clicked.connect(onClicked); - print("Adding menu button"); } else if (button) { button.clicked.disconnect(onClicked); tablet.removeButton(button); diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index c28040000b..d78e90b789 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -22,7 +22,7 @@ var DEFAULT_TABLET_SCALE = 70; var preMakeTime = Date.now(); var validCheckTime = Date.now(); - var debugTablet = true; + var debugTablet = false; var tabletScalePercentage = 70.0; var UIWebTablet = null; var MSECS_PER_SEC = 1000.0; @@ -298,7 +298,6 @@ } wantsMenu = clicked; }); - clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -306,7 +305,6 @@ } wantsMenu = clicked; }); - clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 4e9a5446187187ffcba87a846ec1b22f36cdbf15 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Tue, 21 Aug 2018 16:01:24 -0700 Subject: [PATCH 116/207] redoing newline --- scripts/system/tablet-ui/tabletUI.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index d78e90b789..80ddbeca8b 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -298,6 +298,7 @@ } wantsMenu = clicked; }); + clickMapping.from(Controller.Standard.LeftSecondaryThumb).peek().to(function (clicked) { if (clicked) { //activeHudPoint2d(Controller.Standard.LeftHand); @@ -305,6 +306,7 @@ } wantsMenu = clicked; }); + clickMapping.from(Controller.Standard.Start).peek().to(function (clicked) { if (clicked) { //activeHudPoint2dGamePad(); From 11c1845dfa2ca7e1e01abc663582fff876562dbb Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 21 Aug 2018 16:11:45 -0700 Subject: [PATCH 117/207] added rotate script changes that allow using the hand hip azimuth --- scripts/developer/rotateApp.js | 340 +++++++++++++++++++++++++++++++++ 1 file changed, 340 insertions(+) create mode 100644 scripts/developer/rotateApp.js diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js new file mode 100644 index 0000000000..d65eedfbbe --- /dev/null +++ b/scripts/developer/rotateApp.js @@ -0,0 +1,340 @@ +/* global Script, Vec3, MyAvatar, Tablet, Messages, Quat, +DebugDraw, Mat4, Entities, Xform, Controller, Camera, console, document*/ + +Script.registerValue("ROTATEAPP", true); + +var TABLET_BUTTON_NAME = "ROTATE"; +var CHANGE_OF_BASIS_ROTATION = { x: 0, y: 1, z: 0, w: 0 }; +var DEFAULT_HEAD_TURN_THRESHOLD = 0.5333; +var DEFAULT_HEAD_TURN_FILTER_LENGTH = 4.0; +var LOADING_DELAY = 2000; +var AVERAGING_RATE = 0.03; +var INCREASING = 1.0; +var DECREASING = -1.0; +var DEGREES_PER_PI_RADIANS = 180.0; +var FILTER_FUDGE_RANGE = 0.9; + +var activated = false; +var documentLoaded = false; +var sciptLoaded = false; +var headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; +var hipToLeftHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; +var hipToRightHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; +var averageAzimuth = 0.0; +var hipsPositionRigSpace = { x: 0, y: 0, z: 0 }; +var spine2PositionRigSpace = { x: 0, y: 0, z: 0 }; +var hipsRotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; +var spine2RotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; +var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 }; +var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; +var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; + +var ikTypes = { + RotationAndPosition: 0, + RotationOnly: 1, + HmdHead: 2, + HipsRelativeRotationAndPosition: 3, + Spline: 4, + Unknown: 5 +}; + + +var ANIM_VARS = [ + //"headType", + "spine2Type", + //"hipsType", + "spine2Position", + "spine2Rotation", + //"hipsPosition", + //"hipsRotation" +]; + +var handlerId = MyAvatar.addAnimationStateHandler(function (props) { + //print("in callback"); + //print("props spine2 pos: " + props.spine2Position.x + " " + props.spine2Position.y + " " + props.spine2Position.z); + //print("props hip pos: " + props.hipsPosition.x + " " + props.hipsPosition.y + " " + props.hipsPosition.z); + var result = {}; + //{x:0,y:0,z:0} + //result.headType = ikTypes.HmdHead; + //result.hipsType = ikTypes.RotationAndPosition; + //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; + //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // + result.spine2Type = ikTypes.Spline; + result.spine2Position = { x: 0, y: 1.3, z: 0 }; + result.spine2Rotation = spine2Rotation; + + return result; +}, ANIM_VARS); + +// define state readings constructor +function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) { + this.headPose = headPose; + this.rhandPose = rhandPose; + this.lhandPose = lhandPose; + this.diffFromAverageEulers = diffFromAverageEulers; +} + +// define current state readings object for holding tracker readings and current differences from averages +var currentStateReadings = new StateReading(Controller.getPoseValue(Controller.Standard.Head), + Controller.getPoseValue(Controller.Standard.RightHand), + Controller.getPoseValue(Controller.Standard.LeftHand), + { x: 0, y: 0, z: 0 }); + +// declare the checkbox constructor +function AppCheckbox(type,id,eventType,isChecked) { + this.type = type; + this.id = id; + this.eventType = eventType; + this.data = {value: isChecked}; +} + +var usingStepResetRotationDirection = new AppCheckbox("checkboxtick", "stepReset", "onStepResetCheckBox", false); +var usingDrawAverageFacing = new AppCheckbox("checkboxtick", "drawAverage", "onDrawAverageFacingCheckBox", false); +var checkBoxArray = new Array(usingStepResetRotationDirection, usingDrawAverageFacing); + +// declare the html slider constructor +function AppProperty(name, type, eventType, signalType, setFunction, initValue, convertToThreshold, convertToSlider, signalOn) { + this.name = name; + this.type = type; + this.eventType = eventType; + this.signalType = signalType; + this.setValue = setFunction; + this.value = initValue; + this.get = function () { + return this.value; + }; + this.convertToThreshold = convertToThreshold; + this.convertToSlider = convertToSlider; +} + +// var HTML_URL = Script.resolvePath("file:///c:/dev/hifi_fork/hifi/scripts/developer/rotateRecenterApp.html"); +var HTML_URL = Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/rotateRecenterApp.html"); +var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); + +// define the sliders +var filterLengthProperty = new AppProperty("#filterLength-slider", "slider", "onFilterLengthSlider", "filterSignal", + setFilterLength, DEFAULT_HEAD_TURN_FILTER_LENGTH, function (num) { + var base = 5; + var shift = 0; + return convertExponential(base, num, INCREASING, shift); + }, function (num) { + var base = 5; + var shift = 0; + return convertLog(base, num, INCREASING, shift); + }, true); +var angleThresholdProperty = new AppProperty("#angleThreshold-slider", "slider", "onAngleThresholdSlider", "angleSignal", + setAngleThreshold, DEFAULT_HEAD_TURN_THRESHOLD, function (num) { + return convertToRadians(num); + }, function (num) { + return convertToDegrees(num); + }, true); + +var propArray = new Array(filterLengthProperty, angleThresholdProperty); + +function setFilterLength(num) { + filterLengthProperty.value = num; + MyAvatar.rotationRecenterFilterLength = filterLengthProperty.value; + +} + +function setAngleThreshold(num) { + angleThresholdProperty.value = num; + MyAvatar.rotationThreshold = angleThresholdProperty.value; +} + +function convertToRadians(num) { + return (num / DEGREES_PER_PI_RADIANS) * Math.PI; +} + +function convertToDegrees(num) { + return (num / Math.PI) * DEGREES_PER_PI_RADIANS; +} + +function getLog(x, y) { + return Math.log(y) / Math.log(x); +} + +function convertLog(base, num, direction, shift) { + return direction * getLog(base, (num + FILTER_FUDGE_RANGE)) + shift; +} + +function convertExponential(base, num, direction, shift) { + return Math.pow(base, (direction * num + shift)) - FILTER_FUDGE_RANGE; +} + +function manageClick() { + if (activated) { + tablet.gotoHomeScreen(); + } else { + tablet.gotoWebScreen(HTML_URL); + } +} + +var tabletButton = tablet.addButton({ + text: TABLET_BUTTON_NAME, + icon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg"), + activeIcon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg") +}); + +function onKeyPress(event) { + if (event.text === "'") { + // when the sensors are reset, then reset the mode. + } +} + +function onWebEventReceived(msg) { + var message = JSON.parse(msg); + print(" we have a message from html dialog " + message.type); + propArray.forEach(function (prop) { + if (prop.eventType === message.type) { + prop.setValue(prop.convertToThreshold(message.data.value)); + print("message from " + prop.name); + // break; + } + }); + checkBoxArray.forEach(function(cbox) { + if (cbox.eventType === message.type) { + cbox.data.value = message.data.value; + if (cbox.id === "stepReset") { + MyAvatar.enableStepResetRotation = cbox.data.value; + } + if (cbox.id === "drawAverage") { + MyAvatar.enableDrawAverageFacing = cbox.data.value; + } + // break; + } + + }); + if (message.type === "onCreateRotateApp") { + print("document loaded"); + documentLoaded = true; + Script.setTimeout(initAppForm, LOADING_DELAY); + } +} + +function initAppForm() { + print("step app is loaded: " + documentLoaded); + if (documentLoaded === true) { + propArray.forEach(function (prop) { + print(prop.name); + tablet.emitScriptEvent(JSON.stringify({ + "type": "slider", + "id": prop.name, + "data": { "value": prop.convertToSlider(prop.value) } + })); + }); + checkBoxArray.forEach(function (cbox) { + tablet.emitScriptEvent(JSON.stringify({ + "type": "checkboxtick", + "id": cbox.id, + "data": { value: cbox.data.value } + })); + }); + } + +} + + +function onScreenChanged(type, url) { + print("Screen changed"); + if (type === "Web" && url === HTML_URL) { + if (!activated) { + // hook up to event bridge + tablet.webEventReceived.connect(onWebEventReceived); + print("after connect web event"); + MyAvatar.hmdLeanRecenterEnabled = true; + } + activated = true; + } else { + if (activated) { + // disconnect from event bridge + tablet.webEventReceived.disconnect(onWebEventReceived); + documentLoaded = false; + } + activated = false; + } +} + +function limitAngle(angle) { + return (angle + 180) % 360 - 180; +} + +function computeHandAzimuths() { + var leftHand = currentStateReadings.lhandPose.translation; + var rightHand = currentStateReadings.rhandPose.translation; + var head = currentStateReadings.headPose.translation; + var lHandMinusHead = Vec3.subtract(leftHand, head); + lHandMinusHead.y = 0.0; + var rHandMinusHead = Vec3.subtract(rightHand, head); + rHandMinusHead.y = 0.0; + print(JSON.stringify(leftHand)); + print(JSON.stringify(head)); + var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; + var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); + var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); + hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, 0.99); + hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, 0.99); + + // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); + // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); + var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; + print(leftRightMidpoint); + + return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); + + +} + +function update(dt) { + //update state readings + currentStateReadings.head = Controller.getPoseValue(Controller.Standard.Head); + currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand); + currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand); + + print(JSON.stringify(currentStateReadings.head)); + + var latestSpineRotation = computeHandAzimuths(); + var zAxisSpineRotation = Vec3.multiplyQbyV(latestSpineRotation, { x: 0, y: 0, z: 1 }); + var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.rotation, zAxisSpineRotation); + DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisSpineRotation), { x: 1, y: 0, z: 0 }); + spine2Rotation = latestSpineRotation; + /* + if (HMD.active && !scriptLoaded) { + //Script.load("rotateApp.js"); + scriptLoaded = true; + } + + if (!HMD.active) { + scriptLoaded = false; + } + */ + + // handle the azimuth of the arms + +} + +function shutdownTabletApp() { + tablet.removeButton(tabletButton); + if (activated) { + tablet.webEventReceived.disconnect(onWebEventReceived); + tablet.gotoHomeScreen(); + } + tablet.screenChanged.disconnect(onScreenChanged); +} + +Script.setTimeout(function () { + tabletButton.clicked.connect(manageClick); + tablet.screenChanged.connect(onScreenChanged); + Script.update.connect(update); + Controller.keyPressEvent.connect(onKeyPress); +}, (LOADING_DELAY)); + +Script.scriptEnding.connect(function () { + // if (handlerId) { + // print("removing animation state handler"); + // handlerId = MyAvatar.removeAnimationStateHandler(handlerId); + // } + MyAvatar.hmdLeanRecenterEnabled = true; + Script.update.disconnect(update); + shutdownTabletApp(); +}); \ No newline at end of file From d74fef2ea4c68611ca25070fab22458d00219f6f Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 21 Aug 2018 16:27:47 -0700 Subject: [PATCH 118/207] fixed spine 2 position updating --- scripts/developer/rotateApp.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index d65eedfbbe..ea616b1697 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -60,7 +60,7 @@ var handlerId = MyAvatar.addAnimationStateHandler(function (props) { //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // result.spine2Type = ikTypes.Spline; - result.spine2Position = { x: 0, y: 1.3, z: 0 }; + result.spine2Position = spine2PositionRigSpace;// { x: 0, y: 1.3, z: 0 }; result.spine2Rotation = spine2Rotation; return result; @@ -298,6 +298,8 @@ function update(dt) { var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.rotation, zAxisSpineRotation); DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisSpineRotation), { x: 1, y: 0, z: 0 }); spine2Rotation = latestSpineRotation; + var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); + spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos); /* if (HMD.active && !scriptLoaded) { //Script.load("rotateApp.js"); From e9683011f8e01f55b2f59306947c0b5b1fe22962 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Aug 2018 16:58:38 -0700 Subject: [PATCH 119/207] Fix parabola rendering on AMD GPUs --- libraries/render-utils/src/parabola.slf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/parabola.slf b/libraries/render-utils/src/parabola.slf index 8863f37083..ea51d7e3af 100644 --- a/libraries/render-utils/src/parabola.slf +++ b/libraries/render-utils/src/parabola.slf @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include DeferredBufferWrite.slh@> + layout(location=0) in vec4 _color; -layout(location=0) out vec4 _fragColor; - void main(void) { - _fragColor = _color; + packDeferredFragmentUnlit(vec3(1.0, 0.0, 0.0), 1.0, _color.rgb); } From 5f4903f884a37edb06e6189136e3949afd9c88b3 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 22 Aug 2018 08:53:51 -0700 Subject: [PATCH 120/207] Record entities --- interface/src/avatar/MyAvatar.cpp | 1 + .../src/avatars-renderer/Avatar.cpp | 2 ++ libraries/avatars/src/AvatarData.cpp | 20 +++++++++++++++---- libraries/avatars/src/AvatarData.h | 2 ++ 4 files changed, 21 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 41855e7973..ba9d15159b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -184,6 +184,7 @@ MyAvatar::MyAvatar(QThread* thread) : if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } + createRecordingIDs(); _previousCollisionGroup = _characterController.computeCollisionGroup(); _characterController.setCollisionless(true); } else { diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0b43fd5433..0f2c936507 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -236,6 +236,8 @@ void Avatar::updateAvatarEntities() { return; } + createRecordingIDs(); + if (getID().isNull() || getID() == AVATAR_SELF_ID || DependencyManager::get()->getSessionUUID() == QUuid()) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8c72be633f..40df2fa3aa 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2243,6 +2243,15 @@ void AvatarData::setRecordingBasis(std::shared_ptr recordingBasis) { _recordingBasis = recordingBasis; } +void AvatarData::createRecordingIDs() { + _avatarEntitiesLock.withReadLock([&] { + _avatarEntityForRecording.clear(); + for (int i = 0; i < _avatarEntityData.size(); i++) { + _avatarEntityForRecording.insert(QUuid::createUuid()); + } + }); +} + void AvatarData::clearRecordingBasis() { _recordingBasis.reset(); } @@ -2306,10 +2315,12 @@ QJsonObject AvatarData::toJson() const { _avatarEntitiesLock.withReadLock([&] { if (!_avatarEntityData.empty()) { QJsonArray avatarEntityJson; + int entityCount = 0; for (auto entityID : _avatarEntityData.keys()) { QVariantMap entityData; - entityData.insert("id", entityID); - entityData.insert("properties", _avatarEntityData.value(entityID)); + QUuid newId = _avatarEntityForRecording.size() == _avatarEntityData.size() ? _avatarEntityForRecording.values()[entityCount++] : entityID; + entityData.insert("id", newId); + entityData.insert("properties", _avatarEntityData.value(entityID).toBase64()); avatarEntityJson.push_back(QVariant(entityData).toJsonObject()); } root[JSON_AVATAR_ENTITIES] = avatarEntityJson; @@ -2434,10 +2445,11 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { QJsonArray attachmentsJson = json[JSON_AVATAR_ENTITIES].toArray(); for (auto attachmentJson : attachmentsJson) { - if (attachmentJson.isObject()) { + if (attachmentJson.isObject()) { QVariantMap entityData = attachmentJson.toObject().toVariantMap(); QUuid entityID = entityData.value("id").toUuid(); - QByteArray properties = entityData.value("properties").toByteArray(); + auto ds = QByteArray::fromBase64(entityData.value("properties").toString().toUtf8()); + QByteArray properties = QByteArray::fromBase64(entityData.value("properties").toByteArray()); updateAvatarEntity(entityID, properties); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fcc63fdc98..79d47eb03f 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1077,6 +1077,7 @@ public: void clearRecordingBasis(); TransformPointer getRecordingBasis() const; void setRecordingBasis(TransformPointer recordingBasis = TransformPointer()); + void createRecordingIDs(); QJsonObject toJson() const; void fromJson(const QJsonObject& json, bool useFrameSkeleton = true); @@ -1410,6 +1411,7 @@ protected: mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar + AvatarEntityIDs _avatarEntityForRecording; // create new entities id for avatar recording AvatarEntityMap _avatarEntityData; bool _avatarEntityDataLocallyEdited { false }; bool _avatarEntityDataChanged { false }; From 5a5aa2f5b9cc07a66369a28d715302d9ea1af547 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 22 Aug 2018 09:13:27 -0700 Subject: [PATCH 121/207] Remove proxy line --- libraries/avatars/src/AvatarData.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 40df2fa3aa..c97051eddf 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2448,7 +2448,6 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { if (attachmentJson.isObject()) { QVariantMap entityData = attachmentJson.toObject().toVariantMap(); QUuid entityID = entityData.value("id").toUuid(); - auto ds = QByteArray::fromBase64(entityData.value("properties").toString().toUtf8()); QByteArray properties = QByteArray::fromBase64(entityData.value("properties").toByteArray()); updateAvatarEntity(entityID, properties); } From 456b8bf2a927c40499cc420b44ae6a18e18070a4 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 09:36:52 -0700 Subject: [PATCH 122/207] tweaked the animvars callback to update the spine2 position --- interface/src/avatar/MyAvatar.cpp | 5 ++++ scripts/developer/rotateApp.js | 38 +++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c3d8b8df4f..04a6d58aae 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3937,6 +3937,11 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } + if (!isActive(Rotation) && getForceActivateRotation()) { + activate(Rotation); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + setForceActivateRotation(false); + } } else { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index ea616b1697..7794232196 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -44,7 +44,7 @@ var ANIM_VARS = [ "spine2Type", //"hipsType", "spine2Position", - "spine2Rotation", + "spine2Rotation" //"hipsPosition", //"hipsRotation" ]; @@ -60,7 +60,7 @@ var handlerId = MyAvatar.addAnimationStateHandler(function (props) { //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // result.spine2Type = ikTypes.Spline; - result.spine2Position = spine2PositionRigSpace;// { x: 0, y: 1.3, z: 0 }; + result.spine2Position = spine2PositionRigSpace; // { x: 0, y: 1.3, z: 0 }; result.spine2Rotation = spine2Rotation; return result; @@ -267,18 +267,34 @@ function computeHandAzimuths() { lHandMinusHead.y = 0.0; var rHandMinusHead = Vec3.subtract(rightHand, head); rHandMinusHead.y = 0.0; - print(JSON.stringify(leftHand)); - print(JSON.stringify(head)); + //print(JSON.stringify(leftHand)); + //print(JSON.stringify(head)); var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); - hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, 0.99); - hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, 0.99); + hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, AVERAGING_RATE); + hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, AVERAGING_RATE); // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); - var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; - print(leftRightMidpoint); + var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHand).y + Quat.safeEulerAngles(hipToRHand).y) / 2.0; + var leftRightMidpointAverage = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; + + // limit the angle because we are flipped by 180, fix this tomorrow. + // print(leftRightMidpointAverage/180.0); + // print("threshold value " + angleThresholdProperty.value); + // get it into radians too!! + if ((Math.abs(leftRightMidpointAverage/180.0) * Math.PI) > angleThresholdProperty.value) { + print("recenter the feet under the head"); + MyAvatar.triggerRotationRecenter(); + } + + var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); + var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); + var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation); + DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); + + //print(leftRightMidpoint); return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); @@ -291,12 +307,10 @@ function update(dt) { currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand); currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand); - print(JSON.stringify(currentStateReadings.head)); + //print(JSON.stringify(currentStateReadings.head)); var latestSpineRotation = computeHandAzimuths(); - var zAxisSpineRotation = Vec3.multiplyQbyV(latestSpineRotation, { x: 0, y: 0, z: 1 }); - var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.rotation, zAxisSpineRotation); - DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisSpineRotation), { x: 1, y: 0, z: 0 }); + spine2Rotation = latestSpineRotation; var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos); From 579b2435c27c2733dbe3f421dc0b4d66cd81ea31 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 22 Aug 2018 11:04:26 -0700 Subject: [PATCH 123/207] don't squash delete events during shutdown so that QWebEngine process gets destroyed --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 396c6cbcac..cc24511afd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -3667,7 +3667,7 @@ bool Application::event(QEvent* event) { bool Application::eventFilter(QObject* object, QEvent* event) { - if (_aboutToQuit) { + if (_aboutToQuit && event->type() != QEvent::DeferredDelete && event->type() != QEvent::Destroy) { return true; } From 3b5ba71163a66f95b20320f0fa5b02d6833f519e Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 22 Aug 2018 12:23:24 -0700 Subject: [PATCH 124/207] fix a crash in ESS from missing dependency --- .../src/scripts/EntityScriptServer.cpp | 24 ++++++++++++------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 05002828f5..1d46b6040e 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -55,7 +56,8 @@ int EntityScriptServer::_entitiesScriptEngineCount = 0; EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) { qInstallMessageHandler(messageHandler); - DependencyManager::get()->setPacketSender(&_entityEditSender); + DependencyManager::set(false)->setPacketSender(&_entityEditSender); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -559,14 +561,16 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer mess void EntityScriptServer::aboutToFinish() { shutdownScriptEngine(); - auto entityScriptingInterface = DependencyManager::get(); - // our entity tree is going to go away so tell that to the EntityScriptingInterface - entityScriptingInterface->setEntityTree(nullptr); + { + auto entityScriptingInterface = DependencyManager::get(); + // our entity tree is going to go away so tell that to the EntityScriptingInterface + entityScriptingInterface->setEntityTree(nullptr); - // Should always be true as they are singletons. - if (entityScriptingInterface->getPacketSender() == &_entityEditSender) { - // The packet sender is about to go away. - entityScriptingInterface->setPacketSender(nullptr); + // Should always be true as they are singletons. + if (entityScriptingInterface->getPacketSender() == &_entityEditSender) { + // The packet sender is about to go away. + entityScriptingInterface->setPacketSender(nullptr); + } } DependencyManager::destroy(); @@ -575,8 +579,12 @@ void EntityScriptServer::aboutToFinish() { DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); + // cleanup the AudioInjectorManager (and any still running injectors) DependencyManager::destroy(); + DependencyManager::destroy(); DependencyManager::destroy(); From 1e3ca6f902fc9df3cab552c5ebb9a260ddb685df Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 12:57:06 -0700 Subject: [PATCH 125/207] added quicker follow code and added head to rotate calculation --- interface/src/avatar/MyAvatar.cpp | 21 +++++++++------- libraries/shared/src/AvatarConstants.h | 2 +- scripts/developer/rotateApp.js | 33 +++++++++++++++++++------- 3 files changed, 38 insertions(+), 18 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 04a6d58aae..c79f589b15 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3797,7 +3797,7 @@ void MyAvatar::lateUpdatePalms() { } -static const float FOLLOW_TIME = 0.5f; +static const float FOLLOW_TIME = 0.1f; MyAvatar::FollowHelper::FollowHelper() { deactivate(); @@ -3902,8 +3902,13 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head")); glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); + //if (!isActive(Horizontal) && + // (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) { + // myAvatar.setResetMode(true); + // stepDetected = true; + //} if (!isActive(Horizontal) && - (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) { + (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) { myAvatar.setResetMode(true); stepDetected = true; } @@ -3925,10 +3930,15 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { - if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + if (!isActive(Rotation) && getForceActivateRotation()) { activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + setForceActivateRotation(false); } + //if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + // activate(Rotation); + // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + //} if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); @@ -3937,11 +3947,6 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } } - if (!isActive(Rotation) && getForceActivateRotation()) { - activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); - setForceActivateRotation(false); - } } else { if (!isActive(Horizontal) && (shouldActivateHorizontal(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Horizontal); diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index 39117de7ef..b97f586d32 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -34,7 +34,7 @@ const float DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 3.3f; const float DEFAULT_HEAD_VELOCITY_STEPPING_THRESHOLD = 0.18f; const float DEFAULT_HEAD_PITCH_STEPPING_TOLERANCE = 7.0f; const float DEFAULT_HEAD_ROLL_STEPPING_TOLERANCE = 7.0f; -const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.07f; +const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.05f; const float DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR = 0.5f; const float DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR = 2.0f; const float DEFAULT_AVATAR_HIPS_MASS = 40.0f; diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index 7794232196..4d18910335 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -259,7 +259,7 @@ function limitAngle(angle) { return (angle + 180) % 360 - 180; } -function computeHandAzimuths() { +function computeHandAzimuths(timeElapsed) { var leftHand = currentStateReadings.lhandPose.translation; var rightHand = currentStateReadings.rhandPose.translation; var head = currentStateReadings.headPose.translation; @@ -272,8 +272,9 @@ function computeHandAzimuths() { var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); - hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, AVERAGING_RATE); - hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, AVERAGING_RATE); + var tau = timeElapsed / filterLengthProperty.value; + hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, tau); + hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, tau); // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); @@ -283,11 +284,7 @@ function computeHandAzimuths() { // limit the angle because we are flipped by 180, fix this tomorrow. // print(leftRightMidpointAverage/180.0); // print("threshold value " + angleThresholdProperty.value); - // get it into radians too!! - if ((Math.abs(leftRightMidpointAverage/180.0) * Math.PI) > angleThresholdProperty.value) { - print("recenter the feet under the head"); - MyAvatar.triggerRotationRecenter(); - } + var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); @@ -295,6 +292,22 @@ function computeHandAzimuths() { DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); //print(leftRightMidpoint); + + var headPoseRigSpace = Quat.multiply(CHANGE_OF_BASIS_ROTATION, currentStateReadings.head.rotation); + headPoseAverageOrientation = Quat.slerp(headPoseAverageOrientation, headPoseRigSpace, tau); + var headPoseAverageEulers = Quat.safeEulerAngles(headPoseAverageOrientation); + + // get it into radians too!! + // average head and hands 50/50 + print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y) + var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; + if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { + print("recenter the feet under the head"); + MyAvatar.triggerRotationRecenter(); + hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; + hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; + headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; + } return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); @@ -309,7 +322,9 @@ function update(dt) { //print(JSON.stringify(currentStateReadings.head)); - var latestSpineRotation = computeHandAzimuths(); + + + var latestSpineRotation = computeHandAzimuths(dt); spine2Rotation = latestSpineRotation; var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); From 01e8fd66a3afb09e1c685a48e86c7e2c5be926d6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 22 Aug 2018 12:41:13 -0700 Subject: [PATCH 126/207] fix script engine(s) cleanup from ESS --- .../src/scripts/EntityScriptServer.cpp | 34 +++++++++++-------- libraries/script-engine/src/ScriptEngine.cpp | 4 +-- libraries/script-engine/src/ScriptEngine.h | 2 -- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 1d46b6040e..586931d403 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -457,8 +457,11 @@ void EntityScriptServer::resetEntitiesScriptEngine() { auto newEngineSP = qSharedPointerCast(newEngine); DependencyManager::get()->setEntitiesScriptEngine(newEngineSP); - disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, - this, &EntityScriptServer::updateEntityPPS); + if (_entitiesScriptEngine) { + disconnect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, + this, &EntityScriptServer::updateEntityPPS); + } + _entitiesScriptEngine.swap(newEngine); connect(_entitiesScriptEngine.data(), &ScriptEngine::entityScriptDetailsUpdated, this, &EntityScriptServer::updateEntityPPS); @@ -489,6 +492,21 @@ void EntityScriptServer::shutdownScriptEngine() { _shuttingDown = true; clear(); // always clear() on shutdown + + auto scriptEngines = DependencyManager::get(); + scriptEngines->shutdownScripting(); + + _entitiesScriptEngine.clear(); + + auto entityScriptingInterface = DependencyManager::get(); + // our entity tree is going to go away so tell that to the EntityScriptingInterface + entityScriptingInterface->setEntityTree(nullptr); + + // Should always be true as they are singletons. + if (entityScriptingInterface->getPacketSender() == &_entityEditSender) { + // The packet sender is about to go away. + entityScriptingInterface->setPacketSender(nullptr); + } } void EntityScriptServer::addingEntity(const EntityItemID& entityID) { @@ -561,18 +579,6 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer mess void EntityScriptServer::aboutToFinish() { shutdownScriptEngine(); - { - auto entityScriptingInterface = DependencyManager::get(); - // our entity tree is going to go away so tell that to the EntityScriptingInterface - entityScriptingInterface->setEntityTree(nullptr); - - // Should always be true as they are singletons. - if (entityScriptingInterface->getPacketSender() == &_entityEditSender) { - // The packet sender is about to go away. - entityScriptingInterface->setPacketSender(nullptr); - } - } - DependencyManager::destroy(); DependencyManager::get()->cleanup(); diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index 105742db35..ce4ec89950 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -176,9 +176,7 @@ ScriptEngine::ScriptEngine(Context context, const QString& scriptContents, const _timerFunctionMap(), _fileNameString(fileNameString), _arrayBufferClass(new ArrayBufferClass(this)), - _assetScriptingInterface(new AssetScriptingInterface(this)), - // don't delete `ScriptEngines` until all `ScriptEngine`s are gone - _scriptEngines(DependencyManager::get()) + _assetScriptingInterface(new AssetScriptingInterface(this)) { switch (_context) { case Context::CLIENT_SCRIPT: diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h index 1791360a45..94b50bfd2c 100644 --- a/libraries/script-engine/src/ScriptEngine.h +++ b/libraries/script-engine/src/ScriptEngine.h @@ -806,8 +806,6 @@ protected: static const QString _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS; Setting::Handle _enableExtendedJSExceptions { _SETTINGS_ENABLE_EXTENDED_EXCEPTIONS, true }; - - QSharedPointer _scriptEngines; }; ScriptEnginePointer scriptEngineFactory(ScriptEngine::Context context, From 9dfaf32b7240f22e88ea611966e3a4373b2d552a Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 14:12:19 -0700 Subject: [PATCH 127/207] fixed the step animation but it still needs work --- interface/src/avatar/MyAvatar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 46a5cbe414..44ee5e30c6 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3933,6 +3933,9 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { + const float VELOCITY_THRESHHOLD = 1.0f; + float currentVelocity = glm::length(myAvatar.getLocalVelocity() / myAvatar.getSensorToWorldScale()); + if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { @@ -3946,7 +3949,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); //} if (myAvatar.getCenterOfGravityModelEnabled()) { - if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { + if ((!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) || (isActive(Horizontal) && (currentVelocity > VELOCITY_THRESHHOLD))) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); From 73cca4294002f99c8cc782ca0439787ac5c8966c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 13 Aug 2018 12:35:26 -0700 Subject: [PATCH 128/207] initial prototype --- interface/src/commerce/Ledger.cpp | 23 +++++++++++++++++++++-- interface/src/commerce/Wallet.cpp | 26 ++++++++++++++++++++++++++ interface/src/commerce/Wallet.h | 4 ++++ 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 702251f867..bcd96fdb06 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -125,8 +125,19 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { emit receiveAtResult(result); return false; // We know right away that we will fail, so tell the caller. } - - signedSend("public_key", hfc_key.toUtf8(), signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); + auto wallet = DependencyManager::get(); + QByteArray locker = wallet->getWallet(); + if (locker.isEmpty()) { + signedSend("public_key", hfc_key.toUtf8(), signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); + } else { + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["locker"] = QString::fromUtf8(locker); + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + qCDebug(commerce) << "FIXME transactionString" << transactionString; + signedSend("text", transactionString, signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); + } return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in. } @@ -277,17 +288,22 @@ void Ledger::accountSuccess(QNetworkReply* reply) { // lets set the appropriate stuff in the wallet now auto wallet = DependencyManager::get(); QByteArray response = reply->readAll(); + qCDebug(commerce) << "FIXME accountSuccess got" << response; QJsonObject data = QJsonDocument::fromJson(response).object()["data"].toObject(); auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8()); auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8()); auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8()); QString remotePublicKey = data["public_key"].toString(); + const QByteArray locker = data["locker"].toString().toUtf8(); bool isOverride = wallet->wasSoftReset(); wallet->setSalt(salt); wallet->setIv(iv); wallet->setCKey(ckey); + if (!locker.isEmpty()) { + wallet->setWallet(locker); + } QString keyStatus = "ok"; QStringList localPublicKeys = wallet->listPublicKeys(); @@ -301,6 +317,9 @@ void Ledger::accountSuccess(QNetworkReply* reply) { keyStatus = "preexisting"; } else if (localPublicKeys.first() != remotePublicKey) { keyStatus = "conflicting"; + } else if (locker.isEmpty()) { + QString key = localPublicKeys.first(); + receiveAt(key, key); } } diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index ef6b4654f5..197e6f60b9 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -190,6 +190,32 @@ bool writeKeys(const char* filename, EC_KEY* keys) { return retval; } +bool Wallet::setWallet(const QByteArray& wallet) { + QFile file(keyFilePath()); + if (!file.open(QIODevice::WriteOnly)) { + qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath(); + return false; + } + if (file.write(wallet) != wallet.count()) { + qCCritical(commerce) << "Unable to write wallet in" << keyFilePath(); + return false; + } + file.close(); + qCDebug(commerce) << "FIXME wrote" << wallet.count() << "to" << keyFilePath(); + return true; +} +QByteArray Wallet::getWallet() { + QFile file(keyFilePath()); + if (!file.open(QIODevice::ReadOnly)) { + qCInfo(commerce) << "No existing wallet in" << keyFilePath(); + return QByteArray(); + } + QByteArray wallet = file.readAll(); + file.close(); + qCDebug(commerce) << "FIXME read" << wallet.count() << "from" << keyFilePath(); + return wallet; +} + QPair generateECKeypair() { EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 665afd9a23..4e7608b096 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -44,6 +44,10 @@ public: void setCKey(const QByteArray& ckey) { _ckey = ckey; } QByteArray getCKey() { return _ckey; } + // FIXME protect more + bool setWallet(const QByteArray& wallet); + QByteArray getWallet(); + bool setPassphrase(const QString& passphrase); QString* getPassphrase() { return _passphrase; } bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); } From ef9cc782b91bba76aa31a629a1a6ba3beb6c6f3e Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Mon, 13 Aug 2018 14:30:36 -0700 Subject: [PATCH 129/207] no need to keep old format in client --- interface/src/commerce/Ledger.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index bcd96fdb06..c0bda8493a 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -127,17 +127,13 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { } auto wallet = DependencyManager::get(); QByteArray locker = wallet->getWallet(); - if (locker.isEmpty()) { - signedSend("public_key", hfc_key.toUtf8(), signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); - } else { - QJsonObject transaction; - transaction["public_key"] = hfc_key; - transaction["locker"] = QString::fromUtf8(locker); - QJsonDocument transactionDoc{ transaction }; - auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); - qCDebug(commerce) << "FIXME transactionString" << transactionString; - signedSend("text", transactionString, signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); - } + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["locker"] = QString::fromUtf8(locker); + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + qCDebug(commerce) << "FIXME transactionString" << transactionString; + signedSend("text", transactionString, signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in. } From ef1aacd226b96eb3d88ce7b1826a9d1f74a3d83c Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Tue, 14 Aug 2018 13:14:48 -0700 Subject: [PATCH 130/207] handle password changes --- interface/src/commerce/Ledger.cpp | 24 +++++++++++++++--------- interface/src/commerce/Ledger.h | 3 ++- interface/src/commerce/Wallet.cpp | 13 ++++++++++++- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index c0bda8493a..4ab76c42f5 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -117,7 +117,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure); } -bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { +bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key, const QByteArray& locker) { auto accountManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { qCWarning(commerce) << "Cannot set receiveAt when not logged in."; @@ -125,8 +125,6 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { emit receiveAtResult(result); return false; // We know right away that we will fail, so tell the caller. } - auto wallet = DependencyManager::get(); - QByteArray locker = wallet->getWallet(); QJsonObject transaction; transaction["public_key"] = hfc_key; transaction["locker"] = QString::fromUtf8(locker); @@ -137,6 +135,16 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in. } +bool Ledger::receiveAt() { + auto wallet = DependencyManager::get(); + auto keys = wallet->listPublicKeys(); + if (keys.isEmpty()) { + return false; + } + auto key = keys.first(); + return receiveAt(key, key, wallet->getWallet()); +} + void Ledger::balance(const QStringList& keys) { keysQuery("balance", "balanceSuccess", "balanceFailure"); } @@ -304,18 +312,16 @@ void Ledger::accountSuccess(QNetworkReply* reply) { QString keyStatus = "ok"; QStringList localPublicKeys = wallet->listPublicKeys(); if (remotePublicKey.isEmpty() || isOverride) { - if (!localPublicKeys.isEmpty()) { - QString key = localPublicKeys.first(); - receiveAt(key, key); + if (!localPublicKeys.isEmpty()) { // Let the metaverse know about a local wallet. + receiveAt(); } } else { if (localPublicKeys.isEmpty()) { keyStatus = "preexisting"; } else if (localPublicKeys.first() != remotePublicKey) { keyStatus = "conflicting"; - } else if (locker.isEmpty()) { - QString key = localPublicKeys.first(); - receiveAt(key, key); + } else if (locker.isEmpty()) { // Matches metaverse data, but we haven't lockered it yet. + receiveAt(); } } diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index ba2f167f4b..427395ee11 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -26,7 +26,8 @@ class Ledger : public QObject, public Dependency { public: void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false); - bool receiveAt(const QString& hfc_key, const QString& signing_key); + bool receiveAt(const QString& hfc_key, const QString& signing_key, const QByteArray& locker); + bool receiveAt(); void balance(const QStringList& keys); void inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage); void history(const QStringList& keys, const int& pageNumber, const int& itemsPerPage); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 197e6f60b9..fb6a0c9b03 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -602,7 +602,7 @@ bool Wallet::generateKeyPair() { // 2. It is maximally private, and we can step back from that later if desired. // 3. It maximally exercises all the machinery, so we are most likely to surface issues now. auto ledger = DependencyManager::get(); - return ledger->receiveAt(key, key); + return ledger->receiveAt(key, key, getWallet()); } QStringList Wallet::listPublicKeys() { @@ -741,6 +741,11 @@ QString Wallet::getKeyFilePath() { bool Wallet::writeWallet(const QString& newPassphrase) { EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str()); + auto ledger = DependencyManager::get(); + // Remove any existing locker, because it will be out of date. + if (!_publicKeys.isEmpty() && !ledger->receiveAt(_publicKeys.first(), _publicKeys.first(), QByteArray())) { + return false; // FIXME: receiveAt could fail asynchronously. + } if (keys) { // we read successfully, so now write to a new temp file QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp")); @@ -748,6 +753,7 @@ bool Wallet::writeWallet(const QString& newPassphrase) { if (!newPassphrase.isEmpty()) { setPassphrase(newPassphrase); } + if (writeKeys(tempFileName.toStdString().c_str(), keys)) { if (writeSecurityImage(_securityImage, tempFileName)) { // ok, now move the temp file to the correct spot @@ -755,6 +761,11 @@ bool Wallet::writeWallet(const QString& newPassphrase) { QFile(tempFileName).rename(QString(keyFilePath())); qCDebug(commerce) << "wallet written successfully"; emit keyFilePathIfExistsResult(getKeyFilePath()); + if (!walletIsAuthenticatedWithPassphrase() || !ledger->receiveAt()) { + // FIXME: Should we fail the whole operation? + // Tricky, because we'll need the the key and file from the TEMP location... + qCWarning(commerce) << "Failed to update locker"; + } return true; } else { qCDebug(commerce) << "couldn't write security image to temp wallet"; From 13629f6870fe60b0f094c316b0b2f04e200602bc Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 15 Aug 2018 11:23:50 -0700 Subject: [PATCH 131/207] bypass setup and password, and convert old wallets to account based before lockering --- .../qml/hifi/commerce/wallet/Help.qml | 11 +++++---- .../qml/hifi/commerce/wallet/Wallet.qml | 24 ++++++++++++++++--- interface/src/commerce/Ledger.cpp | 1 + interface/src/commerce/Wallet.cpp | 24 +++++++++++++++---- 4 files changed, 48 insertions(+), 12 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index b453509712..e38f23e5a5 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -114,11 +114,9 @@ In your Wallet's Send Money tab, choose from your list of connections, or choose ListElement { isExpanded: false; question: "What is a Security Pic?" - answer: "Your Security Pic is an encrypted image that you select during Wallet Setup. \ -It acts as an extra layer of Wallet security. \ -When you see your Security Pic, you know that your actions and data are securely making use of your private keys.\ -

Don't enter your passphrase anywhere that doesn't display your Security Pic! \ -If you don't see your Security Pic on a page that requests your Wallet passphrase, someone untrustworthy may be trying to access your Wallet."; + answer: "Your Security Pic acts as an extra layer of Wallet security. \ +When you see your Security Pic, you know that your actions and data are securely making use of your account. \ +Tap here to change your Security Pic."; } ListElement { isExpanded: false; @@ -260,6 +258,9 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta } } else if (link === "#support") { Qt.openUrlExternally("mailto:support@highfidelity.com"); + } else if (link === "#securitypic") { + console.log("HRS FIXME here"); + sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 603d7fb676..f48553a4d0 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -257,9 +257,9 @@ Rectangle { Connections { onSendSignalToWallet: { if (msg.method === 'walletSecurity_changeSecurityImageCancelled') { - root.activeView = "security"; + root.activeView = "walletHome"; // was "security"; } else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') { - root.activeView = "security"; + root.activeView = "walletHome"; // was "security"; } else { sendToScript(msg); } @@ -399,6 +399,9 @@ Rectangle { onSendSignalToWallet: { if (msg.method === 'walletReset' || msg.method === 'passphraseReset') { sendToScript(msg); + } else if (msg.method === 'walletSecurity_changeSecurityImage') { + securityImageChange.initModel(); + root.activeView = "securityImageChange"; } } } @@ -607,7 +610,7 @@ Rectangle { } RalewaySemiBold { - text: "SECURITY"; + text: "PURCHASES"; // was "SECURITY"; // Text size size: 16; // Anchors @@ -629,8 +632,11 @@ Rectangle { anchors.fill: parent; hoverEnabled: enabled; onClicked: { + sendToScript({method: 'goToPurchases_fromWalletHome'}); + /* was root.activeView = "security"; tabButtonsContainer.resetTabButtonColors(); + */ } onEntered: parent.color = hifi.colors.blueHighlight; onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black; @@ -803,12 +809,24 @@ Rectangle { } function walletResetSetup() { + /* Bypass all this and do it automatically root.activeView = "walletSetup"; var timestamp = new Date(); walletSetup.startingTimestamp = timestamp; walletSetup.setupAttemptID = generateUUID(); UserActivityLogger.commerceWalletSetupStarted(timestamp, walletSetup.setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app", (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '')); + */ + + var randomNumber = Math.floor(Math.random() * 34) + 1; + var securityImagePath = "images/" + addLeadingZero(randomNumber) + ".jpg"; + Commerce.getWalletAuthenticatedStatus(); // before writing security image, ensures that salt/account password is set. + Commerce.chooseSecurityImage(securityImagePath); + Commerce.generateKeyPair(); + } + + function addLeadingZero(n) { + return n < 10 ? '0' + n : '' + n; } function followReferrer(msg) { diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 4ab76c42f5..18d22057fd 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -307,6 +307,7 @@ void Ledger::accountSuccess(QNetworkReply* reply) { wallet->setCKey(ckey); if (!locker.isEmpty()) { wallet->setWallet(locker); + wallet->setPassphrase("ACCOUNT"); // We only locker wallets that have been converted to account-based auth. } QString keyStatus = "ok"; diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index fb6a0c9b03..84e90544ff 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -131,7 +131,7 @@ bool Wallet::writeBackupInstructions() { QFile outputFile(outputFilename); bool retval = false; - if (getKeyFilePath() == "") + if (getKeyFilePath().isEmpty()) { return false; } @@ -360,7 +360,7 @@ Wallet::Wallet() { uint status; QString keyStatus = result.contains("data") ? result["data"].toObject()["keyStatus"].toString() : ""; - if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) { + if (wallet->getKeyFilePath().isEmpty() || !wallet->getSecurityImage()) { if (keyStatus == "preexisting") { status = (uint) WalletStatus::WALLET_STATUS_PREEXISTING; } else{ @@ -550,15 +550,23 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { // FIXME: initialize OpenSSL elsewhere soon initialize(); + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: checking" << (!_passphrase || !_passphrase->isEmpty()); // this should always be false if we don't have a passphrase // cached yet if (!_passphrase || _passphrase->isEmpty()) { - return false; + if (!getKeyFilePath().isEmpty()) { // If file exists, then it is an old school file that has not been lockered. Must get user's passphrase. + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: No passphrase, but there is an existing wallet."; + return false; + } else { + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: New setup."; + setPassphrase("ACCOUNT"); // Going forward, consider this an account-based client. + } } if (_publicKeys.count() > 0) { // we _must_ be authenticated if the publicKeys are there DependencyManager::get()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY); + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: wallet was ready"; return true; } @@ -571,10 +579,15 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { // be sure to add the public key so we don't do this over and over _publicKeys.push_back(publicKey.toBase64()); + + if (*_passphrase != "ACCOUNT") { + changePassphrase("ACCOUNT"); // Rewrites with salt and constant, and will be lockered that way. + } + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: wallet now ready"; return true; } } - + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: wallet not ready"; return false; } @@ -585,6 +598,7 @@ bool Wallet::generateKeyPair() { qCInfo(commerce) << "Generating keypair."; auto keyPair = generateECKeypair(); if (!keyPair.first) { + qCWarning(commerce) << "Empty keypair"; return false; } @@ -692,11 +706,13 @@ void Wallet::chooseSecurityImage(const QString& filename) { // there _is_ a keyfile, we need to update it (similar to changing the // passphrase, we need to do so into a temp file and move it). if (!QFile(keyFilePath()).exists()) { + qCDebug(commerce) << "initial security pic set for empty wallet"; emit securityImageResult(true); return; } bool success = writeWallet(); + qCDebug(commerce) << "updated security pic" << success; emit securityImageResult(success); } From 88a3505b6a308cf53d3ad0b63af42f506df1e260 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Wed, 22 Aug 2018 12:59:43 -0700 Subject: [PATCH 132/207] cleanup --- .../qml/hifi/commerce/wallet/Help.qml | 19 +++++++++---------- .../qml/hifi/commerce/wallet/Security.qml | 7 +++++-- .../qml/hifi/commerce/wallet/Wallet.qml | 9 +++------ interface/src/commerce/Ledger.cpp | 6 ++++-- interface/src/commerce/Wallet.cpp | 2 -- interface/src/commerce/Wallet.h | 8 ++++---- 6 files changed, 25 insertions(+), 26 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index e38f23e5a5..1c37ded4ee 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -64,7 +64,7 @@ Item { answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ Get it by going to

BankOfHighFidelity. and meeting with the banker!"; } - ListElement { + /* ListElement { isExpanded: false; question: "What are private keys and where are they stored?"; answer: @@ -74,16 +74,16 @@ After wallet setup, a hifikey file is stored on your computer in High Fidelity I Your hifikey file contains your private key and is protected by your wallet passphrase. \

It is very important to back up your hifikey file! \ Tap here to open the folder where your HifiKeys are stored on your main display." - } - ListElement { + }*/ + /*ListElement { isExpanded: false; question: "How do I back up my private keys?"; answer: "You can back up your hifikey file (which contains your private key and is encrypted using your wallet passphrase) by copying it to a USB flash drive, or to a service like Dropbox or Google Drive. \ Restore your hifikey file by replacing the file in Interface's AppData directory with your backup copy. \ Others with access to your back up should not be able to spend your HFC without your passphrase. \ Tap here to open the folder where your HifiKeys are stored on your main display."; - } - ListElement { + }*/ + /*ListElement { isExpanded: false; question: "What happens if I lose my private keys?"; answer: "We cannot stress enough that you should keep a backup! For security reasons, High Fidelity does not keep a copy, and cannot restore it for you. \ @@ -94,8 +94,8 @@ Here are some things to try:

    \
  • If you are a developer and have installed multiple builds of High Fidelity, your hifikey file might be in another folder
  • \


As a last resort, you can set up your Wallet again and generate a new hifikey file. \ Unfortunately, this means you will start with 0 HFC and your purchased items will not be transferred over."; - } - ListElement { + }*/ + /*ListElement { isExpanded: false; question: "What if I forget my wallet passphrase?"; answer: "Your wallet passphrase is used to encrypt your private keys. Please write it down and store it securely! \ @@ -104,7 +104,7 @@ You will also no longer have access to the contents of your Wallet or My Purchas For security reasons, High Fidelity does not keep a copy of your passphrase, and can't restore it for you. \

If you still cannot remember your wallet passphrase, you can set up your Wallet again and generate a new hifikey file. \ Unfortunately, this means you will start with 0 HFC and your purchased items will not be transferred over."; - } + }*/ ListElement { isExpanded: false; question: "How do I send HFC to other people?"; @@ -116,7 +116,7 @@ In your Wallet's Send Money tab, choose from your list of connections, or choose question: "What is a Security Pic?" answer: "Your Security Pic acts as an extra layer of Wallet security. \ When you see your Security Pic, you know that your actions and data are securely making use of your account. \ -Tap here to change your Security Pic."; +

Tap here to change your Security Pic."; } ListElement { isExpanded: false; @@ -259,7 +259,6 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta } else if (link === "#support") { Qt.openUrlExternally("mailto:support@highfidelity.com"); } else if (link === "#securitypic") { - console.log("HRS FIXME here"); sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index 216d621bf8..08eb94a23c 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -88,6 +88,7 @@ Item { color: hifi.colors.faintGray; } + /* Item { id: changePassphraseContainer; anchors.top: securityTextSeparator.bottom; @@ -154,10 +155,11 @@ Item { // Style color: hifi.colors.faintGray; } + */ Item { id: changeSecurityImageContainer; - anchors.top: changePassphraseSeparator.bottom; + anchors.top: securityTextSeparator.bottom; // changePassphraseSeparator.bottom; anchors.topMargin: 8; anchors.left: parent.left; anchors.leftMargin: 40; @@ -207,7 +209,7 @@ Item { } } } - + /* Rectangle { id: privateKeysSeparator; // Size @@ -344,6 +346,7 @@ Item { } } } + */ } // diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index f48553a4d0..ffd06cb4a8 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -257,9 +257,9 @@ Rectangle { Connections { onSendSignalToWallet: { if (msg.method === 'walletSecurity_changeSecurityImageCancelled') { - root.activeView = "walletHome"; // was "security"; + root.activeView = "security"; } else if (msg.method === 'walletSecurity_changeSecurityImageSuccess') { - root.activeView = "walletHome"; // was "security"; + root.activeView = "security"; } else { sendToScript(msg); } @@ -610,7 +610,7 @@ Rectangle { } RalewaySemiBold { - text: "PURCHASES"; // was "SECURITY"; + text: "SECURITY"; // Text size size: 16; // Anchors @@ -632,11 +632,8 @@ Rectangle { anchors.fill: parent; hoverEnabled: enabled; onClicked: { - sendToScript({method: 'goToPurchases_fromWalletHome'}); - /* was root.activeView = "security"; tabButtonsContainer.resetTabButtonColors(); - */ } onEntered: parent.color = hifi.colors.blueHighlight; onExited: parent.color = root.activeView === "security" ? hifi.colors.blueAccent : hifi.colors.black; diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 18d22057fd..67303f2a9b 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -31,7 +31,9 @@ QJsonObject Ledger::apiResponse(const QString& label, QNetworkReply* reply) { QByteArray response = reply->readAll(); QJsonObject data = QJsonDocument::fromJson(response).object(); +#if defined(DEV_BUILD) // Don't expose user's personal data in the wild. But during development this can be handy. qInfo(commerce) << label << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact); +#endif return data; } // Non-200 responses are not json: @@ -69,7 +71,9 @@ void Ledger::send(const QString& endpoint, const QString& success, const QString auto accountManager = DependencyManager::get(); const QString URL = "/api/v1/commerce/"; JSONCallbackParameters callbackParams(this, success, fail); +#if defined(DEV_BUILD) // Don't expose user's personal data in the wild. But during development this can be handy. qCInfo(commerce) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact); +#endif accountManager->sendRequest(URL + endpoint, authType, method, @@ -130,7 +134,6 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key, const transaction["locker"] = QString::fromUtf8(locker); QJsonDocument transactionDoc{ transaction }; auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); - qCDebug(commerce) << "FIXME transactionString" << transactionString; signedSend("text", transactionString, signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in. } @@ -292,7 +295,6 @@ void Ledger::accountSuccess(QNetworkReply* reply) { // lets set the appropriate stuff in the wallet now auto wallet = DependencyManager::get(); QByteArray response = reply->readAll(); - qCDebug(commerce) << "FIXME accountSuccess got" << response; QJsonObject data = QJsonDocument::fromJson(response).object()["data"].toObject(); auto salt = QByteArray::fromBase64(data["salt"].toString().toUtf8()); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index 84e90544ff..5b8417be7c 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -201,7 +201,6 @@ bool Wallet::setWallet(const QByteArray& wallet) { return false; } file.close(); - qCDebug(commerce) << "FIXME wrote" << wallet.count() << "to" << keyFilePath(); return true; } QByteArray Wallet::getWallet() { @@ -212,7 +211,6 @@ QByteArray Wallet::getWallet() { } QByteArray wallet = file.readAll(); file.close(); - qCDebug(commerce) << "FIXME read" << wallet.count() << "from" << keyFilePath(); return wallet; } diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 4e7608b096..c096713058 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -44,10 +44,6 @@ public: void setCKey(const QByteArray& ckey) { _ckey = ckey; } QByteArray getCKey() { return _ckey; } - // FIXME protect more - bool setWallet(const QByteArray& wallet); - QByteArray getWallet(); - bool setPassphrase(const QString& passphrase); QString* getPassphrase() { return _passphrase; } bool getPassphraseIsCached() { return !(_passphrase->isEmpty()); } @@ -77,6 +73,7 @@ private slots: void handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: + friend class Ledger; QStringList _publicKeys{}; QPixmap* _securityImage { nullptr }; QByteArray _salt; @@ -91,6 +88,9 @@ private: bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen); bool writeBackupInstructions(); + bool setWallet(const QByteArray& wallet); + QByteArray getWallet(); + void account(); }; From b3d127c9531563900998973e850716fb0edbc5a7 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Wed, 22 Aug 2018 14:31:34 -0700 Subject: [PATCH 133/207] Fix flickering --- interface/src/avatar/MyAvatar.cpp | 2 +- .../src/avatars-renderer/Avatar.cpp | 5 +++-- libraries/avatars/src/AvatarData.cpp | 18 +++++++++--------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index ba9d15159b..8b139ee364 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -184,7 +184,6 @@ MyAvatar::MyAvatar(QThread* thread) : if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } - createRecordingIDs(); _previousCollisionGroup = _characterController.computeCollisionGroup(); _characterController.setCollisionless(true); } else { @@ -203,6 +202,7 @@ MyAvatar::MyAvatar(QThread* thread) : connect(recorder.data(), &Recorder::recordingStateChanged, [=] { if (recorder->isRecording()) { + createRecordingIDs(); setRecordingBasis(); } else { clearRecordingBasis(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0f2c936507..93de1ab29b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -236,8 +236,6 @@ void Avatar::updateAvatarEntities() { return; } - createRecordingIDs(); - if (getID().isNull() || getID() == AVATAR_SELF_ID || DependencyManager::get()->getSessionUUID() == QUuid()) { @@ -368,6 +366,9 @@ void Avatar::updateAvatarEntities() { } } } + if (avatarEntities.size() != _avatarEntityForRecording.size()) { + createRecordingIDs(); + } }); setAvatarEntityDataChanged(false); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c97051eddf..6fa5ba2c5b 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2443,15 +2443,15 @@ void AvatarData::fromJson(const QJsonObject& json, bool useFrameSkeleton) { } if (json.contains(JSON_AVATAR_ENTITIES) && json[JSON_AVATAR_ENTITIES].isArray()) { - QJsonArray attachmentsJson = json[JSON_AVATAR_ENTITIES].toArray(); - for (auto attachmentJson : attachmentsJson) { - if (attachmentJson.isObject()) { - QVariantMap entityData = attachmentJson.toObject().toVariantMap(); - QUuid entityID = entityData.value("id").toUuid(); - QByteArray properties = QByteArray::fromBase64(entityData.value("properties").toByteArray()); - updateAvatarEntity(entityID, properties); - } - } + QJsonArray attachmentsJson = json[JSON_AVATAR_ENTITIES].toArray(); + for (auto attachmentJson : attachmentsJson) { + if (attachmentJson.isObject()) { + QVariantMap entityData = attachmentJson.toObject().toVariantMap(); + QUuid entityID = entityData.value("id").toUuid(); + QByteArray properties = QByteArray::fromBase64(entityData.value("properties").toByteArray()); + updateAvatarEntity(entityID, properties); + } + } } if (json.contains(JSON_AVATAR_JOINT_ARRAY)) { From dd098f05c2a8e1c3239548989e6c95a9f9b2d36d Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 22 Aug 2018 14:54:07 -0700 Subject: [PATCH 134/207] Change EntityQueryInitialResultsComplete protocol to use exclusive sequence number --- assignment-client/src/entities/EntityTreeSendThread.cpp | 2 +- interface/src/octree/SafeLanding.cpp | 4 ++-- interface/src/octree/SafeLanding.h | 2 +- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp index f7ca05fbf2..8b7c8771e8 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -164,7 +164,7 @@ bool EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O // Send EntityQueryInitialResultsComplete reliable packet ... auto initialCompletion = NLPacket::create(PacketType::EntityQueryInitialResultsComplete, sizeof(OCTREE_PACKET_SEQUENCE), true); - initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber() - 1U)); + initialCompletion->writePrimitive(OCTREE_PACKET_SEQUENCE(nodeData->getSequenceNumber())); DependencyManager::get()->sendPacket(std::move(initialCompletion), *node); } diff --git a/interface/src/octree/SafeLanding.cpp b/interface/src/octree/SafeLanding.cpp index 31106457fb..60b660f66a 100644 --- a/interface/src/octree/SafeLanding.cpp +++ b/interface/src/octree/SafeLanding.cpp @@ -122,11 +122,11 @@ bool SafeLanding::isSequenceNumbersComplete() { int sequenceSize = _initialStart <= _initialEnd ? _initialEnd - _initialStart: _initialEnd + SEQUENCE_MODULO - _initialStart; auto startIter = _sequenceNumbers.find(_initialStart); - auto endIter = _sequenceNumbers.find(_initialEnd); + auto endIter = _sequenceNumbers.find(_initialEnd - 1); if (sequenceSize == 0 || (startIter != _sequenceNumbers.end() && endIter != _sequenceNumbers.end() - && distance(startIter, endIter) == sequenceSize) ) { + && distance(startIter, endIter) == sequenceSize - 1) ) { _trackingEntities = false; // Don't track anything else that comes in. return true; } diff --git a/interface/src/octree/SafeLanding.h b/interface/src/octree/SafeLanding.h index 210dfbac25..9177930d81 100644 --- a/interface/src/octree/SafeLanding.h +++ b/interface/src/octree/SafeLanding.h @@ -26,7 +26,7 @@ class SafeLanding : public QObject { public: void startEntitySequence(QSharedPointer entityTreeRenderer); void stopEntitySequence(); - void setCompletionSequenceNumbers(int first, int last); + void setCompletionSequenceNumbers(int first, int last); // 'last' exclusive. void noteReceivedsequenceNumber(int sequenceNumber); bool isLoadSequenceComplete(); diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8b50e37699..bf455de3e4 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -95,7 +95,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarIdentityRequest: return 22; default: - return 22; + return 23; } } From c32cc1ead298f973e366da23fc3fa4ad748656e9 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 22 Aug 2018 15:42:15 -0700 Subject: [PATCH 135/207] adding implementation --- interface/src/avatar/MyAvatar.cpp | 3 +-- interface/src/ui/PreferencesDialog.cpp | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b30f05225e..79c3f969b5 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -105,8 +105,8 @@ MyAvatar::MyAvatar(QThread* thread) : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", false), _smoothOrientationTimer(std::numeric_limits::max()), + _useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", true), _smoothOrientationInitial(), _smoothOrientationTarget(), _hmdSensorMatrix(), @@ -444,7 +444,6 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { } void MyAvatar::update(float deltaTime) { - // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index e2037d54d0..c5cce261ae 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -272,7 +272,7 @@ void setupPreferences() { auto getter = [myAvatar]()->bool { return myAvatar->useAdvancedMovementControls(); }; auto setter = [myAvatar](bool value) { myAvatar->setUseAdvancedMovementControls(value); }; preferences->addPreference(new CheckPreference(VR_MOVEMENT, - QStringLiteral("Advanced movement for hand controllers"), + QStringLiteral("Advanced movement for VR (Teleport movement when unchecked)"), getter, setter)); } { From 9518b8cdafa47a51678df0b6c604515bdab5e807 Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 22 Aug 2018 15:46:02 -0700 Subject: [PATCH 136/207] rearrangement --- interface/src/avatar/MyAvatar.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 79c3f969b5..840747003f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -105,8 +105,8 @@ MyAvatar::MyAvatar(QThread* thread) : _eyeContactTarget(LEFT_EYE), _realWorldFieldOfView("realWorldFieldOfView", DEFAULT_REAL_WORLD_FIELD_OF_VIEW_DEGREES), - _smoothOrientationTimer(std::numeric_limits::max()), _useAdvancedMovementControls("advancedMovementForHandControllersIsChecked", true), + _smoothOrientationTimer(std::numeric_limits::max()), _smoothOrientationInitial(), _smoothOrientationTarget(), _hmdSensorMatrix(), From 4daa30c1d0c199f306920b344ce266cd0fa6dc4b Mon Sep 17 00:00:00 2001 From: Wayne Chen Date: Wed, 22 Aug 2018 15:58:37 -0700 Subject: [PATCH 137/207] changing text for check preference --- interface/src/ui/PreferencesDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/ui/PreferencesDialog.cpp b/interface/src/ui/PreferencesDialog.cpp index c5cce261ae..79ca2063ec 100644 --- a/interface/src/ui/PreferencesDialog.cpp +++ b/interface/src/ui/PreferencesDialog.cpp @@ -272,7 +272,7 @@ void setupPreferences() { auto getter = [myAvatar]()->bool { return myAvatar->useAdvancedMovementControls(); }; auto setter = [myAvatar](bool value) { myAvatar->setUseAdvancedMovementControls(value); }; preferences->addPreference(new CheckPreference(VR_MOVEMENT, - QStringLiteral("Advanced movement for VR (Teleport movement when unchecked)"), + QStringLiteral("Advanced movement in VR (Teleport movement when unchecked)"), getter, setter)); } { From c4b916af62788d62946d59a140ad26ede6f9f81f Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 22 Aug 2018 16:22:30 -0700 Subject: [PATCH 138/207] Just bump packet version for EntityQueryInitialResultsComplete --- libraries/networking/src/udt/PacketHeaders.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index bf455de3e4..25503e192f 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -94,8 +94,10 @@ PacketVersion versionForPacketType(PacketType packetType) { return static_cast(AvatarQueryVersion::ConicalFrustums); case PacketType::AvatarIdentityRequest: return 22; - default: + case PacketType::EntityQueryInitialResultsComplete: return 23; + default: + return 22; } } From 5c0b12abf6a7d1983dc5a080cf31422c41ee36d3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 22 Aug 2018 12:07:32 -0700 Subject: [PATCH 139/207] ordered ray/parabola intersection code --- interface/src/avatar/AvatarManager.cpp | 8 +- interface/src/raypick/LaserPointer.cpp | 3 + interface/src/raypick/ParabolaPointer.cpp | 3 + .../src/RenderablePolyVoxEntityItem.cpp | 13 +- libraries/entities/src/EntityTree.cpp | 54 +- libraries/entities/src/EntityTreeElement.cpp | 33 +- libraries/entities/src/EntityTreeElement.h | 9 +- libraries/entities/src/ShapeEntityItem.cpp | 28 +- libraries/octree/src/Octree.cpp | 127 ++--- libraries/octree/src/Octree.h | 22 +- libraries/render-utils/src/Model.cpp | 177 ++++-- libraries/render-utils/src/Model.h | 10 + libraries/shared/src/GeometryUtil.cpp | 7 +- libraries/shared/src/TriangleSet.cpp | 518 +++++++++++------- libraries/shared/src/TriangleSet.h | 25 +- 15 files changed, 589 insertions(+), 448 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0d180bc40d..139f44d58b 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -553,8 +553,6 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic return result; } - glm::vec3 normDirection = glm::normalize(ray.direction); - auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -587,14 +585,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findRayCapsuleIntersection(ray.origin, normDirection, start, end, radius, distance); + bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); if (!intersects) { // ray doesn't intersect avatar's capsule continue; } QVariantMap extraInfo; - intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, normDirection, + intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true); if (intersects && (!result.intersects || distance < result.distance)) { @@ -608,7 +606,7 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic } if (result.intersects) { - result.intersection = ray.origin + normDirection * result.distance; + result.intersection = ray.origin + ray.direction * result.distance; } return result; diff --git a/interface/src/raypick/LaserPointer.cpp b/interface/src/raypick/LaserPointer.cpp index 2382a95105..40a5e5cb69 100644 --- a/interface/src/raypick/LaserPointer.cpp +++ b/interface/src/raypick/LaserPointer.cpp @@ -42,6 +42,9 @@ glm::vec3 LaserPointer::getPickOrigin(const PickResultPointer& pickResult) const glm::vec3 LaserPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const { auto rayPickResult = std::static_pointer_cast(pickResult); + if (!rayPickResult) { + return glm::vec3(0.0f); + } if (distance > 0.0f) { PickRay pick = PickRay(rayPickResult->pickVariant); return pick.origin + distance * pick.direction; diff --git a/interface/src/raypick/ParabolaPointer.cpp b/interface/src/raypick/ParabolaPointer.cpp index 097c98340c..57d57e11c4 100644 --- a/interface/src/raypick/ParabolaPointer.cpp +++ b/interface/src/raypick/ParabolaPointer.cpp @@ -67,6 +67,9 @@ glm::vec3 ParabolaPointer::getPickOrigin(const PickResultPointer& pickResult) co glm::vec3 ParabolaPointer::getPickEnd(const PickResultPointer& pickResult, float distance) const { auto parabolaPickResult = std::static_pointer_cast(pickResult); + if (!parabolaPickResult) { + return glm::vec3(0.0f); + } if (distance > 0.0f) { PickParabola pick = PickParabola(parabolaPickResult->pickVariant); return pick.origin + pick.velocity * distance + 0.5f * pick.acceleration * distance * distance; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index c11ccb70a0..63f27dd170 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -584,8 +584,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o glm::vec4 originInVoxel = wtvMatrix * glm::vec4(origin, 1.0f); glm::vec4 farInVoxel = wtvMatrix * glm::vec4(farPoint, 1.0f); - glm::vec4 directionInVoxel = glm::normalize(farInVoxel - originInVoxel); - glm::vec4 result = glm::vec4(0.0f, 0.0f, 0.0f, 0.0f); PolyVox::RaycastResult raycastResult = doRayCast(originInVoxel, farInVoxel, result); if (raycastResult == PolyVox::RaycastResults::Completed) { @@ -599,14 +597,9 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o voxelBox += result3 - Vectors::HALF; voxelBox += result3 + Vectors::HALF; - float voxelDistance; - bool hit = voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), - voxelDistance, face, surfaceNormal); - - glm::vec4 voxelIntersectionPoint = glm::vec4(glm::vec3(originInVoxel) + glm::vec3(directionInVoxel) * voxelDistance, 1.0); - glm::vec4 intersectionPoint = vtwMatrix * voxelIntersectionPoint; - distance = glm::distance(origin, glm::vec3(intersectionPoint)); - return hit; + glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f); + return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), + distance, face, surfaceNormal); } bool RenderablePolyVoxEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 377e192bb1..1cf6e45d5b 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -825,15 +825,38 @@ bool findRayIntersectionOp(const OctreeElementPointer& element, void* extraData) RayArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, keepSearching, + EntityItemID entityID = entityTreeElementPointer->findRayIntersection(args->origin, args->direction, args->element, args->distance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; + // We recurse OctreeElements in order, so if we hit something, we can stop immediately + keepSearching = false; } return keepSearching; } +float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { + RayArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); + float distance = FLT_MAX; + // If origin is inside the cube, always check this element first + if (entityTreeElementPointer->getAACube().contains(args->origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) { + // Don't add this cell if it's already farther than our best distance so far + if (boundDistance < args->distance) { + distance = boundDistance; + } + } + } + return distance; +} + EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, QVector entityIdsToInclude, QVector entityIdsToDiscard, bool visibleOnly, bool collidableOnly, bool precisionPicking, @@ -846,7 +869,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&]{ - recurseTreeWithOperation(findRayIntersectionOp, &args); + recurseTreeWithOperationSorted(findRayIntersectionOp, findRayIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { @@ -860,15 +883,38 @@ bool findParabolaIntersectionOp(const OctreeElementPointer& element, void* extra ParabolaArgs* args = static_cast(extraData); bool keepSearching = true; EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); - EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, keepSearching, + EntityItemID entityID = entityTreeElementPointer->findParabolaIntersection(args->origin, args->velocity, args->acceleration, args->element, args->parabolicDistance, args->face, args->surfaceNormal, args->entityIdsToInclude, args->entityIdsToDiscard, args->visibleOnly, args->collidableOnly, args->extraInfo, args->precisionPicking); if (!entityID.isNull()) { args->entityID = entityID; + // We recurse OctreeElements in order, so if we hit something, we can stop immediately + keepSearching = false; } return keepSearching; } +float findParabolaIntersectionSortingOp(const OctreeElementPointer& element, void* extraData) { + ParabolaArgs* args = static_cast(extraData); + EntityTreeElementPointer entityTreeElementPointer = std::static_pointer_cast(element); + float distance = FLT_MAX; + // If origin is inside the cube, always check this element first + if (entityTreeElementPointer->getAACube().contains(args->origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (entityTreeElementPointer->getAACube().findParabolaIntersection(args->origin, args->velocity, args->acceleration, boundDistance, face, surfaceNormal)) { + // Don't add this cell if it's already farther than our best distance so far + if (boundDistance < args->parabolicDistance) { + distance = boundDistance; + } + } + } + return distance; +} + EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, QVector entityIdsToInclude, QVector entityIdsToDiscard, bool visibleOnly, bool collidableOnly, bool precisionPicking, @@ -882,7 +928,7 @@ EntityItemID EntityTree::findParabolaIntersection(const PickParabola& parabola, bool requireLock = lockType == Octree::Lock; bool lockResult = withReadLock([&] { - recurseTreeWithOperation(findParabolaIntersectionOp, &args); + recurseTreeWithOperationSorted(findParabolaIntersectionOp, findParabolaIntersectionSortingOp, &args); }, requireLock); if (accurateResult) { diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index 5974fce6c5..e8e11c0ee1 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -140,27 +140,17 @@ bool EntityTreeElement::bestFitBounds(const glm::vec3& minPoint, const glm::vec3 } EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking) { + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); + float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; - // if the ray doesn't intersect with our cube OR the distance to element is less than current best distance - // we can stop searching! - bool hit = _cube.findRayIntersection(origin, direction, distanceToElementCube, localFace, localSurfaceNormal); - if (!hit || (!_cube.contains(origin) && distanceToElementCube > distance)) { - keepSearching = false; // no point in continuing to search - return result; // we did not intersect - } - - // by default, we only allow intersections with leaves with content if (!canPickIntersect()) { - return result; // we don't intersect with non-leaves, and we keep searching + return result; } // if the distance to the element cube is not less than the current best distance, then it's not possible @@ -289,7 +279,7 @@ bool EntityTreeElement::findSpherePenetration(const glm::vec3& center, float rad } EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { @@ -299,17 +289,8 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin BoxFace localFace; glm::vec3 localSurfaceNormal; - // if the parabola doesn't intersect with our cube OR the distance to element is less than current best distance - // we can stop searching! - bool hit = _cube.findParabolaIntersection(origin, velocity, acceleration, distanceToElementCube, localFace, localSurfaceNormal); - if (!hit || (!_cube.contains(origin) && distanceToElementCube > parabolicDistance)) { - keepSearching = false; // no point in continuing to search - return result; // we did not intersect - } - - // by default, we only allow intersections with leaves with content if (!canPickIntersect()) { - return result; // we don't intersect with non-leaves, and we keep searching + return result; } // if the distance to the element cube is not less than the current best distance, then it's not possible diff --git a/libraries/entities/src/EntityTreeElement.h b/libraries/entities/src/EntityTreeElement.h index d6f9db08d6..793340c9a4 100644 --- a/libraries/entities/src/EntityTreeElement.h +++ b/libraries/entities/src/EntityTreeElement.h @@ -136,10 +136,9 @@ public: virtual bool canPickIntersect() const override { return hasEntities(); } virtual EntityItemID findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - bool& keepSearching, OctreeElementPointer& element, float& distance, - BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, - const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, - QVariantMap& extraInfo, bool precisionPicking = false); + OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, + const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, + bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); virtual EntityItemID findDetailedRayIntersection(const glm::vec3& origin, const glm::vec3& direction, OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, @@ -149,7 +148,7 @@ public: glm::vec3& penetration, void** penetratedObject) const override; virtual EntityItemID findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, bool& keepSearching, OctreeElementPointer& element, float& parabolicDistance, + const glm::vec3& acceleration, OctreeElementPointer& element, float& parabolicDistance, BoxFace& face, glm::vec3& surfaceNormal, const QVector& entityIdsToInclude, const QVector& entityIdsToDiscard, bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking = false); diff --git a/libraries/entities/src/ShapeEntityItem.cpp b/libraries/entities/src/ShapeEntityItem.cpp index e4ea1470c1..773a7059dc 100644 --- a/libraries/entities/src/ShapeEntityItem.cpp +++ b/libraries/entities/src/ShapeEntityItem.cpp @@ -262,20 +262,18 @@ bool ShapeEntityItem::findDetailedRayIntersection(const glm::vec3& origin, const glm::mat4 entityToWorldMatrix = getEntityToWorldMatrix(); glm::mat4 worldToEntityMatrix = glm::inverse(entityToWorldMatrix); glm::vec3 entityFrameOrigin = glm::vec3(worldToEntityMatrix * glm::vec4(origin, 1.0f)); - glm::vec3 entityFrameDirection = glm::normalize(glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f))); + glm::vec3 entityFrameDirection = glm::vec3(worldToEntityMatrix * glm::vec4(direction, 0.0f)); - float localDistance; // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 - if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, localDistance)) { - // determine where on the unit sphere the hit point occured - glm::vec3 entityFrameHitAt = entityFrameOrigin + (entityFrameDirection * localDistance); - // then translate back to work coordinates - glm::vec3 hitAt = glm::vec3(entityToWorldMatrix * glm::vec4(entityFrameHitAt, 1.0f)); - distance = glm::distance(origin, hitAt); + if (findRaySphereIntersection(entityFrameOrigin, entityFrameDirection, glm::vec3(0.0f), 0.5f, distance)) { bool success; - // FIXME: this is only correct for uniformly scaled spheres - surfaceNormal = glm::normalize(hitAt - getCenterPosition(success)); - if (!success) { + glm::vec3 center = getCenterPosition(success); + if (success) { + // FIXME: this is only correct for uniformly scaled spheres + // determine where on the unit sphere the hit point occured + glm::vec3 hitAt = origin + (direction * distance); + surfaceNormal = glm::normalize(hitAt - center); + } else { return false; } return true; @@ -297,9 +295,11 @@ bool ShapeEntityItem::findDetailedParabolaIntersection(const glm::vec3& origin, // NOTE: unit sphere has center of 0,0,0 and radius of 0.5 if (findParabolaSphereIntersection(entityFrameOrigin, entityFrameVelocity, entityFrameAcceleration, glm::vec3(0.0f), 0.5f, parabolicDistance)) { bool success; - // FIXME: this is only correct for uniformly scaled spheres - surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - getCenterPosition(success)); - if (!success) { + glm::vec3 center = getCenterPosition(success); + if (success) { + // FIXME: this is only correct for uniformly scaled spheres + surfaceNormal = glm::normalize((origin + velocity * parabolicDistance + 0.5f * acceleration * parabolicDistance * parabolicDistance) - center); + } else { return false; } return true; diff --git a/libraries/octree/src/Octree.cpp b/libraries/octree/src/Octree.cpp index 9bb0e25982..c6474c1fe4 100644 --- a/libraries/octree/src/Octree.cpp +++ b/libraries/octree/src/Octree.cpp @@ -68,55 +68,12 @@ Octree::~Octree() { eraseAllOctreeElements(false); } - -// Inserts the value and key into three arrays sorted by the key array, the first array is the value, -// the second array is a sorted key for the value, the third array is the index for the value in it original -// non-sorted array -// returns -1 if size exceeded -// originalIndexArray is optional -int insertOctreeElementIntoSortedArrays(const OctreeElementPointer& value, float key, int originalIndex, - OctreeElementPointer* valueArray, float* keyArray, int* originalIndexArray, - int currentCount, int maxCount) { - - if (currentCount < maxCount) { - int i = 0; - if (currentCount > 0) { - while (i < currentCount && key > keyArray[i]) { - i++; - } - // i is our desired location - // shift array elements to the right - if (i < currentCount && i+1 < maxCount) { - for (int j = currentCount - 1; j > i; j--) { - valueArray[j] = valueArray[j - 1]; - keyArray[j] = keyArray[j - 1]; - } - } - } - // place new element at i - valueArray[i] = value; - keyArray[i] = key; - if (originalIndexArray) { - originalIndexArray[i] = originalIndex; - } - return currentCount + 1; - } - return -1; // error case -} - - - // Recurses voxel tree calling the RecurseOctreeOperation function for each element. // stops recursion if operation function returns false. void Octree::recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData) { recurseElementWithOperation(_rootElement, operation, extraData); } -// Recurses voxel tree calling the RecurseOctreePostFixOperation function for each element in post-fix order. -void Octree::recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData) { - recurseElementWithPostOperation(_rootElement, operation, extraData); -} - // Recurses voxel element with an operation function void Octree::recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount) { @@ -129,71 +86,53 @@ void Octree::recurseElementWithOperation(const OctreeElementPointer& element, co for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer child = element->getChildAtIndex(i); if (child) { - recurseElementWithOperation(child, operation, extraData, recursionCount+1); + recurseElementWithOperation(child, operation, extraData, recursionCount + 1); } } } } -// Recurses voxel element with an operation function -void Octree::recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - void* extraData, int recursionCount) { +void Octree::recurseTreeWithOperationSorted(const RecurseOctreeOperation& operation, const RecurseOctreeSortingOperation& sortingOperation, void* extraData) { + recurseElementWithOperationSorted(_rootElement, operation, sortingOperation, extraData); +} + +// Recurses voxel element with an operation function, calling operation on its children in a specific order +bool Octree::recurseElementWithOperationSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, + const RecurseOctreeSortingOperation& sortingOperation, void* extraData, int recursionCount) { if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - HIFI_FCDEBUG(octree(), "Octree::recurseElementWithPostOperation() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - return; + HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); + // If we go too deep, we want to keep searching other paths + return true; } + bool keepSearching = operation(element, extraData); + + std::vector sortedChildren; for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { OctreeElementPointer child = element->getChildAtIndex(i); if (child) { - recurseElementWithPostOperation(child, operation, extraData, recursionCount+1); - } - } - operation(element, extraData); -} - -// Recurses voxel tree calling the RecurseOctreeOperation function for each element. -// stops recursion if operation function returns false. -void Octree::recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData) { - - recurseElementWithOperationDistanceSorted(_rootElement, operation, point, extraData); -} - -// Recurses voxel element with an operation function -void Octree::recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData, int recursionCount) { - - if (recursionCount > DANGEROUSLY_DEEP_RECURSION) { - HIFI_FCDEBUG(octree(), "Octree::recurseElementWithOperationDistanceSorted() reached DANGEROUSLY_DEEP_RECURSION, bailing!"); - return; - } - - if (operation(element, extraData)) { - // determine the distance sorted order of our children - OctreeElementPointer sortedChildren[NUMBER_OF_CHILDREN] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; - float distancesToChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int indexOfChildren[NUMBER_OF_CHILDREN] = { 0, 0, 0, 0, 0, 0, 0, 0 }; - int currentCount = 0; - - for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { - OctreeElementPointer childElement = element->getChildAtIndex(i); - if (childElement) { - // chance to optimize, doesn't need to be actual distance!! Could be distance squared - float distanceSquared = childElement->distanceSquareToPoint(point); - currentCount = insertOctreeElementIntoSortedArrays(childElement, distanceSquared, i, - sortedChildren, (float*)&distancesToChildren, - (int*)&indexOfChildren, currentCount, NUMBER_OF_CHILDREN); - } - } - - for (int i = 0; i < currentCount; i++) { - OctreeElementPointer childElement = sortedChildren[i]; - if (childElement) { - recurseElementWithOperationDistanceSorted(childElement, operation, point, extraData); + float priority = sortingOperation(child, extraData); + if (priority < FLT_MAX) { + sortedChildren.emplace_back(priority, child); } } } + + if (sortedChildren.size() > 1) { + static auto comparator = [](const SortedChild& left, const SortedChild& right) { return left.first > right.first; }; + std::sort(sortedChildren.begin(), sortedChildren.end(), comparator); + } + + for (auto it = sortedChildren.begin(); it != sortedChildren.end(); ++it) { + const SortedChild& sortedChild = *it; + // Our children were sorted, so if one hits something, we don't need to check the others + if (!recurseElementWithOperationSorted(sortedChild.second, operation, sortingOperation, extraData, recursionCount + 1)) { + return false; + } + } + // We checked all our children and didn't find anything. + // Stop if we hit something in this element. Continue if we didn't. + return keepSearching; } void Octree::recurseTreeWithOperator(RecurseOctreeOperator* operatorObject) { diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h index b827ed7cd0..a2b2f227cb 100644 --- a/libraries/octree/src/Octree.h +++ b/libraries/octree/src/Octree.h @@ -49,6 +49,9 @@ public: // Callback function, for recuseTreeWithOperation using RecurseOctreeOperation = std::function; +// Function for sorting octree children during recursion. If return value == FLT_MAX, child is discarded +using RecurseOctreeSortingOperation = std::function; +using SortedChild = std::pair; typedef QHash CubeList; const bool NO_EXISTS_BITS = false; @@ -163,17 +166,10 @@ public: OctreeElementPointer getOrCreateChildElementContaining(const AACube& box); void recurseTreeWithOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); - void recurseTreeWithPostOperation(const RecurseOctreeOperation& operation, void* extraData = NULL); - - /// \param operation type of operation - /// \param point point in world-frame (meters) - /// \param extraData hook for user data to be interpreted by special context - void recurseTreeWithOperationDistanceSorted(const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData = NULL); + void recurseTreeWithOperationSorted(const RecurseOctreeOperation& operation, const RecurseOctreeSortingOperation& sortingOperation, void* extraData = NULL); void recurseTreeWithOperator(RecurseOctreeOperator* operatorObject); - bool isDirty() const { return _isDirty; } void clearDirtyBit() { _isDirty = false; } void setDirtyBit() { _isDirty = true; } @@ -227,14 +223,8 @@ public: void recurseElementWithOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, void* extraData, int recursionCount = 0); - - /// Traverse child nodes of node applying operation in post-fix order - /// - void recurseElementWithPostOperation(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - void* extraData, int recursionCount = 0); - - void recurseElementWithOperationDistanceSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, - const glm::vec3& point, void* extraData, int recursionCount = 0); + bool recurseElementWithOperationSorted(const OctreeElementPointer& element, const RecurseOctreeOperation& operation, + const RecurseOctreeSortingOperation& sortingOperation, void* extraData, int recursionCount = 0); bool recurseElementWithOperator(const OctreeElementPointer& element, RecurseOctreeOperator* operatorObject, int recursionCount = 0); diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 8f4eb57f8e..22bc6b6ca7 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -379,14 +379,17 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { QMutexLocker locker(&_mutex); - float bestDistance = std::numeric_limits::max(); + float bestDistance = FLT_MAX; + BoxFace bestFace; Triangle bestModelTriangle; Triangle bestWorldTriangle; - int bestSubMeshIndex = 0; + glm::vec3 bestWorldIntersectionPoint; + glm::vec3 bestMeshIntersectionPoint; + int bestPartIndex; + int bestShapeID; + int bestSubMeshIndex; - int subMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); - if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -399,39 +402,75 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); int shapeID = 0; + int subMeshIndex = 0; + + std::vector sortedTriangleSets; for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { int partIndex = 0; - for (auto &partTriangleSet : meshTriangleSets) { - float triangleSetDistance; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (partTriangleSet.findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); - glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); - float worldDistance = glm::distance(origin, worldIntersectionPoint); - - if (worldDistance < bestDistance) { - bestDistance = worldDistance; - intersectedSomething = true; - face = triangleSetFace; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - extraInfo["partIndex"] = partIndex; - extraInfo["shapeID"] = shapeID; - bestSubMeshIndex = subMeshIndex; + for (auto& partTriangleSet : meshTriangleSets) { + float priority = FLT_MAX; + if (partTriangleSet.getBounds().contains(meshFrameOrigin)) { + priority = 0.0f; + } else { + float partBoundDistance = FLT_MAX; + BoxFace partBoundFace; + glm::vec3 partBoundNormal; + if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance, + partBoundFace, partBoundNormal)) { + priority = partBoundDistance; } } + + if (priority < FLT_MAX) { + sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex); + } partIndex++; shapeID++; } subMeshIndex++; } + if (sortedTriangleSets.size() > 1) { + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); + } + + for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) { + const SortedTriangleSet& sortedTriangleSet = *it; + // We can exit once triangleSetDistance > bestDistance + if (sortedTriangleSet.distance > bestDistance) { + break; + } + float triangleSetDistance = FLT_MAX; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, + triangleSetTriangle, pickAgainstTriangles, allowBackface)) { + if (triangleSetDistance < bestDistance) { + bestDistance = triangleSetDistance; + intersectedSomething = true; + bestFace = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + glm::vec3 meshIntersectionPoint = meshFrameOrigin + (meshFrameDirection * triangleSetDistance); + glm::vec3 worldIntersectionPoint = glm::vec3(meshToWorldMatrix * glm::vec4(meshIntersectionPoint, 1.0f)); + bestWorldIntersectionPoint = worldIntersectionPoint; + bestMeshIntersectionPoint = meshIntersectionPoint; + bestPartIndex = sortedTriangleSet.partIndex; + bestShapeID = sortedTriangleSet.shapeID; + bestSubMeshIndex = sortedTriangleSet.subMeshIndex; + } + } + } + if (intersectedSomething) { distance = bestDistance; + face = bestFace; surfaceNormal = bestWorldTriangle.getNormal(); + extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint); + extraInfo["partIndex"] = bestPartIndex; + extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); @@ -483,13 +522,16 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co QMutexLocker locker(&_mutex); float bestDistance = FLT_MAX; + BoxFace bestFace; Triangle bestModelTriangle; Triangle bestWorldTriangle; - int bestSubMeshIndex = 0; + glm::vec3 bestWorldIntersectionPoint; + glm::vec3 bestMeshIntersectionPoint; + int bestPartIndex; + int bestShapeID; + int bestSubMeshIndex; - int subMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); - if (!_triangleSetsValid) { calculateTriangleSets(geometry); } @@ -503,40 +545,79 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co glm::vec3 meshFrameAcceleration = glm::vec3(worldToMeshMatrix * glm::vec4(acceleration, 0.0f)); int shapeID = 0; + int subMeshIndex = 0; + + std::vector sortedTriangleSets; for (auto& meshTriangleSets : _modelSpaceMeshTriangleSets) { int partIndex = 0; - for (auto &partTriangleSet : meshTriangleSets) { - float triangleSetDistance; - BoxFace triangleSetFace; - Triangle triangleSetTriangle; - if (partTriangleSet.findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, - triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { - if (triangleSetDistance < bestDistance) { - bestDistance = triangleSetDistance; - intersectedSomething = true; - face = triangleSetFace; - bestModelTriangle = triangleSetTriangle; - bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; - glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance + - 0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance; - glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance + - 0.5f * acceleration * triangleSetDistance * triangleSetDistance; - extraInfo["worldIntersectionPoint"] = vec3toVariant(worldIntersectionPoint); - extraInfo["meshIntersectionPoint"] = vec3toVariant(meshIntersectionPoint); - extraInfo["partIndex"] = partIndex; - extraInfo["shapeID"] = shapeID; - bestSubMeshIndex = subMeshIndex; + for (auto& partTriangleSet : meshTriangleSets) { + float priority = FLT_MAX; + if (partTriangleSet.getBounds().contains(meshFrameOrigin)) { + priority = 0.0f; + } else { + float partBoundDistance = FLT_MAX; + BoxFace partBoundFace; + glm::vec3 partBoundNormal; + if (partTriangleSet.getBounds().findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, + partBoundDistance, partBoundFace, partBoundNormal)) { + priority = partBoundDistance; } } + + if (priority < FLT_MAX) { + sortedTriangleSets.emplace_back(priority, &partTriangleSet, partIndex, shapeID, subMeshIndex); + } partIndex++; shapeID++; } subMeshIndex++; } + if (sortedTriangleSets.size() > 1) { + static auto comparator = [](const SortedTriangleSet& left, const SortedTriangleSet& right) { return left.distance > right.distance; }; + std::sort(sortedTriangleSets.begin(), sortedTriangleSets.end(), comparator); + } + + for (auto it = sortedTriangleSets.begin(); it != sortedTriangleSets.end(); ++it) { + const SortedTriangleSet& sortedTriangleSet = *it; + // We can exit once triangleSetDistance > bestDistance + if (sortedTriangleSet.distance > bestDistance) { + break; + } + float triangleSetDistance = FLT_MAX; + BoxFace triangleSetFace; + Triangle triangleSetTriangle; + if (sortedTriangleSet.triangleSet->findParabolaIntersection(meshFrameOrigin, meshFrameVelocity, meshFrameAcceleration, + triangleSetDistance, triangleSetFace, triangleSetTriangle, + pickAgainstTriangles, allowBackface)) { + if (triangleSetDistance < bestDistance) { + bestDistance = triangleSetDistance; + intersectedSomething = true; + bestFace = triangleSetFace; + bestModelTriangle = triangleSetTriangle; + bestWorldTriangle = triangleSetTriangle * meshToWorldMatrix; + glm::vec3 meshIntersectionPoint = meshFrameOrigin + meshFrameVelocity * triangleSetDistance + + 0.5f * meshFrameAcceleration * triangleSetDistance * triangleSetDistance; + glm::vec3 worldIntersectionPoint = origin + velocity * triangleSetDistance + + 0.5f * acceleration * triangleSetDistance * triangleSetDistance; + bestWorldIntersectionPoint = worldIntersectionPoint; + bestMeshIntersectionPoint = meshIntersectionPoint; + bestPartIndex = sortedTriangleSet.partIndex; + bestShapeID = sortedTriangleSet.shapeID; + bestSubMeshIndex = sortedTriangleSet.subMeshIndex; + // These sets can overlap, so we can't exit early if we find something + } + } + } + if (intersectedSomething) { parabolicDistance = bestDistance; + face = bestFace; surfaceNormal = bestWorldTriangle.getNormal(); + extraInfo["worldIntersectionPoint"] = vec3toVariant(bestWorldIntersectionPoint); + extraInfo["meshIntersectionPoint"] = vec3toVariant(bestMeshIntersectionPoint); + extraInfo["partIndex"] = bestPartIndex; + extraInfo["shapeID"] = bestShapeID; if (pickAgainstTriangles) { extraInfo["subMeshIndex"] = bestSubMeshIndex; extraInfo["subMeshName"] = geometry.getModelNameOfMesh(bestSubMeshIndex); diff --git a/libraries/render-utils/src/Model.h b/libraries/render-utils/src/Model.h index 627e5fddab..adb2fcdd3d 100644 --- a/libraries/render-utils/src/Model.h +++ b/libraries/render-utils/src/Model.h @@ -64,6 +64,16 @@ class Model; using ModelPointer = std::shared_ptr; using ModelWeakPointer = std::weak_ptr; +struct SortedTriangleSet { + SortedTriangleSet(float distance, TriangleSet* triangleSet, int partIndex, int shapeID, int subMeshIndex) : + distance(distance), triangleSet(triangleSet), partIndex(partIndex), shapeID(shapeID), subMeshIndex(subMeshIndex) {} + + float distance; + TriangleSet* triangleSet; + int partIndex; + int shapeID; + int subMeshIndex; +}; /// A generic 3D model displaying geometry loaded from a URL. class Model : public QObject, public std::enable_shared_from_this, public scriptable::ModelProvider { diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index 6fb06eb624..a93c2ec9f3 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -286,12 +286,13 @@ bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& directi distance = 0.0f; return true; // starts inside the sphere } - float b = glm::dot(direction, relativeOrigin); - float radicand = b * b - c; + float b = 2.0f * glm::dot(direction, relativeOrigin); + float a = glm::dot(direction, direction); + float radicand = b * b - 4.0f * a * c; if (radicand < 0.0f) { return false; // doesn't hit the sphere } - float t = -b - sqrtf(radicand); + float t = 0.5f * (-b - sqrtf(radicand)) / a; if (t < 0.0f) { return false; // doesn't hit the sphere } diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index cde9c20cab..f3c0ef5776 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -13,6 +13,8 @@ #include "GLMHelpers.h" +#include + void TriangleSet::insert(const Triangle& t) { _isBalanced = false; @@ -30,47 +32,6 @@ void TriangleSet::clear() { _triangleOctree.clear(); } -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { - - // reset our distance to be the max possible, lower level tests will store best distance here - distance = std::numeric_limits::max(); - - if (!_isBalanced) { - balanceOctree(); - } - - int trianglesTouched = 0; - auto result = _triangleOctree.findRayIntersection(origin, direction, distance, face, triangle, precision, trianglesTouched, allowBackface); - - #if WANT_DEBUGGING - if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); - } - #endif - return result; -} - -bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, - float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { - // reset our distance to be the max possible, lower level tests will store best distance here - parabolicDistance = FLT_MAX; - - if (!_isBalanced) { - balanceOctree(); - } - - int trianglesTouched = 0; - auto result = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, parabolicDistance, face, triangle, precision, trianglesTouched, allowBackface); - -#if WANT_DEBUGGING - if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); - } -#endif - return result; -} - bool TriangleSet::convexHullContains(const glm::vec3& point) const { if (!_bounds.contains(point)) { return false; @@ -97,7 +58,7 @@ void TriangleSet::debugDump() { } void TriangleSet::balanceOctree() { - _triangleOctree.reset(_bounds, 0); + _triangleOctree.reset(_bounds); // insert all the triangles for (size_t i = 0; i < _triangles.size(); i++) { @@ -106,79 +67,15 @@ void TriangleSet::balanceOctree() { _isBalanced = true; - #if WANT_DEBUGGING +#if WANT_DEBUGGING debugDump(); - #endif +#endif } - -// Determine of the given ray (origin/direction) in model space intersects with any triangles -// in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, - float& distance, BoxFace& face, Triangle& triangle, bool precision, - int& trianglesTouched, bool allowBackface) { - bool intersectedSomething = false; - float bestDistance = FLT_MAX; - - if (precision) { - for (const auto& triangleIndex : _triangleIndices) { - const auto& thisTriangle = _allTriangles[triangleIndex]; - float thisTriangleDistance; - trianglesTouched++; - if (findRayTriangleIntersection(origin, direction, thisTriangle, thisTriangleDistance, allowBackface)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - triangle = thisTriangle; - } - } - } - } else { - intersectedSomething = true; - bestDistance = distance; - } - - if (intersectedSomething) { - distance = bestDistance; - } - - return intersectedSomething; -} - -bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, - const glm::vec3& acceleration, float& parabolicDistance, - BoxFace& face, Triangle& triangle, bool precision, - int& trianglesTouched, bool allowBackface) { - bool intersectedSomething = false; - float bestDistance = FLT_MAX; - - if (precision) { - for (const auto& triangleIndex : _triangleIndices) { - const auto& thisTriangle = _allTriangles[triangleIndex]; - float thisTriangleDistance; - trianglesTouched++; - if (findParabolaTriangleIntersection(origin, velocity, acceleration, thisTriangle, thisTriangleDistance, allowBackface)) { - if (thisTriangleDistance < bestDistance) { - bestDistance = thisTriangleDistance; - intersectedSomething = true; - triangle = thisTriangle; - } - } - } - } else { - intersectedSomething = true; - bestDistance = parabolicDistance; - } - - if (intersectedSomething) { - parabolicDistance = bestDistance; - } - - return intersectedSomething; -} - -static const int MAX_DEPTH = 4; // for now -static const int MAX_CHILDREN = 8; +// With an octree: 8 ^ MAX_DEPTH = 4096 leaves +//static const int MAX_DEPTH = 4; +// With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves +static const int MAX_DEPTH = 12; TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) @@ -190,7 +87,8 @@ void TriangleSet::TriangleOctreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); - _children.clear(); + _children.first.reset(); + _children.second.reset(); } void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { @@ -201,45 +99,76 @@ void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { void TriangleSet::TriangleOctreeCell::debugDump() { qDebug() << __FUNCTION__; - qDebug() << "bounds:" << getBounds(); - qDebug() << "depth:" << _depth; - qDebug() << "population:" << _population << "this level or below" + qDebug() << " bounds:" << getBounds(); + qDebug() << " depth:" << _depth; + qDebug() << " population:" << _population << "this level or below" << " ---- triangleIndices:" << _triangleIndices.size() << "in this cell"; - qDebug() << "child cells:" << _children.size(); + int numChildren = 0; + if (_children.first) { + numChildren++; + } else if (_children.second) { + numChildren++; + } + qDebug() << "child cells:" << numChildren; if (_depth < MAX_DEPTH) { - int childNum = 0; - for (auto& child : _children) { - qDebug() << "child:" << childNum; - child.second.debugDump(); - childNum++; + if (_children.first) { + qDebug() << "child: 0"; + _children.first->debugDump(); + } + if (_children.second) { + qDebug() << "child: 1"; + _children.second->debugDump(); } } } +std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { + std::pair toReturn; + int axis = 0; + // find biggest axis + glm::vec3 dimensions = _bounds.getDimensions(); + for (int i = 0; i < 3; i++) { + if (dimensions[i] >= dimensions[(i + 1) % 3] && dimensions[i] >= dimensions[(i + 2) % 3]) { + axis = i; + break; + } + } + + // The new boxes are half the size in the largest dimension + glm::vec3 newDimensions = dimensions; + newDimensions[axis] *= 0.5f; + toReturn.first.setBox(_bounds.getCorner(), newDimensions); + glm::vec3 offset = glm::vec3(0.0f); + offset[axis] = newDimensions[axis]; + toReturn.second.setBox(_bounds.getCorner() + offset, newDimensions); + return toReturn; +} + void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { - const Triangle& triangle = _allTriangles[triangleIndex]; _population++; + // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { + const Triangle& triangle = _allTriangles[triangleIndex]; + auto childBounds = getTriangleOctreeCellChildBounds(); - for (int child = 0; child < MAX_CHILDREN; child++) { - AABox childBounds = getBounds().getOctreeChild((AABox::OctreeChild)child); - - + auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { // if the child AABox would contain the triangle... - if (childBounds.contains(triangle)) { + if (childBound.contains(triangle)) { // if the child cell doesn't yet exist, create it... - if (_children.find((AABox::OctreeChild)child) == _children.end()) { - _children.insert( - std::pair - ((AABox::OctreeChild)child, TriangleOctreeCell(_allTriangles, childBounds, _depth + 1))); + if (!child) { + child = std::make_shared(_allTriangles, childBound, _depth + 1); } // insert the triangleIndex in the child cell - _children.at((AABox::OctreeChild)child).insert(triangleIndex); - return; + child->insert(triangleIndex); + return true; } + return false; + }; + if (insertOperator(childBounds.first, _children.first) || insertOperator(childBounds.second, _children.second)) { + return; } } // either we're at max depth, or the triangle doesn't fit in one of our @@ -247,6 +176,62 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { _triangleIndices.push_back(triangleIndex); } +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, + BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { + if (!_isBalanced) { + balanceOctree(); + } + + float localDistance = distance; + int trianglesTouched = 0; + bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + if (hit) { + distance = localDistance; + } + +#if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + } +#endif + return hit; +} + +// Determine of the given ray (origin/direction) in model space intersects with any triangles +// in the set. If an intersection occurs, the distance and surface normal will be provided. +bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, + float& distance, BoxFace& face, Triangle& triangle, bool precision, + int& trianglesTouched, bool allowBackface) { + bool intersectedSomething = false; + float bestDistance = FLT_MAX; + Triangle bestTriangle; + + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& thisTriangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findRayTriangleIntersection(origin, direction, thisTriangle, thisTriangleDistance, allowBackface)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + bestTriangle = thisTriangle; + intersectedSomething = true; + } + } + } + } else { + intersectedSomething = true; + bestDistance = distance; + } + + if (intersectedSomething) { + distance = bestDistance; + triangle = bestTriangle; + } + + return intersectedSomething; +} + bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -257,52 +242,81 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi float bestLocalDistance = FLT_MAX; BoxFace bestLocalFace; Triangle bestLocalTriangle; - glm::vec3 bestLocalNormal; bool intersects = false; - float boxDistance = FLT_MAX; - // if the pick intersects our bounding box, then continue - if (getBounds().findRayIntersection(origin, direction, boxDistance, bestLocalFace, bestLocalNormal)) { - // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) - // then we know that none of our triangles can represent a better intersection and we can return - if (boxDistance > distance) { - return false; - } - - // if we're not yet at the max depth, then check which child the triangle fits in - if (_depth < MAX_DEPTH) { - float bestChildDistance = FLT_MAX; - for (auto& child : _children) { - // check each child, if there's an intersection, it will return some distance that we need - // to compare against the other results, because there might be multiple intersections and - // we will always choose the best (shortest) intersection - float childDistance = bestChildDistance; - BoxFace childFace; - Triangle childTriangle; - if (child.second.findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { - if (childDistance < bestLocalDistance) { - bestLocalDistance = childDistance; - bestChildDistance = childDistance; - bestLocalFace = childFace; - bestLocalTriangle = childTriangle; - intersects = true; - } - } - } - } - // also check our local triangle set - float internalDistance = boxDistance; + // Check our local triangle set first + // The distance passed in here is the distance to our bounding box. If !precision, that distance is used + { + float internalDistance = distance; BoxFace internalFace; Triangle internalTriangle; if (findRayIntersectionInternal(origin, direction, internalDistance, internalFace, internalTriangle, precision, trianglesTouched, allowBackface)) { - if (internalDistance < bestLocalDistance) { - bestLocalDistance = internalDistance; - bestLocalFace = internalFace; - bestLocalTriangle = internalTriangle; - intersects = true; + bestLocalDistance = internalDistance; + bestLocalFace = internalFace; + bestLocalTriangle = internalTriangle; + intersects = true; + } + } + + // if we're not yet at the max depth, then check our children + if (_depth < MAX_DEPTH) { + std::list sortedTriangleCells; + auto sortingOperator = [&](std::shared_ptr& child) { + if (child) { + float priority = FLT_MAX; + if (child->getBounds().contains(origin)) { + priority = 0.0f; + } else { + float childBoundDistance = FLT_MAX; + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) { + // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) + if (childBoundDistance < bestLocalDistance) { + priority = childBoundDistance; + } + } + } + + if (priority < FLT_MAX) { + if (sortedTriangleCells.size() > 0 && priority < sortedTriangleCells.front().first) { + sortedTriangleCells.emplace_front(priority, child); + } else { + sortedTriangleCells.emplace_back(priority, child); + } + } + } + }; + sortingOperator(_children.first); + sortingOperator(_children.second); + + for (auto it = sortedTriangleCells.begin(); it != sortedTriangleCells.end(); ++it) { + const SortedTriangleCell& sortedTriangleCell = *it; + float childDistance = sortedTriangleCell.first; + // We can exit once childDistance > bestLocalDistance + if (childDistance > bestLocalDistance) { + break; + } + // If we're inside the child cell and !precision, we need the actual distance to the cell bounds + if (!precision && childDistance < EPSILON) { + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal); + } + BoxFace childFace; + Triangle childTriangle; + if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalTriangle = childTriangle; + intersects = true; + break; + } } } } + if (intersects) { distance = bestLocalDistance; face = bestLocalFace; @@ -311,6 +325,61 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi return intersects; } +bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, + float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { + if (!_isBalanced) { + balanceOctree(); + } + + float localDistance = parabolicDistance; + int trianglesTouched = 0; + bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + if (hit) { + parabolicDistance = localDistance; + } + +#if WANT_DEBUGGING + if (precision) { + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + } +#endif + return hit; +} + +bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, + const glm::vec3& acceleration, float& parabolicDistance, + BoxFace& face, Triangle& triangle, bool precision, + int& trianglesTouched, bool allowBackface) { + bool intersectedSomething = false; + float bestDistance = FLT_MAX; + Triangle bestTriangle; + + if (precision) { + for (const auto& triangleIndex : _triangleIndices) { + const auto& thisTriangle = _allTriangles[triangleIndex]; + float thisTriangleDistance; + trianglesTouched++; + if (findParabolaTriangleIntersection(origin, velocity, acceleration, thisTriangle, thisTriangleDistance, allowBackface)) { + if (thisTriangleDistance < bestDistance) { + bestDistance = thisTriangleDistance; + bestTriangle = thisTriangle; + intersectedSomething = true; + } + } + } + } else { + intersectedSomething = true; + bestDistance = parabolicDistance; + } + + if (intersectedSomething) { + parabolicDistance = bestDistance; + triangle = bestTriangle; + } + + return intersectedSomething; +} + bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, @@ -322,52 +391,81 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& float bestLocalDistance = FLT_MAX; BoxFace bestLocalFace; Triangle bestLocalTriangle; - glm::vec3 bestLocalNormal; bool intersects = false; - float boxDistance = FLT_MAX; - // if the pick intersects our bounding box, then continue - if (getBounds().findParabolaIntersection(origin, velocity, acceleration, boxDistance, bestLocalFace, bestLocalNormal)) { - // if the intersection with our bounding box, is greater than the current best distance (the distance passed in) - // then we know that none of our triangles can represent a better intersection and we can return - if (boxDistance > parabolicDistance) { - return false; - } - - // if we're not yet at the max depth, then check which child the triangle fits in - if (_depth < MAX_DEPTH) { - float bestChildDistance = FLT_MAX; - for (auto& child : _children) { - // check each child, if there's an intersection, it will return some distance that we need - // to compare against the other results, because there might be multiple intersections and - // we will always choose the best (shortest) intersection - float childDistance = bestChildDistance; - BoxFace childFace; - Triangle childTriangle; - if (child.second.findParabolaIntersection(origin, velocity, acceleration, childDistance, childFace, childTriangle, precision, trianglesTouched)) { - if (childDistance < bestLocalDistance) { - bestLocalDistance = childDistance; - bestChildDistance = childDistance; - bestLocalFace = childFace; - bestLocalTriangle = childTriangle; - intersects = true; - } - } - } - } - // also check our local triangle set - float internalDistance = boxDistance; + // Check our local triangle set first + // The distance passed in here is the distance to our bounding box. If !precision, that distance is used + { + float internalDistance = parabolicDistance; BoxFace internalFace; Triangle internalTriangle; if (findParabolaIntersectionInternal(origin, velocity, acceleration, internalDistance, internalFace, internalTriangle, precision, trianglesTouched, allowBackface)) { - if (internalDistance < bestLocalDistance) { - bestLocalDistance = internalDistance; - bestLocalFace = internalFace; - bestLocalTriangle = internalTriangle; - intersects = true; + bestLocalDistance = internalDistance; + bestLocalFace = internalFace; + bestLocalTriangle = internalTriangle; + intersects = true; + } + } + + // if we're not yet at the max depth, then check our children + if (_depth < MAX_DEPTH) { + std::list sortedTriangleCells; + auto sortingOperator = [&](std::shared_ptr& child) { + if (child) { + float priority = FLT_MAX; + if (child->getBounds().contains(origin)) { + priority = 0.0f; + } else { + float childBoundDistance = FLT_MAX; + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + if (child->getBounds().findParabolaIntersection(origin, velocity, acceleration, childBoundDistance, childBoundFace, childBoundNormal)) { + // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) + if (childBoundDistance < bestLocalDistance) { + priority = childBoundDistance; + } + } + } + + if (priority < FLT_MAX) { + if (sortedTriangleCells.size() > 0 && priority < sortedTriangleCells.front().first) { + sortedTriangleCells.emplace_front(priority, child); + } else { + sortedTriangleCells.emplace_back(priority, child); + } + } + } + }; + sortingOperator(_children.first); + sortingOperator(_children.second); + + for (auto it = sortedTriangleCells.begin(); it != sortedTriangleCells.end(); ++it) { + const SortedTriangleCell& sortedTriangleCell = *it; + float childDistance = sortedTriangleCell.first; + // We can exit once childDistance > bestLocalDistance + if (childDistance > bestLocalDistance) { + break; + } + // If we're inside the child cell and !precision, we need the actual distance to the cell bounds + if (!precision && childDistance < EPSILON) { + BoxFace childBoundFace; + glm::vec3 childBoundNormal; + sortedTriangleCell.second->getBounds().findParabolaIntersection(origin, velocity, acceleration, childDistance, childBoundFace, childBoundNormal); + } + BoxFace childFace; + Triangle childTriangle; + if (sortedTriangleCell.second->findParabolaIntersection(origin, velocity, acceleration, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (childDistance < bestLocalDistance) { + bestLocalDistance = childDistance; + bestLocalFace = childFace; + bestLocalTriangle = childTriangle; + intersects = true; + break; + } } } } + if (intersects) { parabolicDistance = bestLocalDistance; face = bestLocalFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 0b0d0a9ac5..a838e2d9e2 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -12,6 +12,7 @@ #pragma once #include +#include #include "AABox.h" #include "GeometryUtil.h" @@ -20,9 +21,8 @@ class TriangleSet { class TriangleOctreeCell { public: - TriangleOctreeCell(std::vector& allTriangles) : - _allTriangles(allTriangles) - { } + TriangleOctreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} + TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); @@ -40,8 +40,6 @@ class TriangleSet { void debugDump(); protected: - TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); - // checks our internal list of triangles bool findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, @@ -50,20 +48,22 @@ class TriangleSet { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); + std::pair getTriangleOctreeCellChildBounds(); + std::vector& _allTriangles; - std::map _children; - int _depth{ 0 }; - int _population{ 0 }; + std::pair, std::shared_ptr> _children; + int _depth { 0 }; + int _population { 0 }; AABox _bounds; std::vector _triangleIndices; friend class TriangleSet; }; + using SortedTriangleCell = std::pair>; + public: - TriangleSet() : - _triangleOctree(_triangles) - {} + TriangleSet() : _triangleOctree(_triangles) {} void debugDump(); @@ -87,8 +87,7 @@ public: const AABox& getBounds() const { return _bounds; } protected: - - bool _isBalanced{ false }; + bool _isBalanced { false }; std::vector _triangles; TriangleOctreeCell _triangleOctree; AABox _bounds; From c0966d738e334a17f94320639accdfa7f1ae2cde Mon Sep 17 00:00:00 2001 From: Simon Walton Date: Wed, 22 Aug 2018 16:40:49 -0700 Subject: [PATCH 140/207] Use EntityVersion for the EntityQueryInitialResultsComplete packet --- libraries/networking/src/udt/PacketHeaders.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 25503e192f..0f9a49da32 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -95,7 +95,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarIdentityRequest: return 22; case PacketType::EntityQueryInitialResultsComplete: - return 23; + return static_cast(EntityVersion::ParticleSpin); default: return 22; } From 874d9251e76996f1371c4d755db598bb66972906 Mon Sep 17 00:00:00 2001 From: amantley Date: Wed, 22 Aug 2018 17:32:46 -0700 Subject: [PATCH 141/207] demo version, next to implement in c++ --- scripts/developer/rotateApp.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index 4d18910335..60d78d66d9 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -260,6 +260,15 @@ function limitAngle(angle) { } function computeHandAzimuths(timeElapsed) { + + // var leftHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.lhandPose.translation); + // var rightHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.rhandPose.translation); + + + // var hipToLeftHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: leftHandPositionRigSpace.x, y: 0, z: leftHandPositionRigSpace.z }); + // var hipToRightHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: rightHandPositionRigSpace.x, y: 0, z: rightHandPositionRigSpace.z }); + // var hipToHandHalfway = Quat.slerp(hipToLeftHand, hipToRightHand, 0.5); + var leftHand = currentStateReadings.lhandPose.translation; var rightHand = currentStateReadings.rhandPose.translation; var head = currentStateReadings.headPose.translation; @@ -299,17 +308,21 @@ function computeHandAzimuths(timeElapsed) { // get it into radians too!! // average head and hands 50/50 - print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y) + // print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y); var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { - print("recenter the feet under the head"); + //print("recenter the feet under the head"); MyAvatar.triggerRotationRecenter(); hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; } - return Quat.fromVec3Degrees({ x: 0, y: leftRightMidpoint, z: 0 }); + // put a hard max on this easing function. + var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint; + print("rotate angle " + rotateAngle); + + return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 }); } From c474f38860afef8694d4736016c549dbd6bb3d9e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 22 Aug 2018 17:49:02 -0700 Subject: [PATCH 142/207] force coarse picking, sort avatars --- interface/src/Menu.cpp | 6 + interface/src/Menu.h | 1 + interface/src/avatar/AvatarManager.cpp | 174 +++++++++++++++---------- interface/src/avatar/AvatarManager.h | 2 + interface/src/raypick/ParabolaPick.cpp | 7 +- interface/src/raypick/RayPick.cpp | 7 +- libraries/avatars/src/AvatarData.h | 2 +- libraries/pointers/src/Pick.cpp | 4 + libraries/pointers/src/PickManager.h | 11 +- libraries/shared/src/TriangleSet.cpp | 54 ++++---- libraries/shared/src/TriangleSet.h | 18 +-- 11 files changed, 178 insertions(+), 108 deletions(-) diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index fe7083ea30..d4c74127c8 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -46,6 +46,7 @@ #include "InterfaceLogging.h" #include "LocationBookmarks.h" #include "DeferredLightingEffect.h" +#include "PickManager.h" #include "AmbientOcclusionEffect.h" #include "RenderShadowTask.h" @@ -688,6 +689,11 @@ Menu::Menu() { addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraints, 0, false, qApp, SLOT(setShowBulletConstraints(bool))); addCheckableActionToQMenuAndActionHash(physicsOptionsMenu, MenuOption::PhysicsShowBulletConstraintLimits, 0, false, qApp, SLOT(setShowBulletConstraintLimits(bool))); + // Developer > Picking >>> + MenuWrapper* pickingOptionsMenu = developerMenu->addMenu("Picking"); + addCheckableActionToQMenuAndActionHash(pickingOptionsMenu, MenuOption::ForceCoarsePicking, 0, false, + DependencyManager::get().data(), SLOT(setForceCoarsePicking(bool))); + // Developer > Display Crash Options addCheckableActionToQMenuAndActionHash(developerMenu, MenuOption::DisplayCrashOptions, 0, true); // Developer > Crash >>> diff --git a/interface/src/Menu.h b/interface/src/Menu.h index c489a3f0b0..4386bec5ae 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -221,6 +221,7 @@ namespace MenuOption { const QString NotificationSounds = "play_notification_sounds"; const QString NotificationSoundsSnapshot = "play_notification_sounds_snapshot"; const QString NotificationSoundsTablet = "play_notification_sounds_tablet"; + const QString ForceCoarsePicking = "Force Coarse Picking"; } #endif // hifi_Menu_h diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 139f44d58b..6fb35ec1da 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -553,6 +553,14 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic return result; } + // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to + // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code + // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking + // against the avatar is sort-of right, but you likely wont be able to pick against the arms. + + // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. + + std::vector sortedAvatars; auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -561,47 +569,60 @@ RayToAvatarIntersectionResult AvatarManager::findRayIntersectionVector(const Pic continue; } - float distance; - BoxFace face; - glm::vec3 surfaceNormal; - - SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); - - // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to - // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code - // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking - // against the avatar is sort-of right, but you likely wont be able to pick against the arms. - - // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. - + float distance = FLT_MAX; +#if 0 // if we weren't picking against the capsule, we would want to pick against the avatarBounds... - // AABox avatarBounds = avatarModel->getRenderableMeshBound(); - // if (!avatarBounds.findRayIntersection(ray.origin, normDirection, distance, face, surfaceNormal)) { - // // ray doesn't intersect avatar's bounding-box - // continue; - // } - + SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); + AABox avatarBounds = avatarModel->getRenderableMeshBound(); + if (avatarBounds.contains(ray.origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (avatarBounds.findRayIntersection(ray.origin, ray.direction, boundDistance, face, surfaceNormal)) { + distance = boundDistance; + } + } +#else glm::vec3 start; glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); - if (!intersects) { - // ray doesn't intersect avatar's capsule - continue; + findRayCapsuleIntersection(ray.origin, ray.direction, start, end, radius, distance); +#endif + + if (distance < FLT_MAX) { + sortedAvatars.emplace_back(distance, avatar); + } + } + + if (sortedAvatars.size() > 1) { + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); + } + + for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) { + const SortedAvatar& sortedAvatar = *it; + // We can exit once avatarCapsuleDistance > bestDistance + if (sortedAvatar.first > result.distance) { + break; } + float distance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; QVariantMap extraInfo; - intersects = avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, - distance, face, surfaceNormal, extraInfo, true); - - if (intersects && (!result.intersects || distance < result.distance)) { - result.intersects = true; - result.avatarID = avatar->getID(); - result.distance = distance; - result.face = face; - result.surfaceNormal = surfaceNormal; - result.extraInfo = extraInfo; + SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel(); + if (avatarModel->findRayIntersectionAgainstSubMeshes(ray.origin, ray.direction, distance, face, surfaceNormal, extraInfo, true)) { + if (distance < result.distance) { + result.intersects = true; + result.avatarID = sortedAvatar.second->getID(); + result.distance = distance; + result.face = face; + result.surfaceNormal = surfaceNormal; + result.extraInfo = extraInfo; + } } } @@ -625,6 +646,14 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector return result; } + // It's better to intersect the ray against the avatar's actual mesh, but this is currently difficult to + // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code + // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking + // against the avatar is sort-of right, but you likely wont be able to pick against the arms. + + // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. + + std::vector sortedAvatars; auto avatarHashCopy = getHashCopy(); for (auto avatarData : avatarHashCopy) { auto avatar = std::static_pointer_cast(avatarData); @@ -633,47 +662,60 @@ ParabolaToAvatarIntersectionResult AvatarManager::findParabolaIntersectionVector continue; } - float parabolicDistance; - BoxFace face; - glm::vec3 surfaceNormal; - - SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); - - // It's better to intersect the parabola against the avatar's actual mesh, but this is currently difficult to - // do, because the transformed mesh data only exists over in GPU-land. As a compromise, this code - // intersects against the avatars capsule and then against the (T-pose) mesh. The end effect is that picking - // against the avatar is sort-of right, but you likely wont be able to pick against the arms. - - // TODO -- find a way to extract transformed avatar mesh data from the rendering engine. - + float distance = FLT_MAX; +#if 0 // if we weren't picking against the capsule, we would want to pick against the avatarBounds... - // AABox avatarBounds = avatarModel->getRenderableMeshBound(); - // if (!avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal)) { - // // parabola doesn't intersect avatar's bounding-box - // continue; - // } - + SkeletonModelPointer avatarModel = avatar->getSkeletonModel(); + AABox avatarBounds = avatarModel->getRenderableMeshBound(); + if (avatarBounds.contains(pick.origin)) { + distance = 0.0f; + } else { + float boundDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; + if (avatarBounds.findParabolaIntersection(pick.origin, pick.velocity, pick.acceleration, boundDistance, face, surfaceNormal)) { + distance = boundDistance; + } + } +#else glm::vec3 start; glm::vec3 end; float radius; avatar->getCapsule(start, end, radius); - bool intersects = findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), parabolicDistance); - if (!intersects) { - // ray doesn't intersect avatar's capsule - continue; + findParabolaCapsuleIntersection(pick.origin, pick.velocity, pick.acceleration, start, end, radius, avatar->getWorldOrientation(), distance); +#endif + + if (distance < FLT_MAX) { + sortedAvatars.emplace_back(distance, avatar); + } + } + + if (sortedAvatars.size() > 1) { + static auto comparator = [](const SortedAvatar& left, const SortedAvatar& right) { return left.first > right.first; }; + std::sort(sortedAvatars.begin(), sortedAvatars.end(), comparator); + } + + for (auto it = sortedAvatars.begin(); it != sortedAvatars.end(); ++it) { + const SortedAvatar& sortedAvatar = *it; + // We can exit once avatarCapsuleDistance > bestDistance + if (sortedAvatar.first > result.parabolicDistance) { + break; } + float parabolicDistance = FLT_MAX; + BoxFace face; + glm::vec3 surfaceNormal; QVariantMap extraInfo; - intersects = avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, - parabolicDistance, face, surfaceNormal, extraInfo, true); - - if (intersects && (!result.intersects || parabolicDistance < result.parabolicDistance)) { - result.intersects = true; - result.avatarID = avatar->getID(); - result.parabolicDistance = parabolicDistance; - result.face = face; - result.surfaceNormal = surfaceNormal; - result.extraInfo = extraInfo; + SkeletonModelPointer avatarModel = sortedAvatar.second->getSkeletonModel(); + if (avatarModel->findParabolaIntersectionAgainstSubMeshes(pick.origin, pick.velocity, pick.acceleration, parabolicDistance, face, surfaceNormal, extraInfo, true)) { + if (parabolicDistance < result.parabolicDistance) { + result.intersects = true; + result.avatarID = sortedAvatar.second->getID(); + result.parabolicDistance = parabolicDistance; + result.face = face; + result.surfaceNormal = surfaceNormal; + result.extraInfo = extraInfo; + } } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ecf9a2d735..a9c04e8473 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -27,6 +27,8 @@ #include "AvatarMotionState.h" #include "MyAvatar.h" +using SortedAvatar = std::pair>; + /**jsdoc * The AvatarManager API has properties and methods which manage Avatars within the same domain. * diff --git a/interface/src/raypick/ParabolaPick.cpp b/interface/src/raypick/ParabolaPick.cpp index b3e3f16345..f4db1df25a 100644 --- a/interface/src/raypick/ParabolaPick.cpp +++ b/interface/src/raypick/ParabolaPick.cpp @@ -13,11 +13,13 @@ #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" +#include "PickManager.h" PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToEntityIntersectionResult entityRes = - DependencyManager::get()->findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), + DependencyManager::get()->findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.parabolicDistance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); @@ -28,8 +30,9 @@ PickResultPointer ParabolaPick::getEntityIntersection(const PickParabola& pick) PickResultPointer ParabolaPick::getOverlayIntersection(const PickParabola& pick) { if (glm::length2(pick.acceleration) > EPSILON && glm::length2(pick.velocity) > EPSILON) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); ParabolaToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findParabolaIntersectionVector(pick, !getFilter().doesPickCoarse(), + qApp->getOverlays().findParabolaIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.parabolicDistance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); diff --git a/interface/src/raypick/RayPick.cpp b/interface/src/raypick/RayPick.cpp index 96b41dcc72..fde1da3f87 100644 --- a/interface/src/raypick/RayPick.cpp +++ b/interface/src/raypick/RayPick.cpp @@ -13,10 +13,12 @@ #include "avatar/AvatarManager.h" #include "scripting/HMDScriptingInterface.h" #include "DependencyManager.h" +#include "PickManager.h" PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToEntityIntersectionResult entityRes = - DependencyManager::get()->findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), + DependencyManager::get()->findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (entityRes.intersects) { return std::make_shared(IntersectionType::ENTITY, entityRes.entityID, entityRes.distance, entityRes.intersection, pick, entityRes.surfaceNormal, entityRes.extraInfo); @@ -26,8 +28,9 @@ PickResultPointer RayPick::getEntityIntersection(const PickRay& pick) { } PickResultPointer RayPick::getOverlayIntersection(const PickRay& pick) { + bool precisionPicking = !(getFilter().doesPickCoarse() || DependencyManager::get()->getForceCoarsePicking()); RayToOverlayIntersectionResult overlayRes = - qApp->getOverlays().findRayIntersectionVector(pick, !getFilter().doesPickCoarse(), + qApp->getOverlays().findRayIntersectionVector(pick, precisionPicking, getIncludeItemsAs(), getIgnoreItemsAs(), !getFilter().doesPickInvisible(), !getFilter().doesPickNonCollidable()); if (overlayRes.intersects) { return std::make_shared(IntersectionType::OVERLAY, overlayRes.overlayID, overlayRes.distance, overlayRes.intersection, pick, overlayRes.surfaceNormal, overlayRes.extraInfo); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 53a2a69119..9d4ba3902d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1559,7 +1559,7 @@ class RayToAvatarIntersectionResult { public: bool intersects { false }; QUuid avatarID; - float distance { 0.0f }; + float distance { FLT_MAX }; BoxFace face; glm::vec3 intersection; glm::vec3 surfaceNormal; diff --git a/libraries/pointers/src/Pick.cpp b/libraries/pointers/src/Pick.cpp index 4315409bdb..7ac53a2643 100644 --- a/libraries/pointers/src/Pick.cpp +++ b/libraries/pointers/src/Pick.cpp @@ -72,11 +72,15 @@ PickResultPointer PickQuery::getPrevPickResult() const { void PickQuery::setIgnoreItems(const QVector& ignoreItems) { withWriteLock([&] { _ignoreItems = ignoreItems; + // We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same ignoreItems in a different order + std::sort(_ignoreItems.begin(), _ignoreItems.end(), std::less()); }); } void PickQuery::setIncludeItems(const QVector& includeItems) { withWriteLock([&] { _includeItems = includeItems; + // We sort these items here so the PickCacheOptimizer can catch cases where two picks have the same includeItems in a different order + std::sort(_includeItems.begin(), _includeItems.end(), std::less()); }); } \ No newline at end of file diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 242550d837..595c43e71d 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -16,7 +16,10 @@ #include -class PickManager : public Dependency, protected ReadWriteLockable { +#include + +class PickManager : public QObject, public Dependency, protected ReadWriteLockable { + Q_OBJECT SINGLETON_DEPENDENCY public: @@ -53,7 +56,13 @@ public: unsigned int getPerFrameTimeBudget() const { return _perFrameTimeBudget; } void setPerFrameTimeBudget(unsigned int numUsecs) { _perFrameTimeBudget = numUsecs; } + bool getForceCoarsePicking() { return _forceCoarsePicking; } + +public slots: + void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } + protected: + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index f3c0ef5776..1dc8c5657a 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -29,7 +29,7 @@ void TriangleSet::clear() { _bounds.clear(); _isBalanced = false; - _triangleOctree.clear(); + _triangleTree.clear(); } bool TriangleSet::convexHullContains(const glm::vec3& point) const { @@ -53,16 +53,16 @@ void TriangleSet::debugDump() { qDebug() << __FUNCTION__; qDebug() << "bounds:" << getBounds(); qDebug() << "triangles:" << size() << "at top level...."; - qDebug() << "----- _triangleOctree -----"; - _triangleOctree.debugDump(); + qDebug() << "----- _triangleTree -----"; + _triangleTree.debugDump(); } -void TriangleSet::balanceOctree() { - _triangleOctree.reset(_bounds); +void TriangleSet::balanceTree() { + _triangleTree.reset(_bounds); // insert all the triangles for (size_t i = 0; i < _triangles.size(); i++) { - _triangleOctree.insert(i); + _triangleTree.insert(i); } _isBalanced = true; @@ -77,13 +77,13 @@ void TriangleSet::balanceOctree() { // With a k-d tree: 2 ^ MAX_DEPTH = 4096 leaves static const int MAX_DEPTH = 12; -TriangleSet::TriangleOctreeCell::TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : +TriangleSet::TriangleTreeCell::TriangleTreeCell(std::vector& allTriangles, const AABox& bounds, int depth) : _allTriangles(allTriangles) { reset(bounds, depth); } -void TriangleSet::TriangleOctreeCell::clear() { +void TriangleSet::TriangleTreeCell::clear() { _population = 0; _triangleIndices.clear(); _bounds.clear(); @@ -91,13 +91,13 @@ void TriangleSet::TriangleOctreeCell::clear() { _children.second.reset(); } -void TriangleSet::TriangleOctreeCell::reset(const AABox& bounds, int depth) { +void TriangleSet::TriangleTreeCell::reset(const AABox& bounds, int depth) { clear(); _bounds = bounds; _depth = depth; } -void TriangleSet::TriangleOctreeCell::debugDump() { +void TriangleSet::TriangleTreeCell::debugDump() { qDebug() << __FUNCTION__; qDebug() << " bounds:" << getBounds(); qDebug() << " depth:" << _depth; @@ -123,7 +123,7 @@ void TriangleSet::TriangleOctreeCell::debugDump() { } } -std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellChildBounds() { +std::pair TriangleSet::TriangleTreeCell::getTriangleTreeCellChildBounds() { std::pair toReturn; int axis = 0; // find biggest axis @@ -145,20 +145,20 @@ std::pair TriangleSet::TriangleOctreeCell::getTriangleOctreeCellCh return toReturn; } -void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { +void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) { _population++; // if we're not yet at the max depth, then check which child the triangle fits in if (_depth < MAX_DEPTH) { const Triangle& triangle = _allTriangles[triangleIndex]; - auto childBounds = getTriangleOctreeCellChildBounds(); + auto childBounds = getTriangleTreeCellChildBounds(); - auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { + auto insertOperator = [&](const AABox& childBound, std::shared_ptr& child) { // if the child AABox would contain the triangle... if (childBound.contains(triangle)) { // if the child cell doesn't yet exist, create it... if (!child) { - child = std::make_shared(_allTriangles, childBound, _depth + 1); + child = std::make_shared(_allTriangles, childBound, _depth + 1); } // insert the triangleIndex in the child cell @@ -179,19 +179,19 @@ void TriangleSet::TriangleOctreeCell::insert(size_t triangleIndex) { bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { - balanceOctree(); + balanceTree(); } float localDistance = distance; int trianglesTouched = 0; - bool hit = _triangleOctree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { distance = localDistance; } #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size(); } #endif return hit; @@ -199,7 +199,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& // Determine of the given ray (origin/direction) in model space intersects with any triangles // in the set. If an intersection occurs, the distance and surface normal will be provided. -bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, +bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { bool intersectedSomething = false; @@ -232,7 +232,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersectionInternal(const glm::vec return intersectedSomething; } -bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { if (_population < 1) { @@ -261,7 +261,7 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi // if we're not yet at the max depth, then check our children if (_depth < MAX_DEPTH) { std::list sortedTriangleCells; - auto sortingOperator = [&](std::shared_ptr& child) { + auto sortingOperator = [&](std::shared_ptr& child) { if (child) { float priority = FLT_MAX; if (child->getBounds().contains(origin)) { @@ -328,25 +328,25 @@ bool TriangleSet::TriangleOctreeCell::findRayIntersection(const glm::vec3& origi bool TriangleSet::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { - balanceOctree(); + balanceTree(); } float localDistance = parabolicDistance; int trianglesTouched = 0; - bool hit = _triangleOctree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findParabolaIntersection(origin, velocity, acceleration, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { parabolicDistance = localDistance; } #if WANT_DEBUGGING if (precision) { - qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleOctree._population << "_triangles.size:" << _triangles.size(); + qDebug() << "trianglesTouched :" << trianglesTouched << "out of:" << _triangleTree._population << "_triangles.size:" << _triangles.size(); } #endif return hit; } -bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, +bool TriangleSet::TriangleTreeCell::findParabolaIntersectionInternal(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -380,7 +380,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersectionInternal(const glm return intersectedSomething; } -bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, +bool TriangleSet::TriangleTreeCell::findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface) { @@ -410,7 +410,7 @@ bool TriangleSet::TriangleOctreeCell::findParabolaIntersection(const glm::vec3& // if we're not yet at the max depth, then check our children if (_depth < MAX_DEPTH) { std::list sortedTriangleCells; - auto sortingOperator = [&](std::shared_ptr& child) { + auto sortingOperator = [&](std::shared_ptr& child) { if (child) { float priority = FLT_MAX; if (child->getBounds().contains(origin)) { diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index a838e2d9e2..84e362f0d0 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -19,10 +19,10 @@ class TriangleSet { - class TriangleOctreeCell { + class TriangleTreeCell { public: - TriangleOctreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} - TriangleOctreeCell(std::vector& allTriangles, const AABox& bounds, int depth); + TriangleTreeCell(std::vector& allTriangles) : _allTriangles(allTriangles) {} + TriangleTreeCell(std::vector& allTriangles, const AABox& bounds, int depth); void insert(size_t triangleIndex); void reset(const AABox& bounds, int depth = 0); @@ -48,10 +48,10 @@ class TriangleSet { float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); - std::pair getTriangleOctreeCellChildBounds(); + std::pair getTriangleTreeCellChildBounds(); std::vector& _allTriangles; - std::pair, std::shared_ptr> _children; + std::pair, std::shared_ptr> _children; int _depth { 0 }; int _population { 0 }; AABox _bounds; @@ -60,10 +60,10 @@ class TriangleSet { friend class TriangleSet; }; - using SortedTriangleCell = std::pair>; + using SortedTriangleCell = std::pair>; public: - TriangleSet() : _triangleOctree(_triangles) {} + TriangleSet() : _triangleTree(_triangles) {} void debugDump(); @@ -74,7 +74,7 @@ public: bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); - void balanceOctree(); + void balanceTree(); void reserve(size_t size) { _triangles.reserve(size); } // reserve space in the datastructure for size number of triangles size_t size() const { return _triangles.size(); } @@ -89,6 +89,6 @@ public: protected: bool _isBalanced { false }; std::vector _triangles; - TriangleOctreeCell _triangleOctree; + TriangleTreeCell _triangleTree; AABox _bounds; }; From e3ad5adcaec07108360e340302cd643c61e4af31 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 06:49:35 -0700 Subject: [PATCH 143/207] first approach vertical alignment --- libraries/avatars/src/AvatarData.cpp | 2 +- libraries/avatars/src/AvatarData.h | 4 ++ libraries/avatars/src/AvatarHashMap.cpp | 72 +++++++++++++++++++++++-- libraries/avatars/src/AvatarHashMap.h | 2 + 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c1ec19b307..63a905aa6e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -918,7 +918,7 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]); + auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + glm::vec3(0, 2 * _surrogateIndex, 0); if (_globalPosition != newValue) { _globalPosition = newValue; _globalPositionChanged = usecTimestampNow(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 53a2a69119..b3443dbd8a 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -1186,6 +1186,9 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {} virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {} + void setSurrogateIndex(int surrogateIndex) { _surrogateIndex = surrogateIndex; } + int getSurrogateIndex() { return _surrogateIndex; } + signals: /**jsdoc @@ -1443,6 +1446,7 @@ protected: udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; + int _surrogateIndex{ 0 }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 64b26131be..a84d83e663 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,6 +21,8 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" +const int SURROGATE_COUNT = 2; + AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); @@ -139,14 +141,37 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointerisIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); + + if (isNewAvatar) { QWriteLocker locker(&_hashLock); _pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar }); + std::vector surrogateIDs; + for (int i = 0; i < SURROGATE_COUNT; i++) { + QUuid surrogateID = QUuid::createUuid(); + surrogateIDs.push_back(surrogateID); + auto surrogateAvatar = addAvatar(surrogateID, sendingNode); + surrogateAvatar->setSurrogateIndex(i + 1); + surrogateAvatar->parseDataFromBuffer(byteArray); + _pendingAvatars.insert(surrogateID, { std::chrono::steady_clock::now(), 0, surrogateAvatar }); + } + _surrogates.insert(std::pair>(sessionUUID, surrogateIDs)); + } else { + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); + if (!isNewAvatar) { + surrogateAvatar->parseDataFromBuffer(byteArray); + } + } + } // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); + + avatar->parseDataFromBuffer(byteArray); return avatar; } else { // create a dummy AvatarData class to throw this data on the ground @@ -191,13 +216,22 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer bool displayNameChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + auto surrogateIDs = _surrogates[identityUUID]; + for (auto id : surrogateIDs) { + auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); + if (!isNewAvatar) { + surrogateAvatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + } + } } } -void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { +void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { + message->seek(0); while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + avatarID = sessionUUID; // grab the avatar so we can ask it to process trait data bool isNewAvatar; @@ -225,10 +259,12 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (packetTraitVersion > lastProcessedVersions[traitType]) { avatar->processTrait(traitType, message->read(traitBinarySize)); lastProcessedVersions[traitType] = packetTraitVersion; - } else { + } + else { skipBinaryTrait = true; } - } else { + } + else { AvatarTraits::TraitInstanceID traitInstanceID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -238,11 +274,13 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (packetTraitVersion > processedInstanceVersion) { if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } else { + } + else { avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); } processedInstanceVersion = packetTraitVersion; - } else { + } + else { skipBinaryTrait = true; } } @@ -258,6 +296,18 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess } } +void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + processAvatarTraits(avatarID, message, sendingNode); + auto surrogateIDs = _surrogates[avatarID]; + for (auto id : surrogateIDs) { + processAvatarTraits(id, message, sendingNode); + } + } +} + void AvatarHashMap::processKillAvatar(QSharedPointer message, SharedNodePointer sendingNode) { // read the node id QUuid sessionUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -265,6 +315,10 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S KillAvatarReason reason; message->readPrimitive(&reason); removeAvatar(sessionUUID, reason); + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + removeAvatar(id, reason); + } } void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { @@ -276,6 +330,14 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo if (removedAvatar) { handleRemovedAvatar(removedAvatar, removalReason); } + auto surrogateIDs = _surrogates[sessionUUID]; + for (auto id : surrogateIDs) { + _pendingAvatars.remove(id); + auto removedSurrogate = _avatarHash.take(id); + if (removedSurrogate) { + handleRemovedAvatar(removedSurrogate, removalReason); + } + } } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index ba16fa9568..41981df693 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -134,6 +134,7 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); + void processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -167,6 +168,7 @@ protected: mutable QReadWriteLock _hashLock; std::unordered_map _processedTraitVersions; + std::map> _surrogates; private: QUuid _lastOwnerSessionUUID; }; From 30ef544c6debac94cf1df5a0f5ee714c029143a9 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 23 Aug 2018 15:57:14 +0100 Subject: [PATCH 144/207] starting the c++ version of shoulder rotation --- interface/src/avatar/MyAvatar.cpp | 11 +++++++++++ interface/src/avatar/MyAvatar.h | 5 +++++ 2 files changed, 16 insertions(+) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 44ee5e30c6..6211e6f0d8 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -801,6 +801,17 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } } +void MyAvatar::computeHandAzimuth() { + controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); + controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + glm::vec3 localLookat(0.0f, 0.0f, 1.0f); + glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.rotation * localLookat; + glm::vec3 lefttHandRigSpace = leftHandPoseAvatarSpace.rotation * localLookat; + + _hipToHandController = glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z); + +} + void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) { assert(QThread::currentThread() == thread()); auto userInputMapper = DependencyManager::get(); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 10b5884d5a..6b7c4e83a1 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -320,6 +320,9 @@ public: // as it moves through the world. void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); + // compute the hip to hand average azimuth. + void computeHandAzimuth(); + // read the location of a hand controller and save the transform void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); @@ -1658,6 +1661,8 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; + glm::vec2 _hipToHandController; // facing vector in xz plane (sensor space) + float _currentStandingHeight { 0.0f }; bool _resetMode { true }; RingBufferHistory _recentModeReadings; From b7fa47da359dbc2394c85a82280b817ed14197f7 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 23 Aug 2018 10:37:55 -0700 Subject: [PATCH 145/207] cr feedback --- .../qml/hifi/commerce/wallet/Help.qml | 41 ---- .../qml/hifi/commerce/wallet/Security.qml | 207 ------------------ 2 files changed, 248 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index 1c37ded4ee..6d8fc3c33f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -64,47 +64,6 @@ Item { answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ Get it by going to

BankOfHighFidelity. and meeting with the banker!"; } - /* ListElement { - isExpanded: false; - question: "What are private keys and where are they stored?"; - answer: - "A private key is a secret piece of text that is used to prove ownership, unlock confidential information, and sign transactions. \ -In High Fidelity, your private key is used to securely access the contents of your Wallet and Purchases. \ -After wallet setup, a hifikey file is stored on your computer in High Fidelity Interface's AppData directory. \ -Your hifikey file contains your private key and is protected by your wallet passphrase. \ -

It is very important to back up your hifikey file! \ -Tap here to open the folder where your HifiKeys are stored on your main display." - }*/ - /*ListElement { - isExpanded: false; - question: "How do I back up my private keys?"; - answer: "You can back up your hifikey file (which contains your private key and is encrypted using your wallet passphrase) by copying it to a USB flash drive, or to a service like Dropbox or Google Drive. \ -Restore your hifikey file by replacing the file in Interface's AppData directory with your backup copy. \ -Others with access to your back up should not be able to spend your HFC without your passphrase. \ -Tap here to open the folder where your HifiKeys are stored on your main display."; - }*/ - /*ListElement { - isExpanded: false; - question: "What happens if I lose my private keys?"; - answer: "We cannot stress enough that you should keep a backup! For security reasons, High Fidelity does not keep a copy, and cannot restore it for you. \ -If you lose your private key, you will no longer have access to the contents of your Wallet or My Purchases. \ -Here are some things to try:
    \ -
  • If you have backed up your hifikey file before, search your backup location
  • \ -
  • Search your AppData directory in the last machine you used to set up the Wallet
  • \ -
  • If you are a developer and have installed multiple builds of High Fidelity, your hifikey file might be in another folder
  • \ -


As a last resort, you can set up your Wallet again and generate a new hifikey file. \ -Unfortunately, this means you will start with 0 HFC and your purchased items will not be transferred over."; - }*/ - /*ListElement { - isExpanded: false; - question: "What if I forget my wallet passphrase?"; - answer: "Your wallet passphrase is used to encrypt your private keys. Please write it down and store it securely! \ -

If you forget your passphrase, you will no longer be able to decrypt the hifikey file that the passphrase protects. \ -You will also no longer have access to the contents of your Wallet or My Purchases. \ -For security reasons, High Fidelity does not keep a copy of your passphrase, and can't restore it for you. \ -

If you still cannot remember your wallet passphrase, you can set up your Wallet again and generate a new hifikey file. \ -Unfortunately, this means you will start with 0 HFC and your purchased items will not be transferred over."; - }*/ ListElement { isExpanded: false; question: "How do I send HFC to other people?"; diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index 08eb94a23c..da3331ebfc 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -88,75 +88,6 @@ Item { color: hifi.colors.faintGray; } - /* - Item { - id: changePassphraseContainer; - anchors.top: securityTextSeparator.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - height: 75; - - HiFiGlyphs { - id: changePassphraseImage; - text: hifi.glyphs.passphrase; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - RalewaySemiBold { - text: "Passphrase"; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: changePassphraseImage.right; - anchors.leftMargin: 30; - width: 50; - // Text size - size: 18; - // Style - color: hifi.colors.white; - } - - // "Change Passphrase" button - HifiControlsUit.Button { - id: changePassphraseButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - width: 140; - height: 40; - text: "Change"; - onClicked: { - sendSignalToWallet({method: 'walletSecurity_changePassphrase'}); - } - } - } - - Rectangle { - id: changePassphraseSeparator; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.right: parent.right; - anchors.top: changePassphraseContainer.bottom; - anchors.topMargin: 8; - // Style - color: hifi.colors.faintGray; - } - */ - Item { id: changeSecurityImageContainer; anchors.top: securityTextSeparator.bottom; // changePassphraseSeparator.bottom; @@ -209,144 +140,6 @@ Item { } } } - /* - Rectangle { - id: privateKeysSeparator; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.right: parent.right; - anchors.top: changeSecurityImageContainer.bottom; - anchors.topMargin: 8; - // Style - color: hifi.colors.faintGray; - } - - Item { - id: yourPrivateKeysContainer; - anchors.top: privateKeysSeparator.bottom; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - anchors.bottom: parent.bottom; - - onVisibleChanged: { - if (visible) { - Commerce.getKeyFilePathIfExists(); - } - } - - HiFiGlyphs { - id: yourPrivateKeysImage; - text: hifi.glyphs.walletKey; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 20; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - RalewaySemiBold { - id: yourPrivateKeysText; - text: "Private Keys"; - size: 18; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 32; - anchors.left: yourPrivateKeysImage.right; - anchors.leftMargin: 30; - anchors.right: parent.right; - height: 30; - // Style - color: hifi.colors.white; - } - - // Text below "private keys" - RalewayRegular { - id: explanitoryText; - text: "Your money and purchases are secured with private keys that only you have access to."; - // Text size - size: 18; - // Anchors - anchors.top: yourPrivateKeysText.bottom; - anchors.topMargin: 10; - anchors.left: yourPrivateKeysText.left; - anchors.right: yourPrivateKeysText.right; - height: paintedHeight; - // Style - color: hifi.colors.white; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - } - - Rectangle { - id: removeHmdContainer; - z: 998; - visible: false; - - gradient: Gradient { - GradientStop { - position: 0.2; - color: hifi.colors.baseGrayHighlight; - } - GradientStop { - position: 1.0; - color: hifi.colors.baseGrayShadow; - } - } - anchors.fill: backupInstructionsButton; - radius: 5; - MouseArea { - anchors.fill: parent; - propagateComposedEvents: false; - hoverEnabled: true; - } - - RalewayBold { - anchors.fill: parent; - text: "INSTRUCTIONS OPEN ON DESKTOP"; - size: 15; - color: hifi.colors.white; - verticalAlignment: Text.AlignVCenter; - horizontalAlignment: Text.AlignHCenter; - } - - Timer { - id: removeHmdContainerTimer; - interval: 5000; - onTriggered: removeHmdContainer.visible = false - } - } - - HifiControlsUit.Button { - id: backupInstructionsButton; - text: "View Backup Instructions"; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.left: explanitoryText.left; - anchors.right: explanitoryText.right; - anchors.top: explanitoryText.bottom; - anchors.topMargin: 16; - height: 40; - - onClicked: { - var keyPath = "file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/')); - Qt.openUrlExternally(keyPath + "/backup_instructions.html"); - Qt.openUrlExternally(keyPath); - removeHmdContainer.visible = true; - removeHmdContainerTimer.start(); - } - } - } - */ } // From b7d074aaa4e23efe8c50c5f5c5b23f6c35bbc764 Mon Sep 17 00:00:00 2001 From: Howard Stearns Date: Thu, 23 Aug 2018 10:41:19 -0700 Subject: [PATCH 146/207] remove comment --- interface/resources/qml/hifi/commerce/wallet/Security.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index da3331ebfc..e021328ebe 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -90,7 +90,7 @@ Item { Item { id: changeSecurityImageContainer; - anchors.top: securityTextSeparator.bottom; // changePassphraseSeparator.bottom; + anchors.top: securityTextSeparator.bottom; anchors.topMargin: 8; anchors.left: parent.left; anchors.leftMargin: 40; From 15665b25e8fc3d751b5bc647862fc0cf46923266 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Aug 2018 16:05:18 -0700 Subject: [PATCH 147/207] give OtherAvatars a proxy in workload --- interface/src/Application.cpp | 4 ++ interface/src/avatar/AvatarManager.cpp | 55 +++++++++++++++++++++----- interface/src/avatar/AvatarManager.h | 9 +++++ interface/src/avatar/OtherAvatar.cpp | 13 ++++++ interface/src/avatar/OtherAvatar.h | 11 ++++++ 5 files changed, 82 insertions(+), 10 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1d515392b0..c0e024ec1e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1094,6 +1094,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // Set File Logger Session UUID auto avatarManager = DependencyManager::get(); auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; + if (avatarManager) { + workload::SpacePointer space = getEntities()->getWorkloadSpace(); + avatarManager->setSpace(space); + } auto accountManager = DependencyManager::get(); _logger->setSessionID(accountManager->getSessionID()); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 0d180bc40d..551847324a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -81,6 +81,22 @@ AvatarManager::AvatarManager(QObject* parent) : }); } +AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) { + AvatarSharedPointer avatar = AvatarHashMap::addAvatar(sessionUUID, mixerWeakPointer); + + const auto otherAvatar = std::static_pointer_cast(avatar); + if (otherAvatar && _space) { + std::unique_lock lock(_spaceLock); + auto spaceIndex = _space->allocateID(); + otherAvatar->setSpaceIndex(spaceIndex); + workload::Sphere sphere(otherAvatar->getWorldPosition(), otherAvatar->getBoundingRadius()); + workload::Transaction transaction; + transaction.reset(spaceIndex, sphere, workload::Owner(otherAvatar)); + _space->enqueueTransaction(transaction); + } + return avatar; +} + AvatarManager::~AvatarManager() { assert(_motionStates.empty()); } @@ -104,6 +120,11 @@ void AvatarManager::init() { } } +void AvatarManager::setSpace(workload::SpacePointer& space ) { + assert(!_space); + _space = space; +} + void AvatarManager::updateMyAvatar(float deltaTime) { bool showWarnings = Menu::getInstance()->isOptionChecked(MenuOption::PipelineWarnings); PerformanceWarning warn(showWarnings, "AvatarManager::updateMyAvatar()"); @@ -194,18 +215,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { int numAVatarsNotUpdated = 0; bool physicsEnabled = qApp->isPhysicsEnabled(); - render::Transaction transaction; + render::Transaction renderTransaction; + workload::Transaction workloadTransaction; while (!sortedAvatars.empty()) { const SortableAvatar& sortData = sortedAvatars.top(); - const auto avatar = std::static_pointer_cast(sortData.getAvatar()); - const auto otherAvatar = std::static_pointer_cast(sortData.getAvatar()); + const auto avatar = std::static_pointer_cast(sortData.getAvatar()); + // TODO: to help us scale to more avatars it would be nice to not have to poll orb state here // if the geometry is loaded then turn off the orb if (avatar->getSkeletonModel()->isLoaded()) { // remove the orb if it is there - otherAvatar->removeOrb(); + avatar->removeOrb(); } else { - otherAvatar->updateOrbPosition(); + avatar->updateOrbPosition(); } bool ignoring = DependencyManager::get()->isPersonalMutingNode(avatar->getID()); @@ -241,15 +263,16 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { numAvatarsUpdated++; } avatar->simulate(deltaTime, inView); - avatar->updateRenderItem(transaction); + avatar->updateRenderItem(renderTransaction); + avatar->updateSpaceProxy(workloadTransaction); avatar->setLastRenderUpdateTime(startTime); } else { // we've spent our full time budget --> bail on the rest of the avatar updates // --> more avatars may freeze until their priority trickles up - // --> some scale or fade animations may glitch + // --> some scale animations may glitch // --> some avatar velocity measurements may be a little off - // no time simulate, but we take the time to count how many were tragically missed + // no time to simulate, but we take the time to count how many were tragically missed bool inView = sortData.getPriority() > OUT_OF_VIEW_THRESHOLD; if (!inView) { break; @@ -275,6 +298,14 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (_shouldRender) { qApp->getMain3DScene()->enqueueTransaction(transaction); } + + if (!_spaceProxiesToDelete.empty() && _space) { + std::unique_lock lock(_spaceLock); + workloadTransaction.remove(_spaceProxiesToDelete); + _spaceProxiesToDelete.clear(); + } + _space->enqueueTransaction(workloadTransaction); + _numAvatarsUpdated = numAvatarsUpdated; _numAvatarsNotUpdated = numAVatarsNotUpdated; @@ -363,10 +394,14 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { - AvatarHashMap::handleRemovedAvatar(removedAvatar, removalReason); + auto avatar = std::static_pointer_cast(removedAvatar); + { + std::unique_lock lock(_spaceLock); + _spaceProxiesToDelete.push_back(avatar->getSpaceIndex()); + } + AvatarHashMap::handleRemovedAvatar(avatar, removalReason); // remove from physics - auto avatar = std::static_pointer_cast(removedAvatar); avatar->setPhysicsCallback(nullptr); AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get()); if (itr != _motionStates.end()) { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index ecf9a2d735..316e247aa0 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -23,6 +23,7 @@ #include #include #include +#include #include "AvatarMotionState.h" #include "MyAvatar.h" @@ -62,6 +63,7 @@ public: virtual ~AvatarManager(); void init(); + void setSpace(workload::SpacePointer& space ); std::shared_ptr getMyAvatar() { return _myAvatar; } glm::vec3 getMyAvatarPosition() const { return _myAvatar->getWorldPosition(); } @@ -183,6 +185,9 @@ public slots: */ void updateAvatarRenderStatus(bool shouldRenderAvatars); +protected: + AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; + private: explicit AvatarManager(QObject* parent = 0); explicit AvatarManager(const AvatarManager& other); @@ -212,6 +217,10 @@ private: float _avatarSimulationTime { 0.0f }; bool _shouldRender { true }; mutable int _identityRequestsSent { 0 }; + + mutable std::mutex _spaceLock; + workload::SpacePointer _space; + std::vector _spaceProxiesToDelete; }; #endif // hifi_AvatarManager_h diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 2061df6004..579301aab1 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -58,3 +58,16 @@ void OtherAvatar::createOrb() { _otherAvatarOrbMeshPlaceholder->setVisible(true); } } + +void OtherAvatar::setSpaceIndex(int32_t index) { + assert(_spaceIndex == -1); + _spaceIndex = index; +} + +void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { + if (_spaceIndex > -1) { + float approximateBoundingRadius = glm::length(getTargetScale()); + workload::Sphere sphere(getWorldPosition(), approximateBoundingRadius); + transaction.update(_spaceIndex, sphere); + } +} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index f33952b78b..8d32e49579 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -14,7 +14,10 @@ #include "ui/overlays/Sphere3DOverlay.h" #include "InterfaceLogging.h" +#include + class OtherAvatar : public Avatar { + Q_OBJECT public: explicit OtherAvatar(QThread* thread); virtual ~OtherAvatar(); @@ -24,9 +27,17 @@ public: void updateOrbPosition(); void removeOrb(); + void setSpaceIndex(int32_t index); + int32_t getSpaceIndex() const { return _spaceIndex; } + void updateSpaceProxy(workload::Transaction& transaction) const; + +signals: + void spaceUpdate(std::pair data); + protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; + int32_t _spaceIndex { -1 }; }; #endif // hifi_OtherAvatar_h From 21adbf49c06e2780a8b134a7626b5a3a6a7e1f2e Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 10 Aug 2018 17:03:04 -0700 Subject: [PATCH 148/207] remove cruft --- interface/src/avatar/OtherAvatar.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 8d32e49579..27a5b29353 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -17,7 +17,6 @@ #include class OtherAvatar : public Avatar { - Q_OBJECT public: explicit OtherAvatar(QThread* thread); virtual ~OtherAvatar(); @@ -31,9 +30,6 @@ public: int32_t getSpaceIndex() const { return _spaceIndex; } void updateSpaceProxy(workload::Transaction& transaction) const; -signals: - void spaceUpdate(std::pair data); - protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; From 55b1a6762cc3fa68017078a3c49bc20e50b02641 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 10:19:32 -0700 Subject: [PATCH 149/207] explicit delete default ctor, make _nestableType private --- libraries/shared/src/SpatiallyNestable.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.h b/libraries/shared/src/SpatiallyNestable.h index 361f0aaf17..a50e687a9b 100644 --- a/libraries/shared/src/SpatiallyNestable.h +++ b/libraries/shared/src/SpatiallyNestable.h @@ -212,7 +212,6 @@ public: virtual void parentDeleted() { } // called on children of a deleted parent protected: - const NestableType _nestableType; // EntityItem or an AvatarData QUuid _id; mutable SpatiallyNestableWeakPointer _parent; @@ -232,6 +231,8 @@ protected: quint64 _rotationChanged { 0 }; private: + SpatiallyNestable() = delete; + const NestableType _nestableType; // EntityItem or an AvatarData QUuid _parentID; // what is this thing's transform relative to? quint16 _parentJointIndex { INVALID_JOINT_INDEX }; // which joint of the parent is this relative to? From 3c01433e12ebfaed4df2dbf46b5297426fd46f2c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 10:20:58 -0700 Subject: [PATCH 150/207] stub for avatars changing workload regions --- interface/src/avatar/AvatarManager.cpp | 8 +++++-- interface/src/avatar/AvatarManager.h | 12 ++++++---- interface/src/avatar/OtherAvatar.h | 4 ++++ interface/src/workload/PhysicsBoundary.cpp | 24 ++++++++++++++++--- interface/src/workload/PhysicsBoundary.h | 3 --- .../src/EntityTreeRenderer.cpp | 3 ++- 6 files changed, 40 insertions(+), 14 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 551847324a..06c259f907 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -44,7 +44,6 @@ #include "InterfaceLogging.h" #include "Menu.h" #include "MyAvatar.h" -#include "OtherAvatar.h" #include "SceneScriptingInterface.h" // 50 times per second - target is 45hz, but this helps account for any small deviations @@ -91,7 +90,8 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe otherAvatar->setSpaceIndex(spaceIndex); workload::Sphere sphere(otherAvatar->getWorldPosition(), otherAvatar->getBoundingRadius()); workload::Transaction transaction; - transaction.reset(spaceIndex, sphere, workload::Owner(otherAvatar)); + SpatiallyNestablePointer nestable = std::static_pointer_cast(otherAvatar); + transaction.reset(spaceIndex, sphere, workload::Owner(nestable)); _space->enqueueTransaction(transaction); } return avatar; @@ -393,6 +393,10 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return AvatarSharedPointer(new OtherAvatar(qApp->thread()), [](OtherAvatar* ptr) { ptr->deleteLater(); }); } +void AvatarManager::handleSpaceChange(OtherAvatarPointer avatar) { + // WORKLOAD_AVATARS_BOOKMARK: implement this +} + void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { auto avatar = std::static_pointer_cast(removedAvatar); { diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 316e247aa0..bbf13d25c4 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -27,6 +27,7 @@ #include "AvatarMotionState.h" #include "MyAvatar.h" +#include "OtherAvatar.h" /**jsdoc * The AvatarManager API has properties and methods which manage Avatars within the same domain. @@ -94,6 +95,7 @@ public: void getObjectsToRemoveFromPhysics(VectorOfMotionStates& motionStates); void getObjectsToAddToPhysics(VectorOfMotionStates& motionStates); void getObjectsToChange(VectorOfMotionStates& motionStates); + void handleChangedMotionStates(const VectorOfMotionStates& motionStates); void handleCollisionEvents(const CollisionEvents& collisionEvents); @@ -104,23 +106,21 @@ public: * @returns {number} */ Q_INVOKABLE float getAvatarDataRate(const QUuid& sessionID, const QString& rateName = QString("")) const; - + /**jsdoc * @function AvatarManager.getAvatarUpdateRate * @param {Uuid} sessionID * @param {string} [rateName=""] * @returns {number} */ - Q_INVOKABLE float getAvatarUpdateRate(const QUuid& sessionID, const QString& rateName = QString("")) const; - + /**jsdoc * @function AvatarManager.getAvatarSimulationRate * @param {Uuid} sessionID * @param {string} [rateName=""] * @returns {number} */ - Q_INVOKABLE float getAvatarSimulationRate(const QUuid& sessionID, const QString& rateName = QString("")) const; /**jsdoc @@ -155,7 +155,7 @@ public: */ // TODO: remove this HACK once we settle on optimal default sort coefficients Q_INVOKABLE float getAvatarSortCoefficient(const QString& name); - + /**jsdoc * @function AvatarManager.setAvatarSortCoefficient * @param {string} name @@ -185,6 +185,8 @@ public slots: */ void updateAvatarRenderStatus(bool shouldRenderAvatars); + void handleSpaceChange(OtherAvatarPointer avatar); + protected: AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 27a5b29353..dcd54332c6 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -9,6 +9,8 @@ #ifndef hifi_OtherAvatar_h #define hifi_OtherAvatar_h +#include + #include #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" @@ -36,4 +38,6 @@ protected: int32_t _spaceIndex { -1 }; }; +using OtherAvatarPointer = std::shared_ptr; + #endif // hifi_OtherAvatar_h diff --git a/interface/src/workload/PhysicsBoundary.cpp b/interface/src/workload/PhysicsBoundary.cpp index 927121ac04..d1de9ad6b2 100644 --- a/interface/src/workload/PhysicsBoundary.cpp +++ b/interface/src/workload/PhysicsBoundary.cpp @@ -10,9 +10,13 @@ #include "PhysicsBoundary.h" +#include +#include #include #include +#include "avatar/AvatarManager.h" +#include "avatar/OtherAvatar.h" #include "workload/GameWorkload.h" void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const Inputs& inputs) { @@ -22,12 +26,26 @@ void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const } GameWorkloadContext* gameContext = static_cast(context.get()); PhysicalEntitySimulationPointer simulation = gameContext->_simulation; + auto avatarManager = DependencyManager::get(); const auto& regionChanges = inputs.get0(); for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) { const workload::Space::Change& change = regionChanges[i]; - auto entity = space->getOwner(change.proxyId).get(); - if (entity) { - simulation->changeEntity(entity); + auto nestable = space->getOwner(change.proxyId).get(); + if (nestable) { + switch (nestable->getNestableType()) { + case NestableType::Entity: { + EntityItemPointer entity = std::static_pointer_cast(nestable); + simulation->changeEntity(entity); + } + break; + case NestableType::Avatar: { + auto avatar = std::static_pointer_cast(nestable); + avatarManager->handleSpaceChange(avatar); + } + break; + default: + break; + } } } } diff --git a/interface/src/workload/PhysicsBoundary.h b/interface/src/workload/PhysicsBoundary.h index c316fa5686..e4d0411104 100644 --- a/interface/src/workload/PhysicsBoundary.h +++ b/interface/src/workload/PhysicsBoundary.h @@ -10,12 +10,9 @@ #ifndef hifi_PhysicsGatekeeper_h #define hifi_PhysicsGatekeeper_h -#include #include #include -#include "PhysicalEntitySimulation.h" - class PhysicsBoundary { public: using Config = workload::Job::Config; diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index 34d8dbbaef..c3c4095251 100644 --- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp +++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp @@ -295,7 +295,8 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r auto spaceIndex = _space->allocateID(); workload::Sphere sphere(entity->getWorldPosition(), entity->getBoundingRadius()); workload::Transaction transaction; - transaction.reset(spaceIndex, sphere, workload::Owner(entity)); + SpatiallyNestablePointer nestable = std::static_pointer_cast(entity); + transaction.reset(spaceIndex, sphere, workload::Owner(nestable)); _space->enqueueTransaction(transaction); entity->setSpaceIndex(spaceIndex); connect(entity.get(), &EntityItem::spaceUpdate, this, &EntityTreeRenderer::handleSpaceUpdate, Qt::QueuedConnection); From 598a227f1a866090600ae901e1f56ac215323697 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 12:48:53 -0700 Subject: [PATCH 151/207] use OtherAvatar instead of Avatar in AvatarMotionState --- interface/src/avatar/AvatarMotionState.cpp | 8 ++++---- interface/src/avatar/AvatarMotionState.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/interface/src/avatar/AvatarMotionState.cpp b/interface/src/avatar/AvatarMotionState.cpp index 07e6b3f6b0..ca67f634c8 100644 --- a/interface/src/avatar/AvatarMotionState.cpp +++ b/interface/src/avatar/AvatarMotionState.cpp @@ -16,7 +16,7 @@ #include -AvatarMotionState::AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { +AvatarMotionState::AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape) : ObjectMotionState(shape), _avatar(avatar) { assert(_avatar); _type = MOTIONSTATE_TYPE_AVATAR; cacheShapeDiameter(); @@ -57,7 +57,7 @@ PhysicsMotionType AvatarMotionState::computePhysicsMotionType() const { // virtual and protected const btCollisionShape* AvatarMotionState::computeNewShape() { ShapeInfo shapeInfo; - std::static_pointer_cast(_avatar)->computeShapeInfo(shapeInfo); + _avatar->computeShapeInfo(shapeInfo); return getShapeManager()->getShape(shapeInfo); } @@ -151,7 +151,7 @@ glm::vec3 AvatarMotionState::getObjectAngularVelocity() const { // virtual glm::vec3 AvatarMotionState::getObjectGravity() const { - return std::static_pointer_cast(_avatar)->getAcceleration(); + return _avatar->getAcceleration(); } // virtual @@ -176,7 +176,7 @@ void AvatarMotionState::computeCollisionGroupAndMask(int32_t& group, int32_t& ma // virtual float AvatarMotionState::getMass() const { - return std::static_pointer_cast(_avatar)->computeMass(); + return _avatar->computeMass(); } void AvatarMotionState::cacheShapeDiameter() { diff --git a/interface/src/avatar/AvatarMotionState.h b/interface/src/avatar/AvatarMotionState.h index a458704b1a..2533c11d56 100644 --- a/interface/src/avatar/AvatarMotionState.h +++ b/interface/src/avatar/AvatarMotionState.h @@ -14,14 +14,14 @@ #include -#include #include #include +#include "OtherAvatar.h" class AvatarMotionState : public ObjectMotionState { public: - AvatarMotionState(AvatarSharedPointer avatar, const btCollisionShape* shape); + AvatarMotionState(OtherAvatarPointer avatar, const btCollisionShape* shape); virtual void handleEasyChanges(uint32_t& flags) override; virtual bool handleHardAndEasyChanges(uint32_t& flags, PhysicsEngine* engine) override; @@ -85,7 +85,7 @@ protected: virtual bool isReadyToComputeShape() const override { return true; } virtual const btCollisionShape* computeNewShape() override; - AvatarSharedPointer _avatar; + OtherAvatarPointer _avatar; float _diameter { 0.0f }; uint32_t _dirtyFlags; From 615b8addd0bc5848301e51044e43b3e53c25b15a Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 13:23:20 -0700 Subject: [PATCH 152/207] fix order of includes --- interface/src/avatar/OtherAvatar.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index dcd54332c6..3ecf6bea38 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -12,11 +12,11 @@ #include #include +#include + +#include "InterfaceLogging.h" #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" -#include "InterfaceLogging.h" - -#include class OtherAvatar : public Avatar { public: From 1c87d7b109a48eaeccb5030e2a2b0f7dacfb2089 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 15:36:01 -0700 Subject: [PATCH 153/207] reorder initialization to remove compile warning --- libraries/shared/src/SpatiallyNestable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index ccb275ffc9..4b8768704a 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -22,8 +22,8 @@ const float defaultAACubeSize = 1.0f; const int MAX_PARENTING_CHAIN_SIZE = 30; SpatiallyNestable::SpatiallyNestable(NestableType nestableType, QUuid id) : - _nestableType(nestableType), _id(id), + _nestableType(nestableType), _transform() { // set flags in _transform _transform.setTranslation(glm::vec3(0.0f)); From 99aafb1f95f4b23012ca9c77ab2ce1400cfab1a7 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 15:07:15 -0700 Subject: [PATCH 154/207] move code from Avatar to OtherAvatar --- interface/src/avatar/OtherAvatar.cpp | 18 ++++++++++++++++++ interface/src/avatar/OtherAvatar.h | 13 +++++++++++-- .../src/avatars-renderer/Avatar.cpp | 17 ----------------- .../src/avatars-renderer/Avatar.h | 10 +--------- 4 files changed, 30 insertions(+), 28 deletions(-) diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 579301aab1..74ad9af915 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -71,3 +71,21 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { transaction.update(_spaceIndex, sphere); } } + +int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { + int32_t bytesRead = Avatar::parseDataFromBuffer(buffer); + if (_moving && _physicsCallback) { + _physicsCallback(Simulation::DIRTY_POSITION); + } + return bytesRead; +} + +void OtherAvatar::rebuildCollisionShape() { + if (_physicsCallback) { + _physicsCallback(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + } +} + +void OtherAvatar::setPhysicsCallback(AvatarPhysicsCallback cb) { + _physicsCallback = cb; +} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 3ecf6bea38..9f06b05ef0 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -1,6 +1,6 @@ // -// Created by Bradley Austin Davis on 2017/04/27 -// Copyright 2013-2017 High Fidelity, Inc. +// Created by amantly 2018.06.26 +// Copyright 2018 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 @@ -18,6 +18,8 @@ #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" +using AvatarPhysicsCallback = std::function; + class OtherAvatar : public Avatar { public: explicit OtherAvatar(QThread* thread); @@ -32,9 +34,16 @@ public: int32_t getSpaceIndex() const { return _spaceIndex; } void updateSpaceProxy(workload::Transaction& transaction) const; + int parseDataFromBuffer(const QByteArray& buffer) override; + + void setPhysicsCallback(AvatarPhysicsCallback cb); + bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } + void rebuildCollisionShape() override; + protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; + AvatarPhysicsCallback _physicsCallback { nullptr }; int32_t _spaceIndex { -1 }; }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 7dbee0e669..a9af3b7725 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1483,9 +1483,6 @@ int Avatar::parseDataFromBuffer(const QByteArray& buffer) { const float MOVE_DISTANCE_THRESHOLD = 0.001f; _moving = glm::distance(oldPosition, getWorldPosition()) > MOVE_DISTANCE_THRESHOLD; - if (_moving) { - addPhysicsFlags(Simulation::DIRTY_POSITION); - } if (_moving || _hasNewJointData) { locationChanged(); } @@ -1627,20 +1624,6 @@ float Avatar::computeMass() { return _density * TWO_PI * radius * radius * (glm::length(end - start) + 2.0f * radius / 3.0f); } -void Avatar::rebuildCollisionShape() { - addPhysicsFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); -} - -void Avatar::setPhysicsCallback(AvatarPhysicsCallback cb) { - _physicsCallback = cb; -} - -void Avatar::addPhysicsFlags(uint32_t flags) { - if (_physicsCallback) { - _physicsCallback(flags); - } -} - // thread-safe glm::vec3 Avatar::getLeftPalmPosition() const { return _leftPalmPositionCache.get(); diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6e8ac59f1..7d3ec39c4f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -50,8 +50,6 @@ enum ScreenTintLayer { class Texture; -using AvatarPhysicsCallback = std::function; - class Avatar : public AvatarData, public scriptable::ModelProvider { Q_OBJECT @@ -244,7 +242,7 @@ public: // (otherwise floating point error will cause problems at large positions). void applyPositionDelta(const glm::vec3& delta); - virtual void rebuildCollisionShape(); + virtual void rebuildCollisionShape() = 0; virtual void computeShapeInfo(ShapeInfo& shapeInfo); void getCapsule(glm::vec3& start, glm::vec3& end, float& radius); @@ -332,10 +330,6 @@ public: render::ItemID getRenderItemID() { return _renderItemID; } bool isMoving() const { return _moving; } - void setPhysicsCallback(AvatarPhysicsCallback cb); - void addPhysicsFlags(uint32_t flags); - bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } - void fadeIn(render::ScenePointer scene); void fadeOut(render::ScenePointer scene, KillAvatarReason reason); bool isFading() const { return _isFading; } @@ -530,8 +524,6 @@ protected: int _voiceSphereID; - AvatarPhysicsCallback _physicsCallback { nullptr }; - float _displayNameTargetAlpha { 1.0f }; float _displayNameAlpha { 1.0f }; From 3ecabb6583285fd424cae527dcb626d822f6b55c Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 15 Aug 2018 15:20:54 -0700 Subject: [PATCH 155/207] cleanup after removing spaghetti --- interface/src/avatar/AvatarManager.cpp | 4 ++-- interface/src/avatar/OtherAvatar.cpp | 14 ++++++++------ interface/src/avatar/OtherAvatar.h | 8 ++++---- 3 files changed, 14 insertions(+), 12 deletions(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 06c259f907..11257e0808 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -247,7 +247,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (shape) { AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); motionState->setMass(avatar->computeMass()); - avatar->setPhysicsCallback([=] (uint32_t flags) { motionState->addDirtyFlags(flags); }); + avatar->setMotionState(motionState); _motionStates.insert(avatar.get(), motionState); _motionStatesToAddToPhysics.insert(motionState); } @@ -406,7 +406,7 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar AvatarHashMap::handleRemovedAvatar(avatar, removalReason); // remove from physics - avatar->setPhysicsCallback(nullptr); + avatar->setMotionState(nullptr); AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get()); if (itr != _motionStates.end()) { AvatarMotionState* motionState = *itr; diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 74ad9af915..653b6c4533 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -9,6 +9,8 @@ #include "OtherAvatar.h" #include "Application.h" +#include "AvatarMotionState.h" + OtherAvatar::OtherAvatar(QThread* thread) : Avatar(thread) { // give the pointer to our head to inherited _headData variable from AvatarData _headData = new Head(this); @@ -74,18 +76,18 @@ void OtherAvatar::updateSpaceProxy(workload::Transaction& transaction) const { int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { int32_t bytesRead = Avatar::parseDataFromBuffer(buffer); - if (_moving && _physicsCallback) { - _physicsCallback(Simulation::DIRTY_POSITION); + if (_moving && _motionState) { + _motionState->addDirtyFlags(Simulation::DIRTY_POSITION); } return bytesRead; } void OtherAvatar::rebuildCollisionShape() { - if (_physicsCallback) { - _physicsCallback(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); + if (_motionState) { + _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); } } -void OtherAvatar::setPhysicsCallback(AvatarPhysicsCallback cb) { - _physicsCallback = cb; +void OtherAvatar::setMotionState(AvatarMotionState* motionState) { + _motionState = motionState; } diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index 9f06b05ef0..e34d4d767b 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -18,7 +18,7 @@ #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" -using AvatarPhysicsCallback = std::function; +class AvatarMotionState; class OtherAvatar : public Avatar { public: @@ -36,14 +36,14 @@ public: int parseDataFromBuffer(const QByteArray& buffer) override; - void setPhysicsCallback(AvatarPhysicsCallback cb); - bool isInPhysicsSimulation() const { return _physicsCallback != nullptr; } + void setMotionState(AvatarMotionState* motionState); + bool isInPhysicsSimulation() const { return _motionState != nullptr; } void rebuildCollisionShape() override; protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; - AvatarPhysicsCallback _physicsCallback { nullptr }; + AvatarMotionState* _motionState { nullptr }; int32_t _spaceIndex { -1 }; }; From 87223946ad2813c9e71c1738678e385cacf7b52f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Fri, 17 Aug 2018 17:43:17 -0700 Subject: [PATCH 156/207] add/remove avatars according to workload region --- interface/src/Application.cpp | 24 +++-- interface/src/avatar/AvatarManager.cpp | 111 ++++++++++----------- interface/src/avatar/AvatarManager.h | 17 ++-- interface/src/avatar/OtherAvatar.cpp | 12 ++- interface/src/avatar/OtherAvatar.h | 8 +- interface/src/workload/PhysicsBoundary.cpp | 10 +- libraries/physics/src/PhysicsEngine.cpp | 51 ++++++++++ libraries/physics/src/PhysicsEngine.h | 14 +++ 8 files changed, 157 insertions(+), 90 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c0e024ec1e..bfc781fa2a 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -2540,11 +2540,15 @@ void Application::cleanupBeforeQuit() { Application::~Application() { // remove avatars from physics engine - DependencyManager::get()->clearOtherAvatars(); - VectorOfMotionStates motionStates; - DependencyManager::get()->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - DependencyManager::get()->deleteAllAvatars(); + auto avatarManager = DependencyManager::get(); + avatarManager->clearOtherAvatars(); + + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); + + avatarManager->deleteAllAvatars(); _physicsEngine->setCharacterController(nullptr); @@ -5706,12 +5710,10 @@ void Application::update(float deltaTime) { t1 = std::chrono::high_resolution_clock::now(); - avatarManager->getObjectsToRemoveFromPhysics(motionStates); - _physicsEngine->removeObjects(motionStates); - avatarManager->getObjectsToAddToPhysics(motionStates); - _physicsEngine->addObjects(motionStates); - avatarManager->getObjectsToChange(motionStates); - _physicsEngine->changeObjects(motionStates); + PhysicsEngine::Transaction transaction; + avatarManager->buildPhysicsTransaction(transaction); + _physicsEngine->processTransaction(transaction); + avatarManager->handleProcessedPhysicsTransaction(transaction); myAvatar->prepareForPhysicsSimulation(); _physicsEngine->forEachDynamic([&](EntityDynamicPointer dynamic) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 11257e0808..62eadeddef 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -98,7 +98,7 @@ AvatarSharedPointer AvatarManager::addAvatar(const QUuid& sessionUUID, const QWe } AvatarManager::~AvatarManager() { - assert(_motionStates.empty()); + assert(_avatarsToChangeInPhysics.empty()); } void AvatarManager::init() { @@ -213,7 +213,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t updateExpiry = startTime + UPDATE_BUDGET; int numAvatarsUpdated = 0; int numAVatarsNotUpdated = 0; - bool physicsEnabled = qApp->isPhysicsEnabled(); render::Transaction renderTransaction; workload::Transaction workloadTransaction; @@ -240,18 +239,6 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { if (_shouldRender) { avatar->ensureInScene(avatar, qApp->getMain3DScene()); } - if (physicsEnabled && !avatar->isInPhysicsSimulation()) { - ShapeInfo shapeInfo; - avatar->computeShapeInfo(shapeInfo); - btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); - if (shape) { - AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); - motionState->setMass(avatar->computeMass()); - avatar->setMotionState(motionState); - _motionStates.insert(avatar.get(), motionState); - _motionStatesToAddToPhysics.insert(motionState); - } - } avatar->animateScaleChanges(deltaTime); const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; @@ -393,8 +380,52 @@ AvatarSharedPointer AvatarManager::newSharedAvatar() { return AvatarSharedPointer(new OtherAvatar(qApp->thread()), [](OtherAvatar* ptr) { ptr->deleteLater(); }); } -void AvatarManager::handleSpaceChange(OtherAvatarPointer avatar) { - // WORKLOAD_AVATARS_BOOKMARK: implement this +void AvatarManager::queuePhysicsChange(const OtherAvatarPointer& avatar) { + _avatarsToChangeInPhysics.insert(avatar); +} + +void AvatarManager::buildPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + SetOfOtherAvatars failedShapeBuilds; + for (auto avatar : _avatarsToChangeInPhysics) { + bool isInPhysics = avatar->isInPhysicsSimulation(); + if (isInPhysics != avatar->shouldBeInPhysicsSimulation()) { + if (isInPhysics) { + transaction.objectsToRemove.push_back(avatar->_motionState); + avatar->_motionState = nullptr; + } else { + ShapeInfo shapeInfo; + avatar->computeShapeInfo(shapeInfo); + btCollisionShape* shape = const_cast(ObjectMotionState::getShapeManager()->getShape(shapeInfo)); + if (shape) { + AvatarMotionState* motionState = new AvatarMotionState(avatar, shape); + motionState->setMass(avatar->computeMass()); + avatar->_motionState = motionState; + transaction.objectsToAdd.push_back(motionState); + } else { + failedShapeBuilds.insert(avatar); + } + } + } else if (isInPhysics) { + transaction.objectsToChange.push_back(avatar->_motionState); + } + } + _avatarsToChangeInPhysics.swap(failedShapeBuilds); +} + +void AvatarManager::handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction) { + // things on objectsToChange correspond to failed changes + // so we push them back onto _avatarsToChangeInPhysics + for (auto object : transaction.objectsToChange) { + AvatarMotionState* motionState = static_cast(object); + assert(motionState); + assert(motionState->_avatar); + _avatarsToChangeInPhysics.insert(motionState->_avatar); + } + // things on objectsToRemove are ready for delete + for (auto object : transaction.objectsToRemove) { + delete object; + } + transaction.clear(); } void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { @@ -405,15 +436,8 @@ void AvatarManager::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar } AvatarHashMap::handleRemovedAvatar(avatar, removalReason); - // remove from physics - avatar->setMotionState(nullptr); - AvatarMotionStateMap::iterator itr = _motionStates.find(avatar.get()); - if (itr != _motionStates.end()) { - AvatarMotionState* motionState = *itr; - _motionStatesToAddToPhysics.remove(motionState); - _motionStatesToRemoveFromPhysics.push_back(motionState); - _motionStates.erase(itr); - } + avatar->die(); + queuePhysicsChange(avatar); if (removalReason == KillAvatarReason::TheirAvatarEnteredYourBubble) { emit DependencyManager::get()->enteredIgnoreRadius(); @@ -449,8 +473,7 @@ void AvatarManager::clearOtherAvatars() { } void AvatarManager::deleteAllAvatars() { - assert(_motionStates.empty()); // should have called clearOtherAvatars() before getting here - deleteMotionStates(); + assert(_avatarsToChangeInPhysics.empty()); QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); @@ -458,39 +481,7 @@ void AvatarManager::deleteAllAvatars() { auto avatar = std::static_pointer_cast(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); avatar->die(); - } -} - -void AvatarManager::deleteMotionStates() { - // delete motionstates that were removed from physics last frame - for (auto state : _motionStatesToDelete) { - delete state; - } - _motionStatesToDelete.clear(); -} - -void AvatarManager::getObjectsToRemoveFromPhysics(VectorOfMotionStates& result) { - deleteMotionStates(); - result = _motionStatesToRemoveFromPhysics; - _motionStatesToDelete.swap(_motionStatesToRemoveFromPhysics); -} - -void AvatarManager::getObjectsToAddToPhysics(VectorOfMotionStates& result) { - result.clear(); - for (auto motionState : _motionStatesToAddToPhysics) { - result.push_back(motionState); - } - _motionStatesToAddToPhysics.clear(); -} - -void AvatarManager::getObjectsToChange(VectorOfMotionStates& result) { - result.clear(); - AvatarMotionStateMap::iterator motionStateItr = _motionStates.begin(); - while (motionStateItr != _motionStates.end()) { - if ((*motionStateItr)->getIncomingDirtyFlags() != 0) { - result.push_back(*motionStateItr); - } - ++motionStateItr; + assert(!avatar->_motionState); } } diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index bbf13d25c4..36df2f0aaf 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -12,6 +12,8 @@ #ifndef hifi_AvatarManager_h #define hifi_AvatarManager_h +#include + #include #include #include @@ -177,16 +179,17 @@ public: float getMyAvatarSendRate() const { return _myAvatarSendRate.rate(); } int getIdentityRequestsSent() const { return _identityRequestsSent; } -public slots: + void queuePhysicsChange(const OtherAvatarPointer& avatar); + void buildPhysicsTransaction(PhysicsEngine::Transaction& transaction); + void handleProcessedPhysicsTransaction(PhysicsEngine::Transaction& transaction); +public slots: /**jsdoc * @function AvatarManager.updateAvatarRenderStatus * @param {boolean} shouldRenderAvatars */ void updateAvatarRenderStatus(bool shouldRenderAvatars); - void handleSpaceChange(OtherAvatarPointer avatar); - protected: AvatarSharedPointer addAvatar(const QUuid& sessionUUID, const QWeakPointer& mixerWeakPointer) override; @@ -197,16 +200,12 @@ private: void simulateAvatarFades(float deltaTime); AvatarSharedPointer newSharedAvatar() override; - void deleteMotionStates(); void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason) override; QVector _avatarsToFade; - using AvatarMotionStateMap = QMap; - AvatarMotionStateMap _motionStates; - VectorOfMotionStates _motionStatesToRemoveFromPhysics; - VectorOfMotionStates _motionStatesToDelete; - SetOfMotionStates _motionStatesToAddToPhysics; + using SetOfOtherAvatars = std::set; + SetOfOtherAvatars _avatarsToChangeInPhysics; std::shared_ptr _myAvatar; quint64 _lastSendAvatarDataTime = 0; // Controls MyAvatar send data rate. diff --git a/interface/src/avatar/OtherAvatar.cpp b/interface/src/avatar/OtherAvatar.cpp index 653b6c4533..29ad5aed91 100644 --- a/interface/src/avatar/OtherAvatar.cpp +++ b/interface/src/avatar/OtherAvatar.cpp @@ -82,12 +82,16 @@ int OtherAvatar::parseDataFromBuffer(const QByteArray& buffer) { return bytesRead; } +void OtherAvatar::setWorkloadRegion(uint8_t region) { + _workloadRegion = region; +} + +bool OtherAvatar::shouldBeInPhysicsSimulation() const { + return (_workloadRegion < workload::Region::R3 && !isDead()); +} + void OtherAvatar::rebuildCollisionShape() { if (_motionState) { _motionState->addDirtyFlags(Simulation::DIRTY_SHAPE | Simulation::DIRTY_MASS); } } - -void OtherAvatar::setMotionState(AvatarMotionState* motionState) { - _motionState = motionState; -} diff --git a/interface/src/avatar/OtherAvatar.h b/interface/src/avatar/OtherAvatar.h index e34d4d767b..ccfe42dbed 100644 --- a/interface/src/avatar/OtherAvatar.h +++ b/interface/src/avatar/OtherAvatar.h @@ -18,6 +18,7 @@ #include "ui/overlays/Overlays.h" #include "ui/overlays/Sphere3DOverlay.h" +class AvatarManager; class AvatarMotionState; class OtherAvatar : public Avatar { @@ -36,15 +37,20 @@ public: int parseDataFromBuffer(const QByteArray& buffer) override; - void setMotionState(AvatarMotionState* motionState); bool isInPhysicsSimulation() const { return _motionState != nullptr; } void rebuildCollisionShape() override; + void setWorkloadRegion(uint8_t region); + bool shouldBeInPhysicsSimulation() const; + + friend AvatarManager; + protected: std::shared_ptr _otherAvatarOrbMeshPlaceholder { nullptr }; OverlayID _otherAvatarOrbMeshPlaceholderID { UNKNOWN_OVERLAY_ID }; AvatarMotionState* _motionState { nullptr }; int32_t _spaceIndex { -1 }; + uint8_t _workloadRegion { workload::Region::INVALID }; }; using OtherAvatarPointer = std::shared_ptr; diff --git a/interface/src/workload/PhysicsBoundary.cpp b/interface/src/workload/PhysicsBoundary.cpp index d1de9ad6b2..cc78789145 100644 --- a/interface/src/workload/PhysicsBoundary.cpp +++ b/interface/src/workload/PhysicsBoundary.cpp @@ -25,8 +25,6 @@ void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const return; } GameWorkloadContext* gameContext = static_cast(context.get()); - PhysicalEntitySimulationPointer simulation = gameContext->_simulation; - auto avatarManager = DependencyManager::get(); const auto& regionChanges = inputs.get0(); for (uint32_t i = 0; i < (uint32_t)regionChanges.size(); ++i) { const workload::Space::Change& change = regionChanges[i]; @@ -34,13 +32,15 @@ void PhysicsBoundary::run(const workload::WorkloadContextPointer& context, const if (nestable) { switch (nestable->getNestableType()) { case NestableType::Entity: { - EntityItemPointer entity = std::static_pointer_cast(nestable); - simulation->changeEntity(entity); + gameContext->_simulation->changeEntity(std::static_pointer_cast(nestable)); } break; case NestableType::Avatar: { auto avatar = std::static_pointer_cast(nestable); - avatarManager->handleSpaceChange(avatar); + avatar->setWorkloadRegion(change.region); + if (avatar->isInPhysicsSimulation() != avatar->shouldBeInPhysicsSimulation()) { + DependencyManager::get()->queuePhysicsChange(avatar); + } } break; default: diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 163005bc81..92ae3d546d 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -279,6 +279,57 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { } } +void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) { + // removes + for (auto object : transaction.objectsToRemove) { + btRigidBody* body = object->getRigidBody(); + if (body) { + removeDynamicsForBody(body); + _dynamicsWorld->removeRigidBody(body); + + // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. + object->setRigidBody(nullptr); + body->setMotionState(nullptr); + delete body; + } + object->clearIncomingDirtyFlags(); + } + + // adds + for (auto object : transaction.objectsToAdd) { + addObjectToDynamicsWorld(object); + } + + // changes + std::vector failedChanges; + for (auto object : transaction.objectsToChange) { + uint32_t flags = object->getIncomingDirtyFlags() & DIRTY_PHYSICS_FLAGS; + if (flags & HARD_DIRTY_PHYSICS_FLAGS) { + if (object->handleHardAndEasyChanges(flags, this)) { + object->clearIncomingDirtyFlags(); + } else { + failedChanges.push_back(object); + } + } else if (flags & EASY_DIRTY_PHYSICS_FLAGS) { + object->handleEasyChanges(flags); + object->clearIncomingDirtyFlags(); + } + if (object->getMotionType() == MOTION_TYPE_STATIC && object->isActive()) { + _activeStaticBodies.insert(object->getRigidBody()); + } + } + // activeStaticBodies have changed (in an Easy way) and need their Aabbs updated + // but we've configured Bullet to NOT update them automatically (for improved performance) + // so we must do it ourselves + std::set::const_iterator itr = _activeStaticBodies.begin(); + while (itr != _activeStaticBodies.end()) { + _dynamicsWorld->updateSingleAabb(*itr); + ++itr; + } + // we replace objectsToChange with any that failed + transaction.objectsToChange.swap(failedChanges); +} + void PhysicsEngine::removeContacts(ObjectMotionState* motionState) { // trigger events for new/existing/old contacts ContactMap::iterator contactItr = _contactMap.begin(); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index c6e165632b..0f40a06825 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -70,6 +70,18 @@ using CollisionEvents = std::vector; class PhysicsEngine { public: + class Transaction { + public: + void clear() { + objectsToRemove.clear(); + objectsToAdd.clear(); + objectsToChange.clear(); + } + std::vector objectsToRemove; + std::vector objectsToAdd; + std::vector objectsToChange; + }; + PhysicsEngine(const glm::vec3& offset); ~PhysicsEngine(); void init(); @@ -83,6 +95,8 @@ public: VectorOfMotionStates changeObjects(const VectorOfMotionStates& objects); void reinsertObject(ObjectMotionState* object); + void processTransaction(Transaction& transaction); + void stepSimulation(); void harvestPerformanceStats(); void printPerformanceStatsToFile(const QString& filename); From f5cae61b433293d812ae0a0dafb90d975fa751e4 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Aug 2018 14:11:22 -0700 Subject: [PATCH 157/207] fix typo causing compile failure --- interface/src/avatar/AvatarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 62eadeddef..86daf94bfa 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -283,7 +283,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } if (_shouldRender) { - qApp->getMain3DScene()->enqueueTransaction(transaction); + qApp->getMain3DScene()->enqueueTransaction(renderTransaction); } if (!_spaceProxiesToDelete.empty() && _space) { From 777b90bdc06a58e4d236290b50e52334d7d84292 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 20 Aug 2018 16:35:46 -0700 Subject: [PATCH 158/207] add 'physics body count' to debug stats --- interface/resources/qml/Stats.qml | 3 +++ interface/src/Application.cpp | 4 ++++ interface/src/Application.h | 1 + interface/src/ui/Stats.cpp | 1 + interface/src/ui/Stats.h | 9 +++++++++ libraries/physics/src/PhysicsEngine.cpp | 7 +++++++ libraries/physics/src/PhysicsEngine.h | 5 ++++- 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2e6e909312..d89e3333e6 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -60,6 +60,9 @@ Item { StatText { text: "Game Rate: " + root.gameLoopRate } + StatText { + text: "Physics Body Count: " + root.physicsBodyCount + } StatText { visible: root.expanded text: root.gameUpdateStats diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index bfc781fa2a..9057bdea28 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6184,6 +6184,10 @@ bool Application::isHMDMode() const { return getActiveDisplayPlugin()->isHmd(); } +float Application::getNumRigidBodies() const { + return _physicsEngine ? _physicsEngine->getNumRigidBodies() : 0; +} + float Application::getTargetRenderFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); } QRect Application::getDesirableApplicationGeometry() const { diff --git a/interface/src/Application.h b/interface/src/Application.h index 742cf075f6..e415b28343 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -207,6 +207,7 @@ public: size_t getRenderFrameCount() const { return _renderFrameCount; } float getRenderLoopRate() const { return _renderLoopCounter.rate(); } + float getNumRigidBodies() const; float getTargetRenderFrameRate() const; // frames/second float getFieldOfView() { return _fieldOfView.get(); } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ce1cd51de1..9c1e1802c7 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -121,6 +121,7 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); + STAT_UPDATE(physicsBodyCount, qApp->getNumRigidBodies()); STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated()); STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated()); STAT_UPDATE(serverCount, (int)nodeList->size()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index cf624b54c3..35eeb78caa 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -49,6 +49,7 @@ private: \ * @property {number} presentdroprate - Read-only. * @property {number} gameLoopRate - Read-only. * @property {number} avatarCount - Read-only. + * @property {number} physicsBodyCount - Read-only. * @property {number} updatedAvatarCount - Read-only. * @property {number} notUpdatedAvatarCount - Read-only. * @property {number} packetInCount - Read-only. @@ -195,6 +196,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, presentdroprate, 0) STATS_PROPERTY(int, gameLoopRate, 0) STATS_PROPERTY(int, avatarCount, 0) + STATS_PROPERTY(int, physicsBodyCount, 0) STATS_PROPERTY(int, updatedAvatarCount, 0) STATS_PROPERTY(int, notUpdatedAvatarCount, 0) STATS_PROPERTY(int, packetInCount, 0) @@ -406,6 +408,13 @@ signals: */ void gameLoopRateChanged(); + /**jsdoc + * Trigered when + * @function Stats.numPhysicsBodiesChanged + * @returns {Signal} + */ + void physicsBodyCountChanged(); + /**jsdoc * Triggered when the value of the avatarCount property changes. * @function Stats.avatarCountChanged diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 92ae3d546d..8c7b63e046 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -93,6 +93,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { btCollisionShape* shape = const_cast(motionState->getShape()); assert(shape); body = new btRigidBody(mass, motionState, shape, inertia); + ++_numRigidBodies; motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); @@ -116,6 +117,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { shape->calculateLocalInertia(mass, inertia); if (!body) { body = new btRigidBody(mass, motionState, shape, inertia); + ++_numRigidBodies; motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); @@ -139,6 +141,7 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { if (!body) { assert(motionState->getShape()); body = new btRigidBody(mass, motionState, const_cast(motionState->getShape()), inertia); + ++_numRigidBodies; motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); @@ -206,6 +209,7 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { if (body) { removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); + --_numRigidBodies; // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); @@ -223,6 +227,7 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) { if (body) { removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); + --_numRigidBodies; // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); @@ -274,6 +279,7 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { btRigidBody* body = object->getRigidBody(); if (body) { _dynamicsWorld->removeRigidBody(body); + --_numRigidBodies; // add it back addObjectToDynamicsWorld(object); } @@ -286,6 +292,7 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) if (body) { removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); + --_numRigidBodies; // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 0f40a06825..74e0c046a1 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -143,6 +143,8 @@ public: // See PhysicsCollisionGroups.h for mask flags. std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; + int32_t getNumRigidBodies() const { return _numRigidBodies; } + private: QList removeDynamicsForBody(btRigidBody* body); void addObjectToDynamicsWorld(ObjectMotionState* motionState); @@ -174,7 +176,8 @@ private: CharacterController* _myAvatarController; - uint32_t _numContactFrames = 0; + uint32_t _numContactFrames { 0 }; + int32_t _numRigidBodies { 0 }; bool _dumpNextStats { false }; bool _saveNextStats { false }; From 8f2141b59237b5bb66cbede01f49cd22a993612f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 21 Aug 2018 09:19:29 -0700 Subject: [PATCH 159/207] fix compile error --- interface/src/avatar/AvatarManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 86daf94bfa..3112b6e8bd 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -478,7 +478,7 @@ void AvatarManager::deleteAllAvatars() { QReadLocker locker(&_hashLock); AvatarHash::iterator avatarIterator = _avatarHash.begin(); while (avatarIterator != _avatarHash.end()) { - auto avatar = std::static_pointer_cast(avatarIterator.value()); + auto avatar = std::static_pointer_cast(avatarIterator.value()); avatarIterator = _avatarHash.erase(avatarIterator); avatar->die(); assert(!avatar->_motionState); From abf4a926d8688230871cc385c397b483c9dde93f Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 21 Aug 2018 15:16:54 -0700 Subject: [PATCH 160/207] cleanup --- interface/src/workload/PhysicsBoundary.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/interface/src/workload/PhysicsBoundary.h b/interface/src/workload/PhysicsBoundary.h index e4d0411104..bc1e851285 100644 --- a/interface/src/workload/PhysicsBoundary.h +++ b/interface/src/workload/PhysicsBoundary.h @@ -7,8 +7,8 @@ // 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_PhysicsGatekeeper_h -#define hifi_PhysicsGatekeeper_h +#ifndef hifi_PhysicsBoundary_h +#define hifi_PhysicsBoundary_h #include #include @@ -18,11 +18,11 @@ public: using Config = workload::Job::Config; using Inputs = workload::RegionTracker::Outputs; using Outputs = bool; - using JobModel = workload::Job::ModelI; // this doesn't work + using JobModel = workload::Job::ModelI; PhysicsBoundary() {} void configure(const Config& config) { } void run(const workload::WorkloadContextPointer& context, const Inputs& inputs); }; -#endif // hifi_PhysicsGatekeeper_h +#endif // hifi_PhysicsBoundary_h From e3d03cd321734d99060abf4c48b881a3ed7613ea Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 23 Aug 2018 13:16:02 -0700 Subject: [PATCH 161/207] Fix MS17738: Fix a case where the BLAST icon didn't show up in Snapshot Review --- scripts/system/snapshot.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/scripts/system/snapshot.js b/scripts/system/snapshot.js index 3e58bc61ad..37270f896e 100644 --- a/scripts/system/snapshot.js +++ b/scripts/system/snapshot.js @@ -352,7 +352,7 @@ function fillImageDataFromPrevious() { containsGif: previousAnimatedSnapPath !== "", processingGif: false, shouldUpload: false, - canBlast: location.domainID === Settings.getValue("previousSnapshotDomainID"), + canBlast: snapshotDomainID === Settings.getValue("previousSnapshotDomainID"), isLoggedIn: isLoggedIn }; imageData = []; @@ -427,7 +427,7 @@ function snapshotUploaded(isError, reply) { } isUploadingPrintableStill = false; } -var href, domainID; +var href, snapshotDomainID; function takeSnapshot() { tablet.emitScriptEvent(JSON.stringify({ type: "snapshot", @@ -452,8 +452,8 @@ function takeSnapshot() { // Even the domainID could change (e.g., if the user falls into a teleporter while recording). href = location.href; Settings.setValue("previousSnapshotHref", href); - domainID = location.domainID; - Settings.setValue("previousSnapshotDomainID", domainID); + snapshotDomainID = location.domainID; + Settings.setValue("previousSnapshotDomainID", snapshotDomainID); maybeDeleteSnapshotStories(); @@ -551,7 +551,7 @@ function stillSnapshotTaken(pathStillSnapshot, notify) { HMD.openTablet(); - isDomainOpen(domainID, function (canShare) { + isDomainOpen(snapshotDomainID, function (canShare) { snapshotOptions = { containsGif: false, processingGif: false, @@ -594,7 +594,7 @@ function processingGifStarted(pathStillSnapshot) { HMD.openTablet(); - isDomainOpen(domainID, function (canShare) { + isDomainOpen(snapshotDomainID, function (canShare) { snapshotOptions = { containsGif: true, processingGif: true, @@ -622,7 +622,7 @@ function processingGifCompleted(pathAnimatedSnapshot) { Settings.setValue("previousAnimatedSnapPath", pathAnimatedSnapshot); - isDomainOpen(domainID, function (canShare) { + isDomainOpen(snapshotDomainID, function (canShare) { snapshotOptions = { containsGif: true, processingGif: false, From 36784b0039058ce30e8d165e25379431ed37c766 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Aug 2018 10:21:59 -0700 Subject: [PATCH 162/207] reset sent trait data when re-send required --- assignment-client/src/avatars/AvatarMixer.cpp | 23 ++++++++++++------- .../src/avatars/AvatarMixerClientData.cpp | 8 +++++++ .../src/avatars/AvatarMixerClientData.h | 2 ++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 167c1cd29c..edbba20dc7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -541,7 +541,8 @@ void AvatarMixer::handleRequestsDomainListDataPacket(QSharedPointersetLastBroadcastTime(node->getUUID(), 0); + nodeData->setLastBroadcastTime(node->getUUID(), 0); + nodeData->resetSentTraitData(node->getLocalID()); } ); } @@ -588,10 +589,10 @@ void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointergetMessage()) ); if (!avatarID.isNull()) { auto nodeList = DependencyManager::get(); - auto node = nodeList->nodeWithUUID(avatarID); - if (node) { - QMutexLocker lock(&node->getMutex()); - AvatarMixerClientData* avatarClientData = dynamic_cast(node->getLinkedData()); + auto requestedNode = nodeList->nodeWithUUID(avatarID); + + if (requestedNode) { + AvatarMixerClientData* avatarClientData = static_cast(requestedNode->getLinkedData()); if (avatarClientData) { const AvatarData& avatarData = avatarClientData->getAvatar(); QByteArray serializedAvatar = avatarData.identityByteArray(); @@ -600,6 +601,11 @@ void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointersendPacketList(std::move(identityPackets), *senderNode); ++_sumIdentityPackets; } + + AvatarMixerClientData* senderData = static_cast(senderNode->getLinkedData()); + if (senderData) { + senderData->resetSentTraitData(requestedNode->getLocalID()); + } } } } @@ -625,23 +631,24 @@ void AvatarMixer::handleNodeIgnoreRequestPacket(QSharedPointer while (message->getBytesLeftToRead()) { // parse out the UUID being ignored from the packet QUuid ignoredUUID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - if (nodeList->nodeWithUUID(ignoredUUID)) { + auto ignoredNode = nodeList->nodeWithUUID(ignoredUUID); + if (ignoredNode) { if (nodeData) { // Reset the lastBroadcastTime for the ignored avatar to 0 // so the AvatarMixer knows it'll have to send identity data about the ignored avatar // to the ignorer if the ignorer unignores. nodeData->setLastBroadcastTime(ignoredUUID, 0); + nodeData->resetSentTraitData(ignoredNode->getLocalID()); } // Reset the lastBroadcastTime for the ignorer (FROM THE PERSPECTIVE OF THE IGNORED) to 0 // so the AvatarMixer knows it'll have to send identity data about the ignorer // to the ignored if the ignorer unignores. - auto ignoredNode = nodeList->nodeWithUUID(ignoredUUID); AvatarMixerClientData* ignoredNodeData = reinterpret_cast(ignoredNode->getLinkedData()); if (ignoredNodeData) { ignoredNodeData->setLastBroadcastTime(senderNode->getUUID(), 0); + ignoredNodeData->resetSentTraitData(senderNode->getLocalID()); } } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index b2490fc7b4..f524c071ec 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -228,6 +228,9 @@ void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointe killPacket->writePrimitive(KillAvatarReason::YourAvatarEnteredTheirBubble); } setLastBroadcastTime(other->getUUID(), 0); + + resetSentTraitData(other->getLocalID()); + DependencyManager::get()->sendPacket(std::move(killPacket), *self); } } @@ -238,6 +241,11 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, } } +void AvatarMixerClientData::resetSentTraitData(Node::LocalID nodeLocalID) { + _lastSentTraitsTimestamps[nodeLocalID] = TraitsCheckTimestamp(); + _sentTraitVersions[nodeLocalID].reset(); +} + void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { _currentViewFrustums.clear(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 1c2694af48..a892455fe3 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -135,6 +135,8 @@ public: AvatarTraits::TraitVersions& getLastSentTraitVersions(Node::LocalID otherAvatar) { return _sentTraitVersions[otherAvatar]; } + void resetSentTraitData(Node::LocalID nodeID); + private: struct PacketQueue : public std::queue> { QWeakPointer node; From 09065cf19aaa47eedbca871c3a5b7ae75b399e17 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Aug 2018 10:37:41 -0700 Subject: [PATCH 163/207] provide assignment dynamic factory to agent --- assignment-client/src/Agent.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 06a14927d3..2f03f15da7 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -53,6 +53,7 @@ #include // TODO: consider moving to scriptengine.h #include "entities/AssignmentParentFinder.h" +#include "AssignmentDynamicFactory.h" #include "RecordingScriptingInterface.h" #include "AbstractAudioInterface.h" #include "AgentScriptingInterface.h" @@ -67,6 +68,9 @@ Agent::Agent(ReceivedMessage& message) : { DependencyManager::set(); + DependencyManager::registerInheritance(); + DependencyManager::set(); + DependencyManager::set(); DependencyManager::set(); DependencyManager::set(false); @@ -860,6 +864,8 @@ void Agent::aboutToFinish() { DependencyManager::destroy(); DependencyManager::destroy(); + DependencyManager::destroy(); + DependencyManager::destroy(); QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); From e1879bf26ac5bd87996ed5d701a9ea69482aa3a8 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Aug 2018 13:55:17 -0700 Subject: [PATCH 164/207] add assignment dynamic factory to ESS --- assignment-client/src/scripts/EntityScriptServer.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 586931d403..272985093c 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -33,6 +33,7 @@ #include // for EntityScriptServerServices +#include "../AssignmentDynamicFactory.h" #include "EntityScriptServerLogging.h" #include "../entities/AssignmentParentFinder.h" @@ -56,6 +57,9 @@ int EntityScriptServer::_entitiesScriptEngineCount = 0; EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssignment(message) { qInstallMessageHandler(messageHandler); + DependencyManager::registerInheritance(); + DependencyManager::set(); + DependencyManager::set(false)->setPacketSender(&_entityEditSender); DependencyManager::set(); @@ -579,6 +583,7 @@ void EntityScriptServer::handleOctreePacket(QSharedPointer mess void EntityScriptServer::aboutToFinish() { shutdownScriptEngine(); + DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::get()->cleanup(); From a1d88a8588184de8983ee30fa347aad294f660eb Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 23 Aug 2018 14:45:16 -0700 Subject: [PATCH 165/207] Merge howard/wallet-locker2 into my branch --- .../qml/hifi/commerce/wallet/Help.qml | 51 +---- .../qml/hifi/commerce/wallet/Security.qml | 206 +----------------- .../qml/hifi/commerce/wallet/Wallet.qml | 15 ++ interface/src/commerce/Ledger.cpp | 36 ++- interface/src/commerce/Ledger.h | 3 +- interface/src/commerce/Wallet.cpp | 61 +++++- interface/src/commerce/Wallet.h | 4 + 7 files changed, 113 insertions(+), 263 deletions(-) diff --git a/interface/resources/qml/hifi/commerce/wallet/Help.qml b/interface/resources/qml/hifi/commerce/wallet/Help.qml index b453509712..6d8fc3c33f 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Help.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Help.qml @@ -63,47 +63,6 @@ Item { question: "How can I get HFC?"; answer: "High Fidelity commerce is in open beta right now. Want more HFC? \ Get it by going to

BankOfHighFidelity. and meeting with the banker!"; - } - ListElement { - isExpanded: false; - question: "What are private keys and where are they stored?"; - answer: - "A private key is a secret piece of text that is used to prove ownership, unlock confidential information, and sign transactions. \ -In High Fidelity, your private key is used to securely access the contents of your Wallet and Purchases. \ -After wallet setup, a hifikey file is stored on your computer in High Fidelity Interface's AppData directory. \ -Your hifikey file contains your private key and is protected by your wallet passphrase. \ -

It is very important to back up your hifikey file! \ -Tap here to open the folder where your HifiKeys are stored on your main display." - } - ListElement { - isExpanded: false; - question: "How do I back up my private keys?"; - answer: "You can back up your hifikey file (which contains your private key and is encrypted using your wallet passphrase) by copying it to a USB flash drive, or to a service like Dropbox or Google Drive. \ -Restore your hifikey file by replacing the file in Interface's AppData directory with your backup copy. \ -Others with access to your back up should not be able to spend your HFC without your passphrase. \ -Tap here to open the folder where your HifiKeys are stored on your main display."; - } - ListElement { - isExpanded: false; - question: "What happens if I lose my private keys?"; - answer: "We cannot stress enough that you should keep a backup! For security reasons, High Fidelity does not keep a copy, and cannot restore it for you. \ -If you lose your private key, you will no longer have access to the contents of your Wallet or My Purchases. \ -Here are some things to try:
    \ -
  • If you have backed up your hifikey file before, search your backup location
  • \ -
  • Search your AppData directory in the last machine you used to set up the Wallet
  • \ -
  • If you are a developer and have installed multiple builds of High Fidelity, your hifikey file might be in another folder
  • \ -


As a last resort, you can set up your Wallet again and generate a new hifikey file. \ -Unfortunately, this means you will start with 0 HFC and your purchased items will not be transferred over."; - } - ListElement { - isExpanded: false; - question: "What if I forget my wallet passphrase?"; - answer: "Your wallet passphrase is used to encrypt your private keys. Please write it down and store it securely! \ -

If you forget your passphrase, you will no longer be able to decrypt the hifikey file that the passphrase protects. \ -You will also no longer have access to the contents of your Wallet or My Purchases. \ -For security reasons, High Fidelity does not keep a copy of your passphrase, and can't restore it for you. \ -

If you still cannot remember your wallet passphrase, you can set up your Wallet again and generate a new hifikey file. \ -Unfortunately, this means you will start with 0 HFC and your purchased items will not be transferred over."; } ListElement { isExpanded: false; @@ -114,11 +73,9 @@ In your Wallet's Send Money tab, choose from your list of connections, or choose ListElement { isExpanded: false; question: "What is a Security Pic?" - answer: "Your Security Pic is an encrypted image that you select during Wallet Setup. \ -It acts as an extra layer of Wallet security. \ -When you see your Security Pic, you know that your actions and data are securely making use of your private keys.\ -

Don't enter your passphrase anywhere that doesn't display your Security Pic! \ -If you don't see your Security Pic on a page that requests your Wallet passphrase, someone untrustworthy may be trying to access your Wallet."; + answer: "Your Security Pic acts as an extra layer of Wallet security. \ +When you see your Security Pic, you know that your actions and data are securely making use of your account. \ +

Tap here to change your Security Pic."; } ListElement { isExpanded: false; @@ -260,6 +217,8 @@ At the moment, there is currently no way to convert HFC to other currencies. Sta } } else if (link === "#support") { Qt.openUrlExternally("mailto:support@highfidelity.com"); + } else if (link === "#securitypic") { + sendSignalToWallet({method: 'walletSecurity_changeSecurityImage'}); } } } diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index 216d621bf8..e021328ebe 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -88,76 +88,9 @@ Item { color: hifi.colors.faintGray; } - Item { - id: changePassphraseContainer; - anchors.top: securityTextSeparator.bottom; - anchors.topMargin: 8; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - height: 75; - - HiFiGlyphs { - id: changePassphraseImage; - text: hifi.glyphs.passphrase; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - RalewaySemiBold { - text: "Passphrase"; - // Anchors - anchors.top: parent.top; - anchors.bottom: parent.bottom; - anchors.left: changePassphraseImage.right; - anchors.leftMargin: 30; - width: 50; - // Text size - size: 18; - // Style - color: hifi.colors.white; - } - - // "Change Passphrase" button - HifiControlsUit.Button { - id: changePassphraseButton; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.right: parent.right; - anchors.verticalCenter: parent.verticalCenter; - width: 140; - height: 40; - text: "Change"; - onClicked: { - sendSignalToWallet({method: 'walletSecurity_changePassphrase'}); - } - } - } - - Rectangle { - id: changePassphraseSeparator; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.right: parent.right; - anchors.top: changePassphraseContainer.bottom; - anchors.topMargin: 8; - // Style - color: hifi.colors.faintGray; - } - Item { id: changeSecurityImageContainer; - anchors.top: changePassphraseSeparator.bottom; + anchors.top: securityTextSeparator.bottom; anchors.topMargin: 8; anchors.left: parent.left; anchors.leftMargin: 40; @@ -207,143 +140,6 @@ Item { } } } - - Rectangle { - id: privateKeysSeparator; - // Size - width: parent.width; - height: 1; - // Anchors - anchors.left: parent.left; - anchors.right: parent.right; - anchors.top: changeSecurityImageContainer.bottom; - anchors.topMargin: 8; - // Style - color: hifi.colors.faintGray; - } - - Item { - id: yourPrivateKeysContainer; - anchors.top: privateKeysSeparator.bottom; - anchors.left: parent.left; - anchors.leftMargin: 40; - anchors.right: parent.right; - anchors.rightMargin: 55; - anchors.bottom: parent.bottom; - - onVisibleChanged: { - if (visible) { - Commerce.getKeyFilePathIfExists(); - } - } - - HiFiGlyphs { - id: yourPrivateKeysImage; - text: hifi.glyphs.walletKey; - // Size - size: 80; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 20; - anchors.left: parent.left; - // Style - color: hifi.colors.white; - } - - RalewaySemiBold { - id: yourPrivateKeysText; - text: "Private Keys"; - size: 18; - // Anchors - anchors.top: parent.top; - anchors.topMargin: 32; - anchors.left: yourPrivateKeysImage.right; - anchors.leftMargin: 30; - anchors.right: parent.right; - height: 30; - // Style - color: hifi.colors.white; - } - - // Text below "private keys" - RalewayRegular { - id: explanitoryText; - text: "Your money and purchases are secured with private keys that only you have access to."; - // Text size - size: 18; - // Anchors - anchors.top: yourPrivateKeysText.bottom; - anchors.topMargin: 10; - anchors.left: yourPrivateKeysText.left; - anchors.right: yourPrivateKeysText.right; - height: paintedHeight; - // Style - color: hifi.colors.white; - wrapMode: Text.WordWrap; - // Alignment - horizontalAlignment: Text.AlignLeft; - verticalAlignment: Text.AlignVCenter; - } - - Rectangle { - id: removeHmdContainer; - z: 998; - visible: false; - - gradient: Gradient { - GradientStop { - position: 0.2; - color: hifi.colors.baseGrayHighlight; - } - GradientStop { - position: 1.0; - color: hifi.colors.baseGrayShadow; - } - } - anchors.fill: backupInstructionsButton; - radius: 5; - MouseArea { - anchors.fill: parent; - propagateComposedEvents: false; - hoverEnabled: true; - } - - RalewayBold { - anchors.fill: parent; - text: "INSTRUCTIONS OPEN ON DESKTOP"; - size: 15; - color: hifi.colors.white; - verticalAlignment: Text.AlignVCenter; - horizontalAlignment: Text.AlignHCenter; - } - - Timer { - id: removeHmdContainerTimer; - interval: 5000; - onTriggered: removeHmdContainer.visible = false - } - } - - HifiControlsUit.Button { - id: backupInstructionsButton; - text: "View Backup Instructions"; - color: hifi.buttons.blue; - colorScheme: hifi.colorSchemes.dark; - anchors.left: explanitoryText.left; - anchors.right: explanitoryText.right; - anchors.top: explanitoryText.bottom; - anchors.topMargin: 16; - height: 40; - - onClicked: { - var keyPath = "file:///" + root.keyFilePath.substring(0, root.keyFilePath.lastIndexOf('/')); - Qt.openUrlExternally(keyPath + "/backup_instructions.html"); - Qt.openUrlExternally(keyPath); - removeHmdContainer.visible = true; - removeHmdContainerTimer.start(); - } - } - } } // diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index 603d7fb676..ffd06cb4a8 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -399,6 +399,9 @@ Rectangle { onSendSignalToWallet: { if (msg.method === 'walletReset' || msg.method === 'passphraseReset') { sendToScript(msg); + } else if (msg.method === 'walletSecurity_changeSecurityImage') { + securityImageChange.initModel(); + root.activeView = "securityImageChange"; } } } @@ -803,12 +806,24 @@ Rectangle { } function walletResetSetup() { + /* Bypass all this and do it automatically root.activeView = "walletSetup"; var timestamp = new Date(); walletSetup.startingTimestamp = timestamp; walletSetup.setupAttemptID = generateUUID(); UserActivityLogger.commerceWalletSetupStarted(timestamp, walletSetup.setupAttemptID, walletSetup.setupFlowVersion, walletSetup.referrer ? walletSetup.referrer : "wallet app", (AddressManager.placename || AddressManager.hostname || '') + (AddressManager.pathname ? AddressManager.pathname.match(/\/[^\/]+/)[0] : '')); + */ + + var randomNumber = Math.floor(Math.random() * 34) + 1; + var securityImagePath = "images/" + addLeadingZero(randomNumber) + ".jpg"; + Commerce.getWalletAuthenticatedStatus(); // before writing security image, ensures that salt/account password is set. + Commerce.chooseSecurityImage(securityImagePath); + Commerce.generateKeyPair(); + } + + function addLeadingZero(n) { + return n < 10 ? '0' + n : '' + n; } function followReferrer(msg) { diff --git a/interface/src/commerce/Ledger.cpp b/interface/src/commerce/Ledger.cpp index 702251f867..67303f2a9b 100644 --- a/interface/src/commerce/Ledger.cpp +++ b/interface/src/commerce/Ledger.cpp @@ -31,7 +31,9 @@ QJsonObject Ledger::apiResponse(const QString& label, QNetworkReply* reply) { QByteArray response = reply->readAll(); QJsonObject data = QJsonDocument::fromJson(response).object(); +#if defined(DEV_BUILD) // Don't expose user's personal data in the wild. But during development this can be handy. qInfo(commerce) << label << "response" << QJsonDocument(data).toJson(QJsonDocument::Compact); +#endif return data; } // Non-200 responses are not json: @@ -69,7 +71,9 @@ void Ledger::send(const QString& endpoint, const QString& success, const QString auto accountManager = DependencyManager::get(); const QString URL = "/api/v1/commerce/"; JSONCallbackParameters callbackParams(this, success, fail); +#if defined(DEV_BUILD) // Don't expose user's personal data in the wild. But during development this can be handy. qCInfo(commerce) << "Sending" << endpoint << QJsonDocument(request).toJson(QJsonDocument::Compact); +#endif accountManager->sendRequest(URL + endpoint, authType, method, @@ -117,7 +121,7 @@ void Ledger::buy(const QString& hfc_key, int cost, const QString& asset_id, cons signedSend("transaction", transactionString, hfc_key, "buy", "buySuccess", "buyFailure", controlled_failure); } -bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { +bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key, const QByteArray& locker) { auto accountManager = DependencyManager::get(); if (!accountManager->isLoggedIn()) { qCWarning(commerce) << "Cannot set receiveAt when not logged in."; @@ -125,11 +129,25 @@ bool Ledger::receiveAt(const QString& hfc_key, const QString& signing_key) { emit receiveAtResult(result); return false; // We know right away that we will fail, so tell the caller. } - - signedSend("public_key", hfc_key.toUtf8(), signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); + QJsonObject transaction; + transaction["public_key"] = hfc_key; + transaction["locker"] = QString::fromUtf8(locker); + QJsonDocument transactionDoc{ transaction }; + auto transactionString = transactionDoc.toJson(QJsonDocument::Compact); + signedSend("text", transactionString, signing_key, "receive_at", "receiveAtSuccess", "receiveAtFailure"); return true; // Note that there may still be an asynchronous signal of failure that callers might be interested in. } +bool Ledger::receiveAt() { + auto wallet = DependencyManager::get(); + auto keys = wallet->listPublicKeys(); + if (keys.isEmpty()) { + return false; + } + auto key = keys.first(); + return receiveAt(key, key, wallet->getWallet()); +} + void Ledger::balance(const QStringList& keys) { keysQuery("balance", "balanceSuccess", "balanceFailure"); } @@ -283,24 +301,30 @@ void Ledger::accountSuccess(QNetworkReply* reply) { auto iv = QByteArray::fromBase64(data["iv"].toString().toUtf8()); auto ckey = QByteArray::fromBase64(data["ckey"].toString().toUtf8()); QString remotePublicKey = data["public_key"].toString(); + const QByteArray locker = data["locker"].toString().toUtf8(); bool isOverride = wallet->wasSoftReset(); wallet->setSalt(salt); wallet->setIv(iv); wallet->setCKey(ckey); + if (!locker.isEmpty()) { + wallet->setWallet(locker); + wallet->setPassphrase("ACCOUNT"); // We only locker wallets that have been converted to account-based auth. + } QString keyStatus = "ok"; QStringList localPublicKeys = wallet->listPublicKeys(); if (remotePublicKey.isEmpty() || isOverride) { - if (!localPublicKeys.isEmpty()) { - QString key = localPublicKeys.first(); - receiveAt(key, key); + if (!localPublicKeys.isEmpty()) { // Let the metaverse know about a local wallet. + receiveAt(); } } else { if (localPublicKeys.isEmpty()) { keyStatus = "preexisting"; } else if (localPublicKeys.first() != remotePublicKey) { keyStatus = "conflicting"; + } else if (locker.isEmpty()) { // Matches metaverse data, but we haven't lockered it yet. + receiveAt(); } } diff --git a/interface/src/commerce/Ledger.h b/interface/src/commerce/Ledger.h index ba2f167f4b..427395ee11 100644 --- a/interface/src/commerce/Ledger.h +++ b/interface/src/commerce/Ledger.h @@ -26,7 +26,8 @@ class Ledger : public QObject, public Dependency { public: void buy(const QString& hfc_key, int cost, const QString& asset_id, const QString& inventory_key, const bool controlled_failure = false); - bool receiveAt(const QString& hfc_key, const QString& signing_key); + bool receiveAt(const QString& hfc_key, const QString& signing_key, const QByteArray& locker); + bool receiveAt(); void balance(const QStringList& keys); void inventory(const QString& editionFilter, const QString& typeFilter, const QString& titleFilter, const int& page, const int& perPage); void history(const QStringList& keys, const int& pageNumber, const int& itemsPerPage); diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp index ef6b4654f5..5b8417be7c 100644 --- a/interface/src/commerce/Wallet.cpp +++ b/interface/src/commerce/Wallet.cpp @@ -131,7 +131,7 @@ bool Wallet::writeBackupInstructions() { QFile outputFile(outputFilename); bool retval = false; - if (getKeyFilePath() == "") + if (getKeyFilePath().isEmpty()) { return false; } @@ -190,6 +190,30 @@ bool writeKeys(const char* filename, EC_KEY* keys) { return retval; } +bool Wallet::setWallet(const QByteArray& wallet) { + QFile file(keyFilePath()); + if (!file.open(QIODevice::WriteOnly)) { + qCCritical(commerce) << "Unable to open wallet for write in" << keyFilePath(); + return false; + } + if (file.write(wallet) != wallet.count()) { + qCCritical(commerce) << "Unable to write wallet in" << keyFilePath(); + return false; + } + file.close(); + return true; +} +QByteArray Wallet::getWallet() { + QFile file(keyFilePath()); + if (!file.open(QIODevice::ReadOnly)) { + qCInfo(commerce) << "No existing wallet in" << keyFilePath(); + return QByteArray(); + } + QByteArray wallet = file.readAll(); + file.close(); + return wallet; +} + QPair generateECKeypair() { EC_KEY* keyPair = EC_KEY_new_by_curve_name(NID_secp256k1); @@ -334,7 +358,7 @@ Wallet::Wallet() { uint status; QString keyStatus = result.contains("data") ? result["data"].toObject()["keyStatus"].toString() : ""; - if (wallet->getKeyFilePath() == "" || !wallet->getSecurityImage()) { + if (wallet->getKeyFilePath().isEmpty() || !wallet->getSecurityImage()) { if (keyStatus == "preexisting") { status = (uint) WalletStatus::WALLET_STATUS_PREEXISTING; } else{ @@ -524,15 +548,23 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { // FIXME: initialize OpenSSL elsewhere soon initialize(); + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: checking" << (!_passphrase || !_passphrase->isEmpty()); // this should always be false if we don't have a passphrase // cached yet if (!_passphrase || _passphrase->isEmpty()) { - return false; + if (!getKeyFilePath().isEmpty()) { // If file exists, then it is an old school file that has not been lockered. Must get user's passphrase. + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: No passphrase, but there is an existing wallet."; + return false; + } else { + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: New setup."; + setPassphrase("ACCOUNT"); // Going forward, consider this an account-based client. + } } if (_publicKeys.count() > 0) { // we _must_ be authenticated if the publicKeys are there DependencyManager::get()->setWalletStatus((uint)WalletStatus::WALLET_STATUS_READY); + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: wallet was ready"; return true; } @@ -545,10 +577,15 @@ bool Wallet::walletIsAuthenticatedWithPassphrase() { // be sure to add the public key so we don't do this over and over _publicKeys.push_back(publicKey.toBase64()); + + if (*_passphrase != "ACCOUNT") { + changePassphrase("ACCOUNT"); // Rewrites with salt and constant, and will be lockered that way. + } + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: wallet now ready"; return true; } } - + qCDebug(commerce) << "walletIsAuthenticatedWithPassphrase: wallet not ready"; return false; } @@ -559,6 +596,7 @@ bool Wallet::generateKeyPair() { qCInfo(commerce) << "Generating keypair."; auto keyPair = generateECKeypair(); if (!keyPair.first) { + qCWarning(commerce) << "Empty keypair"; return false; } @@ -576,7 +614,7 @@ bool Wallet::generateKeyPair() { // 2. It is maximally private, and we can step back from that later if desired. // 3. It maximally exercises all the machinery, so we are most likely to surface issues now. auto ledger = DependencyManager::get(); - return ledger->receiveAt(key, key); + return ledger->receiveAt(key, key, getWallet()); } QStringList Wallet::listPublicKeys() { @@ -666,11 +704,13 @@ void Wallet::chooseSecurityImage(const QString& filename) { // there _is_ a keyfile, we need to update it (similar to changing the // passphrase, we need to do so into a temp file and move it). if (!QFile(keyFilePath()).exists()) { + qCDebug(commerce) << "initial security pic set for empty wallet"; emit securityImageResult(true); return; } bool success = writeWallet(); + qCDebug(commerce) << "updated security pic" << success; emit securityImageResult(success); } @@ -715,6 +755,11 @@ QString Wallet::getKeyFilePath() { bool Wallet::writeWallet(const QString& newPassphrase) { EC_KEY* keys = readKeys(keyFilePath().toStdString().c_str()); + auto ledger = DependencyManager::get(); + // Remove any existing locker, because it will be out of date. + if (!_publicKeys.isEmpty() && !ledger->receiveAt(_publicKeys.first(), _publicKeys.first(), QByteArray())) { + return false; // FIXME: receiveAt could fail asynchronously. + } if (keys) { // we read successfully, so now write to a new temp file QString tempFileName = QString("%1.%2").arg(keyFilePath(), QString("temp")); @@ -722,6 +767,7 @@ bool Wallet::writeWallet(const QString& newPassphrase) { if (!newPassphrase.isEmpty()) { setPassphrase(newPassphrase); } + if (writeKeys(tempFileName.toStdString().c_str(), keys)) { if (writeSecurityImage(_securityImage, tempFileName)) { // ok, now move the temp file to the correct spot @@ -729,6 +775,11 @@ bool Wallet::writeWallet(const QString& newPassphrase) { QFile(tempFileName).rename(QString(keyFilePath())); qCDebug(commerce) << "wallet written successfully"; emit keyFilePathIfExistsResult(getKeyFilePath()); + if (!walletIsAuthenticatedWithPassphrase() || !ledger->receiveAt()) { + // FIXME: Should we fail the whole operation? + // Tricky, because we'll need the the key and file from the TEMP location... + qCWarning(commerce) << "Failed to update locker"; + } return true; } else { qCDebug(commerce) << "couldn't write security image to temp wallet"; diff --git a/interface/src/commerce/Wallet.h b/interface/src/commerce/Wallet.h index 665afd9a23..c096713058 100644 --- a/interface/src/commerce/Wallet.h +++ b/interface/src/commerce/Wallet.h @@ -73,6 +73,7 @@ private slots: void handleChallengeOwnershipPacket(QSharedPointer packet, SharedNodePointer sendingNode); private: + friend class Ledger; QStringList _publicKeys{}; QPixmap* _securityImage { nullptr }; QByteArray _salt; @@ -87,6 +88,9 @@ private: bool readSecurityImage(const QString& inputFilePath, unsigned char** outputBufferPtr, int* outputBufferLen); bool writeBackupInstructions(); + bool setWallet(const QByteArray& wallet); + QByteArray getWallet(); + void account(); }; From dddb2141f041422c0b9365be84480003f28037c1 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Thu, 23 Aug 2018 15:45:29 -0700 Subject: [PATCH 166/207] Implement Wallet Security feature - Auto Logout checkbox --- .../qml/LoginDialog/LinkAccountBody.qml | 1 + .../qml/hifi/commerce/wallet/Security.qml | 75 +++++++++++++++++++ .../qml/hifi/commerce/wallet/Wallet.qml | 11 +++ interface/src/Application.cpp | 6 ++ scripts/system/commerce/wallet.js | 7 ++ 5 files changed, 100 insertions(+) diff --git a/interface/resources/qml/LoginDialog/LinkAccountBody.qml b/interface/resources/qml/LoginDialog/LinkAccountBody.qml index 814778a4b1..4c6e5f6fce 100644 --- a/interface/resources/qml/LoginDialog/LinkAccountBody.qml +++ b/interface/resources/qml/LoginDialog/LinkAccountBody.qml @@ -120,6 +120,7 @@ Item { TextField { id: usernameField + text: Settings.getValue("wallet/savedUsername", ""); width: parent.width focus: true label: "Username or Email" diff --git a/interface/resources/qml/hifi/commerce/wallet/Security.qml b/interface/resources/qml/hifi/commerce/wallet/Security.qml index e021328ebe..14ac696ef7 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Security.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Security.qml @@ -140,6 +140,81 @@ Item { } } } + + Item { + id: autoLogoutContainer; + anchors.top: changeSecurityImageContainer.bottom; + anchors.topMargin: 8; + anchors.left: parent.left; + anchors.leftMargin: 40; + anchors.right: parent.right; + anchors.rightMargin: 55; + height: 75; + + HiFiGlyphs { + id: autoLogoutImage; + text: hifi.glyphs.walletKey; + // Size + size: 80; + // Anchors + anchors.top: parent.top; + anchors.topMargin: 20; + anchors.left: parent.left; + // Style + color: hifi.colors.white; + } + + HifiControlsUit.CheckBox { + id: autoLogoutCheckbox; + checked: Settings.getValue("wallet/autoLogout", false); + text: "Automatically Log Out when Exiting Interface" + // Anchors + anchors.verticalCenter: autoLogoutImage.verticalCenter; + anchors.left: autoLogoutImage.right; + anchors.leftMargin: 20; + anchors.right: autoLogoutHelp.left; + anchors.rightMargin: 12; + boxSize: 28; + labelFontSize: 18; + color: hifi.colors.white; + onCheckedChanged: { + Settings.setValue("wallet/autoLogout", checked); + if (checked) { + Settings.setValue("wallet/savedUsername", Account.username); + } else { + Settings.setValue("wallet/savedUsername", ""); + } + } + } + + RalewaySemiBold { + id: autoLogoutHelp; + text: '[?]'; + // Anchors + anchors.verticalCenter: autoLogoutImage.verticalCenter; + anchors.right: parent.right; + width: 30; + height: 30; + // Text size + size: 18; + // Style + color: hifi.colors.blueHighlight; + + MouseArea { + anchors.fill: parent; + hoverEnabled: true; + onEntered: { + parent.color = hifi.colors.blueAccent; + } + onExited: { + parent.color = hifi.colors.blueHighlight; + } + onClicked: { + sendSignalToWallet({method: 'walletSecurity_autoLogoutHelp'}); + } + } + } + } } // diff --git a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml index ffd06cb4a8..65d98af234 100644 --- a/interface/resources/qml/hifi/commerce/wallet/Wallet.qml +++ b/interface/resources/qml/hifi/commerce/wallet/Wallet.qml @@ -382,6 +382,17 @@ Rectangle { } else if (msg.method === 'walletSecurity_changeSecurityImage') { securityImageChange.initModel(); root.activeView = "securityImageChange"; + } else if (msg.method === 'walletSecurity_autoLogoutHelp') { + lightboxPopup.titleText = "Automatically Log Out"; + lightboxPopup.bodyText = "By default, after you log in to High Fidelity, you will stay logged in to your High Fidelity " + + "account even after you close and re-open Interface. This means anyone who opens Interface on your computer " + + "could make purchases with your Wallet.\n\n" + + "If you do not want to stay logged in across Interface sessions, check this box."; + lightboxPopup.button1text = "CLOSE"; + lightboxPopup.button1method = function() { + lightboxPopup.visible = false; + } + lightboxPopup.visible = true; } } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 1d515392b0..60d23c94dd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -375,6 +375,7 @@ static const int INTERVAL_TO_CHECK_HMD_WORN_STATUS = 500; // milliseconds static const QString DESKTOP_DISPLAY_PLUGIN_NAME = "Desktop"; static const QString ACTIVE_DISPLAY_PLUGIN_SETTING_NAME = "activeDisplayPlugin"; static const QString SYSTEM_TABLET = "com.highfidelity.interface.tablet.system"; +static const QString AUTO_LOGOUT_SETTING_NAME = "wallet/autoLogout"; const std::vector> Application::_acceptedExtensions { { SVO_EXTENSION, &Application::importSVOFromURL }, @@ -1730,6 +1731,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QTimer* settingsTimer = new QTimer(); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{ connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{ + bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); + if (autoLogout) { + auto accountManager = DependencyManager::get(); + accountManager->logout(); + } // Disconnect the signal from the save settings QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings); // Stop the settings timer diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index ac25269e41..8730273e7c 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -406,6 +406,11 @@ sendMoneyRecipient = null; } + function onUsernameChanged() { + Settings.setValue("wallet/autoLogout", false); + Settings.setValue("wallet/savedUsername", ""); + } + // Function Name: fromQml() // // Description: @@ -581,6 +586,7 @@ var tablet = null; var walletEnabled = Settings.getValue("commerce", true); function startup() { + GlobalServices.myUsernameChanged.connect(onUsernameChanged); if (walletEnabled) { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ @@ -612,6 +618,7 @@ removeOverlays(); } function shutdown() { + GlobalServices.myUsernameChanged.disconnect(onUsernameChanged); button.clicked.disconnect(onButtonClicked); tablet.removeButton(button); deleteSendMoneyParticleEffect(); From 3d959ca7ee6c59496f30dc15f6da02f666376166 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 15:52:42 -0700 Subject: [PATCH 167/207] Refactoring and cleaning --- .../src/avatars-renderer/Avatar.h | 1 - libraries/avatars/src/AvatarData.cpp | 21 +- libraries/avatars/src/AvatarData.h | 8 +- libraries/avatars/src/AvatarHashMap.cpp | 223 ++++++++++++++---- libraries/avatars/src/AvatarHashMap.h | 23 +- 5 files changed, 223 insertions(+), 53 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h index c6e8ac59f1..63aa6d9b6f 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.h +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.h @@ -464,7 +464,6 @@ protected: glm::vec3 _lastAngularVelocity; glm::vec3 _angularAcceleration; glm::quat _lastOrientation; - glm::vec3 _worldUpDirection { Vectors::UP }; bool _moving { false }; ///< set when position is changing diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 63a905aa6e..ddb53f8acb 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -918,7 +918,26 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + glm::vec3(0, 2 * _surrogateIndex, 0); + + const float SPACE_BETWEEN_AVATARS = 2.0f; + const int RANDOM_RADIUS = 100; + const int AVATARS_PER_ROW = 3; + + glm::vec3 offset; + + if (false) { + qsrand(static_cast(getID().toByteArray().toInt())); + float xrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); + float yrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); + offset = glm::vec3(xrand, 0.0f, yrand); + } + else { + int row = _replicaIndex % AVATARS_PER_ROW; + int col = floor(_replicaIndex / AVATARS_PER_ROW); + offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); + } + + auto newValue = glm::vec3(data->globalPosition[0], data->globalPosition[1], data->globalPosition[2]) + offset; if (_globalPosition != newValue) { _globalPosition = newValue; _globalPositionChanged = usecTimestampNow(); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index b3443dbd8a..36cc0f936d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -337,6 +337,7 @@ enum KillAvatarReason : uint8_t { TheirAvatarEnteredYourBubble, YourAvatarEnteredTheirBubble }; + Q_DECLARE_METATYPE(KillAvatarReason); class QDataStream; @@ -1185,9 +1186,8 @@ public: virtual void addMaterial(graphics::MaterialLayer material, const std::string& parentMaterialName) {} virtual void removeMaterial(graphics::MaterialPointer material, const std::string& parentMaterialName) {} - - void setSurrogateIndex(int surrogateIndex) { _surrogateIndex = surrogateIndex; } - int getSurrogateIndex() { return _surrogateIndex; } + void setReplicaIndex(int replicaIndex) { _replicaIndex = replicaIndex; } + int getReplicaIndex() { return _replicaIndex; } signals: @@ -1446,7 +1446,7 @@ protected: udt::SequenceNumber _identitySequenceNumber { 0 }; bool _hasProcessedFirstIdentity { false }; float _density; - int _surrogateIndex{ 0 }; + int _replicaIndex { 0 }; // null unless MyAvatar or ScriptableAvatar sending traits data to mixer std::unique_ptr _clientTraitsHandler; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index a84d83e663..800825f574 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -21,7 +21,80 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" -const int SURROGATE_COUNT = 2; + +void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { + if (_replicasMap.find(parentID) == _replicasMap.end()) { + std::vector emptyReplicas = std::vector(); + _replicasMap.insert(std::pair>(parentID, emptyReplicas)); + } + auto &replicas = _replicasMap[parentID]; + replica->setReplicaIndex((int)replicas.size() + 1); + replicas.push_back(replica); +} + +std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID, int count) { + std::vector ids; + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (int i = 0; i < replicas.size(); i++) { + ids.push_back(replicas[i]->getID()); + } + } else if (count > 0) { + for (int i = 0; i < count; i++) { + ids.push_back(QUuid::createUuid()); + } + } + return ids; +} + +void AvatarReplicas::parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->parseDataFromBuffer(buffer); + } + } +} + +void AvatarReplicas::removeReplicas(const QUuid& parentID) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + _replicasMap.erase(parentID); + } +} + +void AvatarReplicas::processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processAvatarIdentity(identityData, identityChanged, displayNameChanged); + } + } +} +void AvatarReplicas::processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processTrait(traitType, traitBinaryData); + } + } +} +void AvatarReplicas::processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processDeletedTraitInstance(traitType, instanceID); + } + } +} +void AvatarReplicas::processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { + if (_replicasMap.find(parentID) != _replicasMap.end()) { + auto &replicas = _replicasMap[parentID]; + for (auto avatar : replicas) { + avatar->processTraitInstance(traitType, instanceID, traitBinaryData); + } + } +} AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); @@ -138,40 +211,28 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer(); + const int REPLICAS_COUNT = 8; + bool isNewAvatar; if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); - - + if (isNewAvatar) { QWriteLocker locker(&_hashLock); _pendingAvatars.insert(sessionUUID, { std::chrono::steady_clock::now(), 0, avatar }); - std::vector surrogateIDs; - for (int i = 0; i < SURROGATE_COUNT; i++) { - QUuid surrogateID = QUuid::createUuid(); - surrogateIDs.push_back(surrogateID); - auto surrogateAvatar = addAvatar(surrogateID, sendingNode); - surrogateAvatar->setSurrogateIndex(i + 1); - surrogateAvatar->parseDataFromBuffer(byteArray); - _pendingAvatars.insert(surrogateID, { std::chrono::steady_clock::now(), 0, surrogateAvatar }); + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID, REPLICAS_COUNT); + for (auto replicaID : replicaIDs) { + auto replicaAvatar = addAvatar(replicaID, sendingNode); + _replicas.addReplica(sessionUUID, replicaAvatar); + _pendingAvatars.insert(replicaID, { std::chrono::steady_clock::now(), 0, replicaAvatar }); } - _surrogates.insert(std::pair>(sessionUUID, surrogateIDs)); - } else { - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { - auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); - if (!isNewAvatar) { - surrogateAvatar->parseDataFromBuffer(byteArray); - } - } - - } + } // have the matching (or new) avatar parse the data from the packet int bytesRead = avatar->parseDataFromBuffer(byteArray); message->seek(positionBeforeRead + bytesRead); + _replicas.parseDataFromBuffer(sessionUUID, byteArray); - avatar->parseDataFromBuffer(byteArray); return avatar; } else { // create a dummy AvatarData class to throw this data on the ground @@ -216,17 +277,11 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer bool displayNameChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - auto surrogateIDs = _surrogates[identityUUID]; - for (auto id : surrogateIDs) { - auto surrogateAvatar = newOrExistingAvatar(id, sendingNode, isNewAvatar); - if (!isNewAvatar) { - surrogateAvatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); - } - } + _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); } } -void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { +void AvatarHashMap::processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { message->seek(0); while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for @@ -297,13 +352,88 @@ void AvatarHashMap::processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { + /* while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - processAvatarTraits(avatarID, message, sendingNode); - auto surrogateIDs = _surrogates[avatarID]; - for (auto id : surrogateIDs) { - processAvatarTraits(id, message, sendingNode); + processBulkAvatarTraitsForID(avatarID, message, sendingNode); + auto replicaIDs = _replicas.getReplicaIDs(avatarID); + for (auto id : replicaIDs) { + processBulkAvatarTraitsForID(id, message, sendingNode); + } + } + */ + int position = 0; + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + // grab the avatar so we can ask it to process trait data + bool isNewAvatar; + auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); + + // read the first trait type for this avatar + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + // grab the last trait versions for this avatar + auto& lastProcessedVersions = _processedTraitVersions[avatarID]; + + while (traitType != AvatarTraits::NullTrait) { + AvatarTraits::TraitVersion packetTraitVersion; + message->readPrimitive(&packetTraitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + bool skipBinaryTrait = false; + + + if (AvatarTraits::isSimpleTrait(traitType)) { + message->readPrimitive(&traitBinarySize); + + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + position = message->getPosition(); + avatar->processTrait(traitType, message->read(traitBinarySize)); + message->seek(position); + _replicas.processTrait(avatarID, traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } + else { + skipBinaryTrait = true; + } + } + else { + AvatarTraits::TraitInstanceID traitInstanceID = + QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + message->readPrimitive(&traitBinarySize); + + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); + _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); + } + else { + position = message->getPosition(); + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); + message->seek(position); + _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, message->read(traitBinarySize)); + } + processedInstanceVersion = packetTraitVersion; + } + else { + skipBinaryTrait = true; + } + } + + if (skipBinaryTrait) { + // we didn't read this trait because it was older or because we didn't have an avatar to process it for + message->seek(message->getPosition() + traitBinarySize); + } + + // read the next trait type, which is null if there are no more traits for this avatar + message->readPrimitive(&traitType); } } } @@ -315,8 +445,8 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S KillAvatarReason reason; message->readPrimitive(&reason); removeAvatar(sessionUUID, reason); - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); + for (auto id : replicaIDs) { removeAvatar(id, reason); } } @@ -324,20 +454,23 @@ void AvatarHashMap::processKillAvatar(QSharedPointer message, S void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason) { QWriteLocker locker(&_hashLock); + auto replicaIDs = _replicas.getReplicaIDs(sessionUUID); + _replicas.removeReplicas(sessionUUID); + for (auto id : replicaIDs) { + _pendingAvatars.remove(id); + auto removedReplica = _avatarHash.take(id); + if (removedReplica) { + handleRemovedAvatar(removedReplica, removalReason); + } + } + _pendingAvatars.remove(sessionUUID); auto removedAvatar = _avatarHash.take(sessionUUID); if (removedAvatar) { handleRemovedAvatar(removedAvatar, removalReason); } - auto surrogateIDs = _surrogates[sessionUUID]; - for (auto id : surrogateIDs) { - _pendingAvatars.remove(id); - auto removedSurrogate = _avatarHash.take(id); - if (removedSurrogate) { - handleRemovedAvatar(removedSurrogate, removalReason); - } - } + } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 41981df693..2d8d7f907f 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -41,6 +41,24 @@ * @hifi-assignment-client */ +class AvatarReplicas { +public: + AvatarReplicas() {}; + void addReplica(const QUuid& parentID, AvatarSharedPointer replica); + std::vector getReplicaIDs(const QUuid& parentID, int count = 0); + void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); + void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + void removeReplicas(const QUuid& parentID); + void processTrait(const QUuid& parentID, AvatarTraits::TraitType traitType, QByteArray traitBinaryData); + void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); + void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + +private: + std::map> _replicasMap; +}; + + class AvatarHashMap : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY @@ -134,7 +152,7 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); - void processAvatarTraits(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); + void processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc @@ -168,7 +186,8 @@ protected: mutable QReadWriteLock _hashLock; std::unordered_map _processedTraitVersions; - std::map> _surrogates; + AvatarReplicas _replicas; + private: QUuid _lastOwnerSessionUUID; }; From 10d0833c19c4eadd9bf9cfafeb4ee9badeeecd81 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Thu, 23 Aug 2018 15:57:54 -0700 Subject: [PATCH 168/207] use DynamicsWorld::numCollisionObjects instead of our own count --- interface/resources/qml/Stats.qml | 2 +- interface/src/Application.cpp | 4 ++-- interface/src/Application.h | 2 +- interface/src/ui/Stats.cpp | 2 +- interface/src/ui/Stats.h | 6 +++--- libraries/physics/src/PhysicsEngine.cpp | 11 ++++------- libraries/physics/src/PhysicsEngine.h | 4 +--- 7 files changed, 13 insertions(+), 18 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index d89e3333e6..54798f8762 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -61,7 +61,7 @@ Item { text: "Game Rate: " + root.gameLoopRate } StatText { - text: "Physics Body Count: " + root.physicsBodyCount + text: "Physics Object Count: " + root.physicsObjectCount } StatText { visible: root.expanded diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 9057bdea28..b27af7316f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6184,8 +6184,8 @@ bool Application::isHMDMode() const { return getActiveDisplayPlugin()->isHmd(); } -float Application::getNumRigidBodies() const { - return _physicsEngine ? _physicsEngine->getNumRigidBodies() : 0; +float Application::getNumCollisionObjects() const { + return _physicsEngine ? _physicsEngine->getNumCollisionObjects() : 0; } float Application::getTargetRenderFrameRate() const { return getActiveDisplayPlugin()->getTargetFrameRate(); } diff --git a/interface/src/Application.h b/interface/src/Application.h index e415b28343..ae7e686f35 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -207,7 +207,7 @@ public: size_t getRenderFrameCount() const { return _renderFrameCount; } float getRenderLoopRate() const { return _renderLoopCounter.rate(); } - float getNumRigidBodies() const; + float getNumCollisionObjects() const; float getTargetRenderFrameRate() const; // frames/second float getFieldOfView() { return _fieldOfView.get(); } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index 9c1e1802c7..557635469b 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -121,7 +121,7 @@ void Stats::updateStats(bool force) { auto avatarManager = DependencyManager::get(); // we need to take one avatar out so we don't include ourselves STAT_UPDATE(avatarCount, avatarManager->size() - 1); - STAT_UPDATE(physicsBodyCount, qApp->getNumRigidBodies()); + STAT_UPDATE(physicsObjectCount, qApp->getNumCollisionObjects()); STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated()); STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated()); STAT_UPDATE(serverCount, (int)nodeList->size()); diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index 35eeb78caa..84a114b72d 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -49,7 +49,7 @@ private: \ * @property {number} presentdroprate - Read-only. * @property {number} gameLoopRate - Read-only. * @property {number} avatarCount - Read-only. - * @property {number} physicsBodyCount - Read-only. + * @property {number} physicsObjectCount - Read-only. * @property {number} updatedAvatarCount - Read-only. * @property {number} notUpdatedAvatarCount - Read-only. * @property {number} packetInCount - Read-only. @@ -196,7 +196,7 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, presentdroprate, 0) STATS_PROPERTY(int, gameLoopRate, 0) STATS_PROPERTY(int, avatarCount, 0) - STATS_PROPERTY(int, physicsBodyCount, 0) + STATS_PROPERTY(int, physicsObjectCount, 0) STATS_PROPERTY(int, updatedAvatarCount, 0) STATS_PROPERTY(int, notUpdatedAvatarCount, 0) STATS_PROPERTY(int, packetInCount, 0) @@ -413,7 +413,7 @@ signals: * @function Stats.numPhysicsBodiesChanged * @returns {Signal} */ - void physicsBodyCountChanged(); + void physicsObjectCountChanged(); /**jsdoc * Triggered when the value of the avatarCount property changes. diff --git a/libraries/physics/src/PhysicsEngine.cpp b/libraries/physics/src/PhysicsEngine.cpp index 8c7b63e046..d5ce60bef9 100644 --- a/libraries/physics/src/PhysicsEngine.cpp +++ b/libraries/physics/src/PhysicsEngine.cpp @@ -77,6 +77,10 @@ uint32_t PhysicsEngine::getNumSubsteps() const { return _dynamicsWorld->getNumSubsteps(); } +int32_t PhysicsEngine::getNumCollisionObjects() const { + return _dynamicsWorld ? _dynamicsWorld->getNumCollisionObjects() : 0; +} + // private void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { assert(motionState); @@ -93,7 +97,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { btCollisionShape* shape = const_cast(motionState->getShape()); assert(shape); body = new btRigidBody(mass, motionState, shape, inertia); - ++_numRigidBodies; motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); @@ -117,7 +120,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { shape->calculateLocalInertia(mass, inertia); if (!body) { body = new btRigidBody(mass, motionState, shape, inertia); - ++_numRigidBodies; motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); @@ -141,7 +143,6 @@ void PhysicsEngine::addObjectToDynamicsWorld(ObjectMotionState* motionState) { if (!body) { assert(motionState->getShape()); body = new btRigidBody(mass, motionState, const_cast(motionState->getShape()), inertia); - ++_numRigidBodies; motionState->setRigidBody(body); } else { body->setMassProps(mass, inertia); @@ -209,7 +210,6 @@ void PhysicsEngine::removeObjects(const VectorOfMotionStates& objects) { if (body) { removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); - --_numRigidBodies; // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); @@ -227,7 +227,6 @@ void PhysicsEngine::removeSetOfObjects(const SetOfMotionStates& objects) { if (body) { removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); - --_numRigidBodies; // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); @@ -279,7 +278,6 @@ void PhysicsEngine::reinsertObject(ObjectMotionState* object) { btRigidBody* body = object->getRigidBody(); if (body) { _dynamicsWorld->removeRigidBody(body); - --_numRigidBodies; // add it back addObjectToDynamicsWorld(object); } @@ -292,7 +290,6 @@ void PhysicsEngine::processTransaction(PhysicsEngine::Transaction& transaction) if (body) { removeDynamicsForBody(body); _dynamicsWorld->removeRigidBody(body); - --_numRigidBodies; // NOTE: setRigidBody() modifies body->m_userPointer so we should clear the MotionState's body BEFORE deleting it. object->setRigidBody(nullptr); diff --git a/libraries/physics/src/PhysicsEngine.h b/libraries/physics/src/PhysicsEngine.h index 74e0c046a1..317f7ef312 100644 --- a/libraries/physics/src/PhysicsEngine.h +++ b/libraries/physics/src/PhysicsEngine.h @@ -87,6 +87,7 @@ public: void init(); uint32_t getNumSubsteps() const; + int32_t getNumCollisionObjects() const; void removeObjects(const VectorOfMotionStates& objects); void removeSetOfObjects(const SetOfMotionStates& objects); // only called during teardown @@ -143,8 +144,6 @@ public: // See PhysicsCollisionGroups.h for mask flags. std::vector contactTest(uint16_t mask, const ShapeInfo& regionShapeInfo, const Transform& regionTransform, uint16_t group = USER_COLLISION_GROUP_DYNAMIC) const; - int32_t getNumRigidBodies() const { return _numRigidBodies; } - private: QList removeDynamicsForBody(btRigidBody* body); void addObjectToDynamicsWorld(ObjectMotionState* motionState); @@ -177,7 +176,6 @@ private: CharacterController* _myAvatarController; uint32_t _numContactFrames { 0 }; - int32_t _numRigidBodies { 0 }; bool _dumpNextStats { false }; bool _saveNextStats { false }; From 220dbf586f6e6382d8dc8338c48ec9b42e210ce2 Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 16:46:58 -0700 Subject: [PATCH 169/207] set the phase to 0.0 when it is negative --- libraries/animation/src/AnimBlendLinearMove.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 6313d4cbe9..27352fd546 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -155,6 +155,7 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn // integrate phase forward in time. _phase += omega * dt; + qCDebug(animation) << "the _phase is " << _phase; // detect loop trigger events if (_phase >= 1.0f) { @@ -172,4 +173,7 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; _phase = fmodf(frame / NUM_FRAMES, 1.0f); + if (_phase < 0.0f) { + _phase = 0.0f; // 1.0f + _phase; + } } From 1263db568823c5577bd7fa1cddf897acd7711c5a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 23 Aug 2018 16:08:16 -0700 Subject: [PATCH 170/207] force NL reset if the domain changes session ID or local ID --- libraries/networking/src/NodeList.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index dd351ef940..4e8cbbf9db 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -273,6 +273,7 @@ void NodeList::reset(bool skipDomainHandlerReset) { // refresh the owner UUID to the NULL UUID setSessionUUID(QUuid()); + setSessionLocalID(Node::NULL_LOCAL_ID); // if we setup the DTLS socket, also disconnect from the DTLS socket readyRead() so it can handle handshaking if (_dtlsSocket) { @@ -647,6 +648,17 @@ void NodeList::processDomainServerList(QSharedPointer message) Node::LocalID newLocalID; packetStream >> newUUID; packetStream >> newLocalID; + + // when connected, if the session ID or local ID were not null and changed, we should reset + auto currentLocalID = getSessionLocalID(); + auto currentSessionID = getSessionUUID(); + if (_domainHandler.isConnected() && + ((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) || + (!currentSessionID.isNull() && newUUID != currentSessionID))) { + qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset"; + reset(true); + } + setSessionLocalID(newLocalID); setSessionUUID(newUUID); From 0cc302f68c9812325f19a55158503b5339ccd82b Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:28:38 -0700 Subject: [PATCH 171/207] set phase to 0.0 --- libraries/animation/src/AnimBlendLinearMove.cpp | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 27352fd546..d648b9966f 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -139,6 +139,8 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); assert(nextClipNode); + + float v0 = _characteristicSpeeds[prevPoseIndex]; float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float v1 = _characteristicSpeeds[nextPoseIndex]; @@ -153,9 +155,17 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn float f1 = nextClipNode->getStartFrame() + _phase * n1; nextClipNode->setCurrentFrame(f1); + + // integrate phase forward in time. _phase += omega * dt; - qCDebug(animation) << "the _phase is " << _phase; + + qCDebug(animation) << "the _phase is " << _phase << " and omega " << omega << _desiredSpeed; + + if (_phase < 0.0f) { + _phase = 0.0f; // 1.0f + _phase; + } + // detect loop trigger events if (_phase >= 1.0f) { @@ -173,7 +183,4 @@ void AnimBlendLinearMove::setCurrentFrameInternal(float frame) { assert(clipNode); const float NUM_FRAMES = (clipNode->getEndFrame() - clipNode->getStartFrame()) + 1.0f; _phase = fmodf(frame / NUM_FRAMES, 1.0f); - if (_phase < 0.0f) { - _phase = 0.0f; // 1.0f + _phase; - } } From 884be2f99d4a7ea417260902880e412264ad2d5a Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:30:03 -0700 Subject: [PATCH 172/207] removed comment and whitespace --- libraries/animation/src/AnimBlendLinearMove.cpp | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index d648b9966f..8879f3d9d4 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -155,18 +155,13 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn float f1 = nextClipNode->getStartFrame() + _phase * n1; nextClipNode->setCurrentFrame(f1); - - // integrate phase forward in time. _phase += omega * dt; - - qCDebug(animation) << "the _phase is " << _phase << " and omega " << omega << _desiredSpeed; if (_phase < 0.0f) { - _phase = 0.0f; // 1.0f + _phase; + _phase = 0.0f; } - // detect loop trigger events if (_phase >= 1.0f) { triggersOut.setTrigger(_id + "Loop"); From 671bf32d5813ef84837e7d22fd83933d4f9cba5e Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:36:35 -0700 Subject: [PATCH 173/207] removed whitespace --- libraries/animation/src/AnimBlendLinearMove.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/animation/src/AnimBlendLinearMove.cpp b/libraries/animation/src/AnimBlendLinearMove.cpp index 8879f3d9d4..42098eb072 100644 --- a/libraries/animation/src/AnimBlendLinearMove.cpp +++ b/libraries/animation/src/AnimBlendLinearMove.cpp @@ -139,8 +139,6 @@ void AnimBlendLinearMove::setFrameAndPhase(float dt, float alpha, int prevPoseIn auto nextClipNode = std::dynamic_pointer_cast(_children[nextPoseIndex]); assert(nextClipNode); - - float v0 = _characteristicSpeeds[prevPoseIndex]; float n0 = (prevClipNode->getEndFrame() - prevClipNode->getStartFrame()) + 1.0f; float v1 = _characteristicSpeeds[nextPoseIndex]; From b5b71676448c1805321831e6c363076d1a4c0f94 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Thu, 23 Aug 2018 17:47:25 -0700 Subject: [PATCH 174/207] add to stats --- interface/resources/qml/Stats.qml | 16 ++++ interface/src/ui/Stats.cpp | 17 ++++- interface/src/ui/Stats.h | 75 ++++++++++++++++++- .../src/RenderablePolyVoxEntityItem.cpp | 1 - libraries/entities/src/EntityTreeElement.cpp | 6 -- libraries/pointers/src/PickCacheOptimizer.h | 10 ++- libraries/pointers/src/PickManager.cpp | 14 ++-- libraries/pointers/src/PickManager.h | 6 ++ libraries/render-utils/src/Model.cpp | 12 +-- 9 files changed, 134 insertions(+), 23 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index 2e6e909312..8b277d4c20 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -116,6 +116,22 @@ Item { visible: root.expanded text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount } + StatText { + visible: root.expanded + text: "Total picks:\n " + + root.stylusPicksCount + " styluses\n " + + root.rayPicksCount + " rays\n " + + root.parabolaPicksCount + " parabolas\n " + + root.collisionPicksCount + " colliders" + } + StatText { + visible: root.expanded + text: "Intersection calls: Entities/Overlays/Avatars/HUD\n " + + root.stylusPicksUpdated.x + "/" + root.stylusPicksUpdated.y + "/" + root.stylusPicksUpdated.z + "/" + root.stylusPicksUpdated.w + "\n " + + root.rayPicksUpdated.x + "/" + root.rayPicksUpdated.y + "/" + root.rayPicksUpdated.z + "/" + root.rayPicksUpdated.w + "\n " + + root.parabolaPicksUpdated.x + "/" + root.parabolaPicksUpdated.y + "/" + root.parabolaPicksUpdated.z + "/" + root.parabolaPicksUpdated.w + "\n " + + root.collisionPicksUpdated.x + "/" + root.collisionPicksUpdated.y + "/" + root.collisionPicksUpdated.z + "/" + root.collisionPicksUpdated.w + } } } diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index ce1cd51de1..16e2bb955f 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include @@ -146,6 +147,20 @@ void Stats::updateStats(bool force) { } STAT_UPDATE(gameLoopRate, (int)qApp->getGameLoopRate()); + auto pickManager = DependencyManager::get(); + if (pickManager && (_expanded || force)) { + std::vector totalPicks = pickManager->getTotalPickCounts(); + STAT_UPDATE(stylusPicksCount, totalPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksCount, totalPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksCount, totalPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksCount, totalPicks[PickQuery::Collision]); + std::vector updatedPicks = pickManager->getUpdatedPickCounts(); + STAT_UPDATE(stylusPicksUpdated, updatedPicks[PickQuery::Stylus]); + STAT_UPDATE(rayPicksUpdated, updatedPicks[PickQuery::Ray]); + STAT_UPDATE(parabolaPicksUpdated, updatedPicks[PickQuery::Parabola]); + STAT_UPDATE(collisionPicksUpdated, updatedPicks[PickQuery::Collision]); + } + auto bandwidthRecorder = DependencyManager::get(); STAT_UPDATE(packetInCount, (int)bandwidthRecorder->getCachedTotalAverageInputPacketsPerSecond()); STAT_UPDATE(packetOutCount, (int)bandwidthRecorder->getCachedTotalAverageOutputPacketsPerSecond()); @@ -285,7 +300,7 @@ void Stats::updateStats(bool force) { // downloads << (int)(resource->getProgress() * 100.0f) << "% "; //} //downloads << "(" << << " pending)"; - } // expanded avatar column + } // Fourth column, octree stats int serverCount = 0; diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index cf624b54c3..7845a540de 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -22,7 +22,6 @@ public: \ private: \ type _##name{ initialValue }; - /**jsdoc * @namespace Stats * @@ -168,6 +167,15 @@ private: \ * @property {number} implicitHeight * * @property {object} layer - Read-only. + + * @property {number} stylusPicksCount - Read-only. + * @property {number} rayPicksCount - Read-only. + * @property {number} parabolaPicksCount - Read-only. + * @property {number} collisionPicksCount - Read-only. + * @property {Vec4} stylusPicksUpdated - Read-only. + * @property {Vec4} rayPicksUpdated - Read-only. + * @property {Vec4} parabolaPicksUpdated - Read-only. + * @property {Vec4} collisionPicksUpdated - Read-only. */ // Properties from x onwards are QQuickItem properties. @@ -285,6 +293,15 @@ class Stats : public QQuickItem { STATS_PROPERTY(float, avatarSimulationTime, 0) Q_PROPERTY(QStringList animStackNames READ animStackNames NOTIFY animStackNamesChanged) + STATS_PROPERTY(int, stylusPicksCount, 0) + STATS_PROPERTY(int, rayPicksCount, 0) + STATS_PROPERTY(int, parabolaPicksCount, 0) + STATS_PROPERTY(int, collisionPicksCount, 0) + STATS_PROPERTY(QVector4D, stylusPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, rayPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, parabolaPicksUpdated, QVector4D(0, 0, 0, 0)) + STATS_PROPERTY(QVector4D, collisionPicksUpdated, QVector4D(0, 0, 0, 0)) + public: static Stats* getInstance(); @@ -1245,6 +1262,62 @@ signals: * @function Stats.update */ + /**jsdoc + * Triggered when the value of the stylusPicksCount property changes. + * @function Stats.stylusPicksCountChanged + * @returns {Signal} + */ + void stylusPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksCount property changes. + * @function Stats.rayPicksCountChanged + * @returns {Signal} + */ + void rayPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksCount property changes. + * @function Stats.parabolaPicksCountChanged + * @returns {Signal} + */ + void parabolaPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksCount property changes. + * @function Stats.collisionPicksCountChanged + * @returns {Signal} + */ + void collisionPicksCountChanged(); + + /**jsdoc + * Triggered when the value of the stylusPicksUpdated property changes. + * @function Stats.stylusPicksUpdatedChanged + * @returns {Signal} + */ + void stylusPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the rayPicksUpdated property changes. + * @function Stats.rayPicksUpdatedChanged + * @returns {Signal} + */ + void rayPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the parabolaPicksUpdated property changes. + * @function Stats.parabolaPicksUpdatedChanged + * @returns {Signal} + */ + void parabolaPicksUpdatedChanged(); + + /**jsdoc + * Triggered when the value of the collisionPicksUpdated property changes. + * @function Stats.collisionPicksUpdatedChanged + * @returns {Signal} + */ + void collisionPicksUpdatedChanged(); + private: int _recentMaxPackets{ 0 } ; // recent max incoming voxel packets to process bool _resetRecentMaxPacketsSoon{ true }; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index 63f27dd170..af8d4fac86 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -571,7 +571,6 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o } glm::mat4 wtvMatrix = worldToVoxelMatrix(); - glm::mat4 vtwMatrix = voxelToWorldMatrix(); glm::vec3 normDirection = glm::normalize(direction); // the PolyVox ray intersection code requires a near and far point. diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index e8e11c0ee1..b7288755a6 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -145,7 +145,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con bool visibleOnly, bool collidableOnly, QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = FLT_MAX; BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -153,8 +152,6 @@ EntityItemID EntityTreeElement::findRayIntersection(const glm::vec3& origin, con return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = distance; EntityItemID entityID = findDetailedRayIntersection(origin, direction, element, distanceToElementDetails, @@ -285,7 +282,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin QVariantMap& extraInfo, bool precisionPicking) { EntityItemID result; - float distanceToElementCube = std::numeric_limits::max(); BoxFace localFace; glm::vec3 localSurfaceNormal; @@ -293,8 +289,6 @@ EntityItemID EntityTreeElement::findParabolaIntersection(const glm::vec3& origin return result; } - // if the distance to the element cube is not less than the current best distance, then it's not possible - // for any details inside the cube to be closer so we don't need to consider them. QVariantMap localExtraInfo; float distanceToElementDetails = parabolicDistance; // We can precompute the world-space parabola normal and reuse it for the parabola plane intersects AABox sphere check diff --git a/libraries/pointers/src/PickCacheOptimizer.h b/libraries/pointers/src/PickCacheOptimizer.h index 49a039935c..7a52cfc410 100644 --- a/libraries/pointers/src/PickCacheOptimizer.h +++ b/libraries/pointers/src/PickCacheOptimizer.h @@ -37,7 +37,7 @@ template class PickCacheOptimizer { public: - void update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); + QVector4D update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD); protected: typedef std::unordered_map> PickCache; @@ -67,8 +67,9 @@ void PickCacheOptimizer::cacheResult(const bool intersects, const PickResultP } template -void PickCacheOptimizer::update(std::unordered_map>& picks, +QVector4D PickCacheOptimizer::update(std::unordered_map>& picks, uint32_t& nextToUpdate, uint64_t expiry, bool shouldPickHUD) { + QVector4D numIntersectionsComputed; PickCache results; const uint32_t INVALID_PICK_ID = 0; auto itr = picks.begin(); @@ -91,6 +92,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getEntityFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, entityKey)) { PickResultPointer entityRes = pick->getEntityIntersection(mathematicalPick); + numIntersectionsComputed[0]++; if (entityRes) { cacheResult(entityRes->doesIntersect(), entityRes, entityKey, res, mathematicalPick, results, pick); } @@ -101,6 +103,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getOverlayFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, overlayKey)) { PickResultPointer overlayRes = pick->getOverlayIntersection(mathematicalPick); + numIntersectionsComputed[1]++; if (overlayRes) { cacheResult(overlayRes->doesIntersect(), overlayRes, overlayKey, res, mathematicalPick, results, pick); } @@ -111,6 +114,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getAvatarFlags(), pick->getIncludeItems(), pick->getIgnoreItems() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, avatarKey)) { PickResultPointer avatarRes = pick->getAvatarIntersection(mathematicalPick); + numIntersectionsComputed[2]++; if (avatarRes) { cacheResult(avatarRes->doesIntersect(), avatarRes, avatarKey, res, mathematicalPick, results, pick); } @@ -122,6 +126,7 @@ void PickCacheOptimizer::update(std::unordered_mapgetFilter().getHUDFlags(), QVector(), QVector() }; if (!checkAndCompareCachedResults(mathematicalPick, results, res, hudKey)) { PickResultPointer hudRes = pick->getHUDIntersection(mathematicalPick); + numIntersectionsComputed[3]++; if (hudRes) { cacheResult(true, hudRes, hudKey, res, mathematicalPick, results, pick); } @@ -145,6 +150,7 @@ void PickCacheOptimizer::update(std::unordered_mapsecond].erase(uid); _typeMap.erase(uid); + _totalPickCounts[type->second]--; } }); } @@ -96,12 +98,12 @@ void PickManager::update() { }); bool shouldPickHUD = _shouldPickHUDOperator(); - // we pass the same expiry to both updates, but the stylus updates are relatively cheap - // and the rayPicks updae will ALWAYS update at least one ray even when there is no budget - _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); - _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); - _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); - _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); + // FIXME: give each type its own expiry + // Each type will update at least one pick, regardless of the expiry + _updatedPickCounts[PickQuery::Stylus] = _stylusPickCacheOptimizer.update(cachedPicks[PickQuery::Stylus], _nextPickToUpdate[PickQuery::Stylus], expiry, false); + _updatedPickCounts[PickQuery::Ray] = _rayPickCacheOptimizer.update(cachedPicks[PickQuery::Ray], _nextPickToUpdate[PickQuery::Ray], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Parabola] = _parabolaPickCacheOptimizer.update(cachedPicks[PickQuery::Parabola], _nextPickToUpdate[PickQuery::Parabola], expiry, shouldPickHUD); + _updatedPickCounts[PickQuery::Collision] = _collisionPickCacheOptimizer.update(cachedPicks[PickQuery::Collision], _nextPickToUpdate[PickQuery::Collision], expiry, false); } bool PickManager::isLeftHand(unsigned int uid) { diff --git a/libraries/pointers/src/PickManager.h b/libraries/pointers/src/PickManager.h index 595c43e71d..e8d32771e0 100644 --- a/libraries/pointers/src/PickManager.h +++ b/libraries/pointers/src/PickManager.h @@ -58,10 +58,16 @@ public: bool getForceCoarsePicking() { return _forceCoarsePicking; } + const std::vector& getUpdatedPickCounts() { return _updatedPickCounts; } + const std::vector& getTotalPickCounts() { return _totalPickCounts; } + public slots: void setForceCoarsePicking(bool forceCoarsePicking) { _forceCoarsePicking = forceCoarsePicking; } protected: + std::vector _updatedPickCounts { PickQuery::NUM_PICK_TYPES }; + std::vector _totalPickCounts { 0, 0, 0, 0 }; + bool _forceCoarsePicking { false }; std::function _shouldPickHUDOperator; std::function _calculatePos2DFromHUDOperator; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 22bc6b6ca7..ae3ee6e9a2 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -385,9 +385,9 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { @@ -527,9 +527,9 @@ bool Model::findParabolaIntersectionAgainstSubMeshes(const glm::vec3& origin, co Triangle bestWorldTriangle; glm::vec3 bestWorldIntersectionPoint; glm::vec3 bestMeshIntersectionPoint; - int bestPartIndex; - int bestShapeID; - int bestSubMeshIndex; + int bestPartIndex = 0; + int bestShapeID = 0; + int bestSubMeshIndex = 0; const FBXGeometry& geometry = getFBXGeometry(); if (!_triangleSetsValid) { From 0e0561bb24bfbf93f62b71ef18276e15d8c415fa Mon Sep 17 00:00:00 2001 From: amantley Date: Thu, 23 Aug 2018 17:59:18 -0700 Subject: [PATCH 175/207] latest rotate code --- interface/src/avatar/MyAvatar.cpp | 20 +++++++++++++++----- scripts/developer/rotateApp.js | 2 +- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6211e6f0d8..8d9c503e5e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -462,7 +462,7 @@ void MyAvatar::update(float deltaTime) { setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - + computeHandAzimuth(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE @@ -473,6 +473,14 @@ void MyAvatar::update(float deltaTime) { glm::vec3 worldFacing = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacing.x, 0.0f, _headControllerFacing.y)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacing, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); + + // draw hand azimuth vector + + glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); + glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); + qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); + DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); } #endif @@ -802,11 +810,13 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } void MyAvatar::computeHandAzimuth() { - controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); - controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + auto leftHandPoseAvatarSpace = getLeftHandPose(); + auto rightHandPoseAvatarSpace = getRightHandPose(); glm::vec3 localLookat(0.0f, 0.0f, 1.0f); - glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.rotation * localLookat; - glm::vec3 lefttHandRigSpace = leftHandPoseAvatarSpace.rotation * localLookat; + glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); + glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); + + //qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; _hipToHandController = glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z); diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index 60d78d66d9..e4080c9bb0 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -320,7 +320,7 @@ function computeHandAzimuths(timeElapsed) { // put a hard max on this easing function. var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint; - print("rotate angle " + rotateAngle); + //print("rotate angle " + rotateAngle); return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 }); From 1fd0d7da94c3cca2f4dc367cc41f8e84eed1f072 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Thu, 23 Aug 2018 18:03:27 -0700 Subject: [PATCH 176/207] Create API call --- libraries/avatars/src/AvatarHashMap.cpp | 24 +++++++++++++++++------- libraries/avatars/src/AvatarHashMap.h | 12 ++++++++++-- 2 files changed, 27 insertions(+), 9 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 800825f574..6949efc19c 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -32,15 +32,15 @@ void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer repli replicas.push_back(replica); } -std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID, int count) { +std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID) { std::vector ids; if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; for (int i = 0; i < replicas.size(); i++) { ids.push_back(replicas[i]->getID()); } - } else if (count > 0) { - for (int i = 0; i < count; i++) { + } else if (_replicaCount > 0) { + for (int i = 0; i < _replicaCount; i++) { ids.push_back(QUuid::createUuid()); } } @@ -139,6 +139,19 @@ bool AvatarHashMap::isAvatarInRange(const glm::vec3& position, const float range return false; } +void AvatarHashMap::setReplicaCount(int count) { + _replicas.setReplicaCount(count); + auto avatars = getAvatarIdentifiers(); + for (int i = 0; i < avatars.size(); i++) { + KillAvatarReason reason = KillAvatarReason::NoReason; + removeAvatar(avatars[i], reason); + auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); + for (auto id : replicaIDs) { + removeAvatar(id, reason); + } + } +} + int AvatarHashMap::numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters) { auto hashCopy = getHashCopy(); auto rangeMeters2 = rangeMeters * rangeMeters; @@ -210,9 +223,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer(); - - const int REPLICAS_COUNT = 8; - bool isNewAvatar; if (sessionUUID != _lastOwnerSessionUUID && (!nodeList->isIgnoringNode(sessionUUID) || nodeList->getRequestsDomainListData())) { auto avatar = newOrExistingAvatar(sessionUUID, sendingNode, isNewAvatar); @@ -220,7 +230,7 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer getReplicaIDs(const QUuid& parentID, int count = 0); + std::vector getReplicaIDs(const QUuid& parentID); void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); void processAvatarIdentity(const QUuid& parentID, const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); void removeReplicas(const QUuid& parentID); @@ -53,9 +53,11 @@ public: void processDeletedTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + void setReplicaCount(int count) { _replicaCount = count; } private: std::map> _replicasMap; + int _replicaCount; }; @@ -92,6 +94,12 @@ public: // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } + /**jsdoc + * @function AvatarList.setReplicaCount + * @param {number} count // The times an avatar will get replicated + */ + Q_INVOKABLE void setReplicaCount(int count); + virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); From 44a6c7df5f1b648796d8f45fc419acea8c774873 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 24 Aug 2018 07:17:17 +0100 Subject: [PATCH 177/207] added hook to myskeleton model to update spine2, need to get the rotation from myavatar then we have spine rot --- interface/src/avatar/MyAvatar.cpp | 4 +++- interface/src/avatar/MySkeletonModel.cpp | 8 ++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 8d9c503e5e..2771f567b3 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -476,11 +476,13 @@ void MyAvatar::update(float deltaTime) { // draw hand azimuth vector + glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); + DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } #endif @@ -818,7 +820,7 @@ void MyAvatar::computeHandAzimuth() { //qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; - _hipToHandController = glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z); + _hipToHandController = lerp(glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z), glm::vec2(leftHandRigSpace.x, leftHandRigSpace.z),0.5f); } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0fc5e7521e..491fab466d 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -238,6 +238,14 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; + + // spine 2 hack to be improved. + AnimPose newSpinePose; + bool fred = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), newSpinePose); + newSpinePose.rot() = glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f)*newSpinePose.rot(); + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = newSpinePose; + params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; + } else { _prevHipsValid = false; } From 25610c75876ba7db1de50f26855582d05a4a9ac2 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Fri, 24 Aug 2018 11:04:36 +0100 Subject: [PATCH 178/207] got the hands turning the shoulders, needs error checking --- interface/src/avatar/MyAvatar.cpp | 15 +++++++++++++++ interface/src/avatar/MyAvatar.h | 2 ++ interface/src/avatar/MySkeletonModel.cpp | 5 ++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2771f567b3..97179f5d22 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -3349,6 +3349,21 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { return createMatFromQuatAndPos(headOrientationYawOnly, bodyPos); } +glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { + + static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; + glm::vec3 hipToHandRigSpace = RIG_CHANGE_OF_BASIS * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + + //to do: check for zero before normalizing. + glm::vec3 u, v, w; + generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); + glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); + + //glm::quat spine2RigSpace = glm::quat_cast(glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::normalize(hipToHandRigSpace), glm::vec3(0.0f, 1.0f, 0.0f))); + + return spine2RigSpace; +} + // ease in function for dampening cg movement static float slope(float num) { const float CURVE_CONSTANT = 1.0f; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 6b7c4e83a1..3c951264e8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1050,6 +1050,8 @@ public: // results are in sensor frame (-z forward) glm::mat4 deriveBodyFromHMDSensor() const; + glm::mat4 getSpine2RotationRigSpace() const; + glm::vec3 computeCounterBalance(); // derive avatar body position and orientation from using the current HMD Sensor location in relation to the previous diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 491fab466d..5272b1499d 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -240,9 +240,8 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // spine 2 hack to be improved. - AnimPose newSpinePose; - bool fred = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), newSpinePose); - newSpinePose.rot() = glm::quat(0.7071f, 0.0f, 0.0f, 0.7071f)*newSpinePose.rot(); + AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); + //newSpinePose.rot() = myAvatar->getSpine2RotationRigSpace();// *newSpinePose.rot(); params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = newSpinePose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; From 5654acf5bf28c9476402a1bc6a2cb4ef3545bf07 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 24 Aug 2018 09:24:58 -0700 Subject: [PATCH 179/207] Fix warnings --- libraries/avatars/src/AvatarHashMap.cpp | 99 ++----------------------- libraries/avatars/src/AvatarHashMap.h | 8 +- 2 files changed, 15 insertions(+), 92 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 6949efc19c..67cc9f0563 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -36,7 +36,7 @@ std::vector AvatarReplicas::getReplicaIDs(const QUuid& parentID) { std::vector ids; if (_replicasMap.find(parentID) != _replicasMap.end()) { auto &replicas = _replicasMap[parentID]; - for (int i = 0; i < replicas.size(); i++) { + for (int i = 0; i < (int)replicas.size(); i++) { ids.push_back(replicas[i]->getID()); } } else if (_replicaCount > 0) { @@ -288,92 +288,12 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer // In this case, the "sendingNode" is the Avatar Mixer. avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); _replicas.processAvatarIdentity(identityUUID, message->getMessage(), identityChanged, displayNameChanged); - } -} -void AvatarHashMap::processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode) { - message->seek(0); - while (message->getBytesLeftToRead()) { - // read the avatar ID to figure out which avatar this is for - auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - avatarID = sessionUUID; - - // grab the avatar so we can ask it to process trait data - bool isNewAvatar; - auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); - - // read the first trait type for this avatar - AvatarTraits::TraitType traitType; - message->readPrimitive(&traitType); - - // grab the last trait versions for this avatar - auto& lastProcessedVersions = _processedTraitVersions[avatarID]; - - while (traitType != AvatarTraits::NullTrait) { - AvatarTraits::TraitVersion packetTraitVersion; - message->readPrimitive(&packetTraitVersion); - - AvatarTraits::TraitWireSize traitBinarySize; - bool skipBinaryTrait = false; - - - if (AvatarTraits::isSimpleTrait(traitType)) { - message->readPrimitive(&traitBinarySize); - - // check if this trait version is newer than what we already have for this avatar - if (packetTraitVersion > lastProcessedVersions[traitType]) { - avatar->processTrait(traitType, message->read(traitBinarySize)); - lastProcessedVersions[traitType] = packetTraitVersion; - } - else { - skipBinaryTrait = true; - } - } - else { - AvatarTraits::TraitInstanceID traitInstanceID = - QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - - message->readPrimitive(&traitBinarySize); - - auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); - if (packetTraitVersion > processedInstanceVersion) { - if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { - avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } - else { - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - } - processedInstanceVersion = packetTraitVersion; - } - else { - skipBinaryTrait = true; - } - } - - if (skipBinaryTrait) { - // we didn't read this trait because it was older or because we didn't have an avatar to process it for - message->seek(message->getPosition() + traitBinarySize); - } - - // read the next trait type, which is null if there are no more traits for this avatar - message->readPrimitive(&traitType); - } } } void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { - /* - while (message->getBytesLeftToRead()) { - // read the avatar ID to figure out which avatar this is for - auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); - processBulkAvatarTraitsForID(avatarID, message, sendingNode); - auto replicaIDs = _replicas.getReplicaIDs(avatarID); - for (auto id : replicaIDs) { - processBulkAvatarTraitsForID(id, message, sendingNode); - } - } - */ - int position = 0; + while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -381,7 +301,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // grab the avatar so we can ask it to process trait data bool isNewAvatar; auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); - // read the first trait type for this avatar AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); @@ -402,10 +321,9 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess // check if this trait version is newer than what we already have for this avatar if (packetTraitVersion > lastProcessedVersions[traitType]) { - position = message->getPosition(); - avatar->processTrait(traitType, message->read(traitBinarySize)); - message->seek(position); - _replicas.processTrait(avatarID, traitType, message->read(traitBinarySize)); + auto traitData = message->read(traitBinarySize); + avatar->processTrait(traitType, traitData); + _replicas.processTrait(avatarID, traitType, traitData); lastProcessedVersions[traitType] = packetTraitVersion; } else { @@ -425,10 +343,9 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); } else { - position = message->getPosition(); - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - message->seek(position); - _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, message->read(traitBinarySize)); + auto traitData = message->read(traitBinarySize); + avatar->processTraitInstance(traitType, traitInstanceID, traitData); + _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData); } processedInstanceVersion = packetTraitVersion; } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index b7199d25ac..97c54e1787 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -54,6 +54,7 @@ public: void processTraitInstance(const QUuid& parentID, AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); void setReplicaCount(int count) { _replicaCount = count; } + int getReplicaCount() { return _replicaCount; } private: std::map> _replicasMap; @@ -99,6 +100,12 @@ public: * @param {number} count // The times an avatar will get replicated */ Q_INVOKABLE void setReplicaCount(int count); + + /**jsdoc + * @function AvatarList.setReplicaCount + * @param {number} count // The times an avatar will get replicated + */ + Q_INVOKABLE int getReplicaCount() { return _replicas.getReplicaCount(); }; virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); @@ -160,7 +167,6 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); - void processBulkAvatarTraitsForID(QUuid sessionUUID, QSharedPointer message, SharedNodePointer sendingNode); void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); /**jsdoc From a60ab863a39a78653a680b06d3886f2ca5d5c646 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 24 Aug 2018 11:06:36 -0700 Subject: [PATCH 180/207] demo version of the head and hands determining the rotate function --- interface/src/avatar/MyAvatar.cpp | 45 +++++++++++++++--------- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/MySkeletonModel.cpp | 5 ++- scripts/developer/rotateApp.js | 7 ++-- 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 97179f5d22..a1e30144ee 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -449,7 +449,18 @@ void MyAvatar::update(float deltaTime) { const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); float tau = deltaTime / HMD_FACING_TIMESCALE; - _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); + + // put the spine facing in sensor space. + // then mix it with head facing to determine rotation recenter + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); + glm::vec2 normedHandHip = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHip, _headControllerFacing, 0.5f); + //qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); + qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { _rotationChanged = usecTimestampNow(); @@ -479,7 +490,7 @@ void MyAvatar::update(float deltaTime) { glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); - qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); + //qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); @@ -815,13 +826,17 @@ void MyAvatar::computeHandAzimuth() { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); glm::vec3 localLookat(0.0f, 0.0f, 1.0f); - glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); - glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); - - //qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; - - _hipToHandController = lerp(glm::vec2(rightHandRigSpace.x, rightHandRigSpace.z), glm::vec2(leftHandRigSpace.x, leftHandRigSpace.z),0.5f); + // glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); + // glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); + // qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; + if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), 0.5f); + //need to use easing function here. + //var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0) / 3.0) * leftRightMidpoint; + } else { + _hipToHandController = glm::vec2(0.0f, 1.0f); + } } void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) { @@ -3359,8 +3374,6 @@ glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); - //glm::quat spine2RigSpace = glm::quat_cast(glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::normalize(hipToHandRigSpace), glm::vec3(0.0f, 1.0f, 0.0f))); - return spine2RigSpace; } @@ -3977,15 +3990,15 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { - if (!isActive(Rotation) && getForceActivateRotation()) { - activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); - setForceActivateRotation(false); - } - //if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + //if (!isActive(Rotation) && getForceActivateRotation()) { // activate(Rotation); // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + // setForceActivateRotation(false); //} + if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { + activate(Rotation); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + } if (myAvatar.getCenterOfGravityModelEnabled()) { if ((!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) || (isActive(Horizontal) && (currentVelocity > VELOCITY_THRESHHOLD))) { activate(Horizontal); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 3c951264e8..177b453267 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1663,7 +1663,7 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; - glm::vec2 _hipToHandController; // facing vector in xz plane (sensor space) + glm::vec2 _hipToHandController; // spine2 facing vector in xz plane (avatar space) float _currentStandingHeight { 0.0f }; bool _resetMode { true }; diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 5272b1499d..158f9bb0b6 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -240,9 +240,12 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // spine 2 hack to be improved. + AnimPose currentPose; + bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose); AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); + currentPose.rot() = newSpinePose.rot(); //newSpinePose.rot() = myAvatar->getSpine2RotationRigSpace();// *newSpinePose.rot(); - params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = newSpinePose; + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } else { diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js index e4080c9bb0..030385b6fb 100644 --- a/scripts/developer/rotateApp.js +++ b/scripts/developer/rotateApp.js @@ -29,6 +29,7 @@ var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 }; var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; +/* var ikTypes = { RotationAndPosition: 0, RotationOnly: 1, @@ -65,7 +66,7 @@ var handlerId = MyAvatar.addAnimationStateHandler(function (props) { return result; }, ANIM_VARS); - +*/ // define state readings constructor function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) { this.headPose = headPose; @@ -298,7 +299,7 @@ function computeHandAzimuths(timeElapsed) { var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation); - DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); + // DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); //print(leftRightMidpoint); @@ -312,7 +313,7 @@ function computeHandAzimuths(timeElapsed) { var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { //print("recenter the feet under the head"); - MyAvatar.triggerRotationRecenter(); + // MyAvatar.triggerRotationRecenter(); hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; From 1c0c17cecbb879901301feaaa73b0868a5f1335e Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 11:20:10 -0700 Subject: [PATCH 181/207] faster triangle intersection --- libraries/shared/src/GeometryUtil.cpp | 38 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/libraries/shared/src/GeometryUtil.cpp b/libraries/shared/src/GeometryUtil.cpp index a93c2ec9f3..c69b05db53 100644 --- a/libraries/shared/src/GeometryUtil.cpp +++ b/libraries/shared/src/GeometryUtil.cpp @@ -392,24 +392,34 @@ Triangle Triangle::operator*(const glm::mat4& transform) const { }; } +// https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm bool findRayTriangleIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& v0, const glm::vec3& v1, const glm::vec3& v2, float& distance, bool allowBackface) { - glm::vec3 firstSide = v0 - v1; - glm::vec3 secondSide = v2 - v1; - glm::vec3 normal = glm::cross(secondSide, firstSide); - float dividend = glm::dot(normal, v1) - glm::dot(origin, normal); - if (!allowBackface && dividend > 0.0f) { - return false; // origin below plane - } - float divisor = glm::dot(normal, direction); - if (divisor >= 0.0f) { + glm::vec3 firstSide = v1 - v0; + glm::vec3 secondSide = v2 - v0; + glm::vec3 P = glm::cross(direction, secondSide); + float det = glm::dot(firstSide, P); + if (!allowBackface && det < EPSILON) { + return false; + } else if (fabsf(det) < EPSILON) { return false; } - float t = dividend / divisor; - glm::vec3 point = origin + direction * t; - if (glm::dot(normal, glm::cross(point - v1, firstSide)) > 0.0f && - glm::dot(normal, glm::cross(secondSide, point - v1)) > 0.0f && - glm::dot(normal, glm::cross(point - v0, v2 - v0)) > 0.0f) { + + float invDet = 1.0f / det; + glm::vec3 T = origin - v0; + float u = glm::dot(T, P) * invDet; + if (u < 0.0f || u > 1.0f) { + return false; + } + + glm::vec3 Q = glm::cross(T, firstSide); + float v = glm::dot(direction, Q) * invDet; + if (v < 0.0f || u + v > 1.0f) { + return false; + } + + float t = glm::dot(secondSide, Q) * invDet; + if (t > EPSILON) { distance = t; return true; } From ddbadf5a6936e29a426bd862ffca06129cfd1fac Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Fri, 24 Aug 2018 11:59:46 -0700 Subject: [PATCH 182/207] change to Test API --- interface/src/Application.h | 3 +++ .../src/scripting/TestScriptingInterface.cpp | 8 ++++++++ interface/src/scripting/TestScriptingInterface.h | 14 ++++++++++++++ libraries/avatars/src/AvatarHashMap.h | 15 +++------------ 4 files changed, 28 insertions(+), 12 deletions(-) diff --git a/interface/src/Application.h b/interface/src/Application.h index 742cf075f6..0d4417cd91 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -311,6 +311,9 @@ public: Q_INVOKABLE void copyToClipboard(const QString& text); + int getOtherAvatarsReplicaCount() { return DependencyManager::get()->getReplicaCount(); } + void setOtherAvatarsReplicaCount(int count) { DependencyManager::get()->setReplicaCount(count); } + #if defined(Q_OS_ANDROID) void beforeEnterBackground(); void enterBackground(); diff --git a/interface/src/scripting/TestScriptingInterface.cpp b/interface/src/scripting/TestScriptingInterface.cpp index 430441226f..52f6a3ebc0 100644 --- a/interface/src/scripting/TestScriptingInterface.cpp +++ b/interface/src/scripting/TestScriptingInterface.cpp @@ -190,4 +190,12 @@ void TestScriptingInterface::saveObject(QVariant variant, const QString& filenam void TestScriptingInterface::showMaximized() { qApp->getWindow()->showMaximized(); +} + +void TestScriptingInterface::setOtherAvatarsReplicaCount(int count) { + qApp->setOtherAvatarsReplicaCount(count); +} + +int TestScriptingInterface::getOtherAvatarsReplicaCount() { + return qApp->getOtherAvatarsReplicaCount(); } \ No newline at end of file diff --git a/interface/src/scripting/TestScriptingInterface.h b/interface/src/scripting/TestScriptingInterface.h index c47e39d1f3..4a1d1a3eeb 100644 --- a/interface/src/scripting/TestScriptingInterface.h +++ b/interface/src/scripting/TestScriptingInterface.h @@ -149,6 +149,20 @@ public slots: */ void showMaximized(); + /**jsdoc + * Values higher than 0 will create replicas of other-avatars when entering a domain for testing purpouses + * @function Test.setOtherAvatarsReplicaCount + * @param {number} count - Number of replicas we want to create + */ + Q_INVOKABLE void setOtherAvatarsReplicaCount(int count); + + /**jsdoc + * Return the number of replicas that are being created of other-avatars when entering a domain + * @function Test.getOtherAvatarsReplicaCount + * @returns {number} Current number of replicas of other-avatars. + */ + Q_INVOKABLE int getOtherAvatarsReplicaCount(); + private: bool waitForCondition(qint64 maxWaitMs, std::function condition); QString _testResultsLocation; diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index 97c54e1787..e4e197a080 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -95,21 +95,12 @@ public: // Null/Default-constructed QUuids will return MyAvatar Q_INVOKABLE virtual ScriptAvatarData* getAvatar(QUuid avatarID) { return new ScriptAvatarData(getAvatarBySessionID(avatarID)); } - /**jsdoc - * @function AvatarList.setReplicaCount - * @param {number} count // The times an avatar will get replicated - */ - Q_INVOKABLE void setReplicaCount(int count); - - /**jsdoc - * @function AvatarList.setReplicaCount - * @param {number} count // The times an avatar will get replicated - */ - Q_INVOKABLE int getReplicaCount() { return _replicas.getReplicaCount(); }; - virtual AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const { return findAvatar(sessionID); } int numberOfAvatarsInRange(const glm::vec3& position, float rangeMeters); + void setReplicaCount(int count); + int getReplicaCount() { return _replicas.getReplicaCount(); }; + signals: /**jsdoc From cb4ce4fedb8bc6155b71ae08ad074037b750defd Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 24 Aug 2018 13:05:04 -0700 Subject: [PATCH 183/207] make sure settings are re-requested on new connection --- libraries/networking/src/NodeList.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp index 4e8cbbf9db..e458ffab7e 100644 --- a/libraries/networking/src/NodeList.cpp +++ b/libraries/networking/src/NodeList.cpp @@ -656,7 +656,13 @@ void NodeList::processDomainServerList(QSharedPointer message) ((currentLocalID != Node::NULL_LOCAL_ID && newLocalID != currentLocalID) || (!currentSessionID.isNull() && newUUID != currentSessionID))) { qCDebug(networking) << "Local ID or Session ID changed while connected to domain - forcing NodeList reset"; + + // reset the nodelist, but don't do a domain handler reset since we're about to process a good domain list reset(true); + + // tell the domain handler that we're no longer connected so that below + // it can re-perform actions as if we just connected + _domainHandler.setIsConnected(false); } setSessionLocalID(newLocalID); From d8b5a1a4a6ddd80ec02ce3bce8911c6ff9d9766a Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 24 Aug 2018 14:21:54 -0700 Subject: [PATCH 184/207] always remove old socket-matching node before add --- domain-server/src/DomainGatekeeper.cpp | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/domain-server/src/DomainGatekeeper.cpp b/domain-server/src/DomainGatekeeper.cpp index 2307b1be3b..e23d9e57a8 100644 --- a/domain-server/src/DomainGatekeeper.cpp +++ b/domain-server/src/DomainGatekeeper.cpp @@ -463,19 +463,15 @@ SharedNodePointer DomainGatekeeper::processAgentConnectRequest(const NodeConnect limitedNodeList->eachNodeBreakable([nodeConnection, username, &existingNodeID](const SharedNodePointer& node){ if (node->getPublicSocket() == nodeConnection.publicSockAddr && node->getLocalSocket() == nodeConnection.localSockAddr) { - // we have a node that already has these exact sockets - this can occur if a node - // is failing to connect to the domain - - // we'll re-use the existing node ID - // as long as the user hasn't changed their username (by logging in or logging out) - auto existingNodeData = static_cast(node->getLinkedData()); - - if (existingNodeData->getUsername() == username) { - qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); - existingNodeID = node->getUUID(); - return false; - } + // we have a node that already has these exact sockets + // this can occur if a node is failing to connect to the domain + + // remove the old node before adding the new node + qDebug() << "Deleting existing connection from same sockaddr: " << node->getUUID(); + existingNodeID = node->getUUID(); + return false; } + return true; }); From 98d145dc2487508bcf1d4076892cb6f0cf14e3dc Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 15:19:48 -0700 Subject: [PATCH 185/207] fix crash on shutdown in domain with material --- interface/src/Application.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e290531471..44bba6250b 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1841,6 +1841,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo }); EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) { + if (_aboutToQuit) { + return false; + } + // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); if (renderable) { @@ -1856,6 +1860,10 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo return false; }); EntityTree::setRemoveMaterialFromEntityOperator([this](const QUuid& entityID, graphics::MaterialPointer material, const std::string& parentMaterialName) { + if (_aboutToQuit) { + return false; + } + // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); if (renderable) { From ff6020e0ef2489f3cfbe5e883d1688b73a0b3971 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Fri, 24 Aug 2018 15:41:25 -0700 Subject: [PATCH 186/207] fix compile errors, maybe --- libraries/render-utils/src/Model.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 4fc8e265ab..71250c6483 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -319,7 +319,7 @@ bool Model::updateGeometry() { // Interleave normals and tangents // Parallel version for performance - tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { + tbb::parallel_for(tbb::blocked_range(0, mesh.normals.size()), [&](const tbb::blocked_range& range) { auto normalsRange = std::make_pair(mesh.normals.begin() + range.begin(), mesh.normals.begin() + range.end()); auto tangentsRange = std::make_pair(mesh.tangents.begin() + range.begin(), mesh.tangents.begin() + range.end()); auto normalsAndTangentsIt = normalsAndTangents.begin() + 2 * range.begin(); @@ -1281,7 +1281,7 @@ void Blender::run() { } float normalCoefficient = vertexCoefficient * NORMAL_COEFFICIENT_SCALE; const FBXBlendshape& blendshape = mesh.blendshapes.at(i); - tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { + tbb::parallel_for(tbb::blocked_range(0, blendshape.indices.size()), [&](const tbb::blocked_range& range) { for (auto j = range.begin(); j < range.end(); j++) { int index = blendshape.indices.at(j); meshVertices[index] += blendshape.vertices.at(j) * vertexCoefficient; From 2bec8678639e2aff14f83063be6e2154c34ee983 Mon Sep 17 00:00:00 2001 From: amantley Date: Fri, 24 Aug 2018 16:29:08 -0700 Subject: [PATCH 187/207] added the sanity check to see if the shoulders have flipped --- interface/src/avatar/MyAvatar.cpp | 19 ++++++++++++++++--- interface/src/avatar/MyAvatar.h | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index a1e30144ee..ae02eacf75 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -458,9 +458,11 @@ void MyAvatar::update(float deltaTime) { glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); glm::vec2 normedHandHip = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHip, _headControllerFacing, 0.5f); - //qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; + // if head facing dot mid point hands facing is close to -1 and the hands midpoint is close to -1 you then flip + + // qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); - qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; + // qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { _rotationChanged = usecTimestampNow(); @@ -831,11 +833,22 @@ void MyAvatar::computeHandAzimuth() { // qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + vec2 oldAzimuthReading = _hipToHandController; _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), 0.5f); + + //if they are 180 apart + float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); + float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); + if (cosBackwardAngle > cosForwardAngle) { + // this means we have lost continuity with the current chest position + _hipToHandController = -_hipToHandController; + } + + //need to use easing function here. //var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0) / 3.0) * leftRightMidpoint; } else { - _hipToHandController = glm::vec2(0.0f, 1.0f); + // _hipToHandController = glm::vec2(0.0f, -1.0f); } } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 177b453267..ea5059bf39 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1663,7 +1663,7 @@ private: glm::vec2 _headControllerFacingMovingAverage { 0.0f, 0.0f }; // facing vector in xz plane (sensor space) glm::quat _averageHeadRotation { 0.0f, 0.0f, 0.0f, 1.0f }; - glm::vec2 _hipToHandController; // spine2 facing vector in xz plane (avatar space) + glm::vec2 _hipToHandController { 0.0f, -1.0f }; // spine2 facing vector in xz plane (avatar space) float _currentStandingHeight { 0.0f }; bool _resetMode { true }; From 8ede6f1cd09cf2ed33a006fa9629dae134fe0d5f Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 27 Aug 2018 06:55:52 -0700 Subject: [PATCH 188/207] Fix removing my avatar and code standards --- libraries/avatars/src/AvatarData.cpp | 16 ++++----------- libraries/avatars/src/AvatarHashMap.cpp | 26 ++++++++++++------------- libraries/avatars/src/AvatarHashMap.h | 4 ++-- 3 files changed, 19 insertions(+), 27 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ddb53f8acb..5e1094276c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -919,19 +919,11 @@ int AvatarData::parseDataFromBuffer(const QByteArray& buffer) { PACKET_READ_CHECK(AvatarGlobalPosition, sizeof(AvatarDataPacket::AvatarGlobalPosition)); auto data = reinterpret_cast(sourceBuffer); - const float SPACE_BETWEEN_AVATARS = 2.0f; - const int RANDOM_RADIUS = 100; - const int AVATARS_PER_ROW = 3; + glm::vec3 offset = glm::vec3(0.0f, 0.0f, 0.0f); - glm::vec3 offset; - - if (false) { - qsrand(static_cast(getID().toByteArray().toInt())); - float xrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); - float yrand = float((qrand() % ((RANDOM_RADIUS + 1) - 10) + 10) / 10.0f); - offset = glm::vec3(xrand, 0.0f, yrand); - } - else { + if (_replicaIndex > 0) { + const float SPACE_BETWEEN_AVATARS = 2.0f; + const int AVATARS_PER_ROW = 3; int row = _replicaIndex % AVATARS_PER_ROW; int col = floor(_replicaIndex / AVATARS_PER_ROW); offset = glm::vec3(row * SPACE_BETWEEN_AVATARS, 0.0f, col * SPACE_BETWEEN_AVATARS); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 67cc9f0563..8dfee3551e 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -23,6 +23,9 @@ void AvatarReplicas::addReplica(const QUuid& parentID, AvatarSharedPointer replica) { + if (parentID == QUuid()) { + return; + } if (_replicasMap.find(parentID) == _replicasMap.end()) { std::vector emptyReplicas = std::vector(); _replicasMap.insert(std::pair>(parentID, emptyReplicas)); @@ -144,10 +147,12 @@ void AvatarHashMap::setReplicaCount(int count) { auto avatars = getAvatarIdentifiers(); for (int i = 0; i < avatars.size(); i++) { KillAvatarReason reason = KillAvatarReason::NoReason; - removeAvatar(avatars[i], reason); - auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); - for (auto id : replicaIDs) { - removeAvatar(id, reason); + if (avatars[i] != QUuid()) { + removeAvatar(avatars[i], reason); + auto replicaIDs = _replicas.getReplicaIDs(avatars[i]); + for (auto id : replicaIDs) { + removeAvatar(id, reason); + } } } } @@ -315,7 +320,6 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitWireSize traitBinarySize; bool skipBinaryTrait = false; - if (AvatarTraits::isSimpleTrait(traitType)) { message->readPrimitive(&traitBinarySize); @@ -325,12 +329,10 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess avatar->processTrait(traitType, traitData); _replicas.processTrait(avatarID, traitType, traitData); lastProcessedVersions[traitType] = packetTraitVersion; - } - else { + } else { skipBinaryTrait = true; } - } - else { + } else { AvatarTraits::TraitInstanceID traitInstanceID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); @@ -341,15 +343,13 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { avatar->processDeletedTraitInstance(traitType, traitInstanceID); _replicas.processDeletedTraitInstance(avatarID, traitType, traitInstanceID); - } - else { + } else { auto traitData = message->read(traitBinarySize); avatar->processTraitInstance(traitType, traitInstanceID, traitData); _replicas.processTraitInstance(avatarID, traitType, traitInstanceID, traitData); } processedInstanceVersion = packetTraitVersion; - } - else { + } else { skipBinaryTrait = true; } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index e4e197a080..0f847b2a61 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -43,7 +43,7 @@ class AvatarReplicas { public: - AvatarReplicas() : _replicaCount(0) {} + AvatarReplicas() {} void addReplica(const QUuid& parentID, AvatarSharedPointer replica); std::vector getReplicaIDs(const QUuid& parentID); void parseDataFromBuffer(const QUuid& parentID, const QByteArray& buffer); @@ -58,7 +58,7 @@ public: private: std::map> _replicasMap; - int _replicaCount; + int _replicaCount { 0 }; }; From 945982b1bdfa576ab07a2e8cbb416ed1f34c44f3 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Wed, 11 Jul 2018 14:05:07 -0700 Subject: [PATCH 189/207] Working on pauses in tomb domain --- libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp | 1 - libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp index 01424c2ac6..c1848d99b1 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackend.cpp @@ -765,7 +765,6 @@ void GLBackend::recycle() const { } _textureManagement._transferEngine->manageMemory(); - Texture::KtxStorage::releaseOpenKtxFiles(); } void GLBackend::setCameraCorrection(const Mat4& correction, const Mat4& prevRenderView, bool reset) { diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp index eaee054b7d..f4e81448cc 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLTextureTransfer.cpp @@ -405,6 +405,7 @@ bool GLTextureTransferEngineDefault::processActiveBufferQueue() { _activeTransferQueue.splice(_activeTransferQueue.end(), activeBufferQueue); } + Texture::KtxStorage::releaseOpenKtxFiles(); return true; } From 055f03878e1879f0b461e909e54d1cb1877379df Mon Sep 17 00:00:00 2001 From: amantley Date: Mon, 27 Aug 2018 14:44:27 -0700 Subject: [PATCH 190/207] made changes to add roll to the spine2 rotation. also only the shoulders control the rotation recenter now --- interface/src/avatar/MyAvatar.cpp | 70 +++++++++--------------- interface/src/avatar/MyAvatar.h | 4 +- interface/src/avatar/MySkeletonModel.cpp | 17 ++++-- libraries/shared/src/AvatarConstants.h | 2 +- 4 files changed, 41 insertions(+), 52 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 6012bc4419..c4d855ee77 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -447,6 +447,7 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); + const float PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH = 0.0f; // 100 percent shoulders float tau = deltaTime / HMD_FACING_TIMESCALE; @@ -456,13 +457,13 @@ void MyAvatar::update(float deltaTime) { glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); - glm::vec2 normedHandHip = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHip, _headControllerFacing, 0.5f); - // if head facing dot mid point hands facing is close to -1 and the hands midpoint is close to -1 you then flip - - // qCDebug(interfaceapp) << "the hand hip azimuth " << normedHandHip << "the head look at" << _headControllerFacing; - _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); - // qCDebug(interfaceapp) << "the hand hip azimuth average " << _headControllerFacingMovingAverage; + glm::vec2 normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); + if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); + } else { + _headControllerFacingMovingAverage = _headControllerFacing; + } if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { _rotationChanged = usecTimestampNow(); @@ -477,7 +478,6 @@ void MyAvatar::update(float deltaTime) { computeHandAzimuth(); - #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); @@ -488,13 +488,7 @@ void MyAvatar::update(float deltaTime) { DebugDraw::getInstance().drawRay(worldHeadPos, worldHeadPos + worldFacingAverage, glm::vec4(0.0f, 0.0f, 1.0f, 1.0f)); // draw hand azimuth vector - glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); - glm::vec3 worldRHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).getTranslation()); - glm::vec3 worldLHandAzimuth = transformPoint(getTransform().getMatrix(), getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).getTranslation()); - //qCDebug(interfaceapp) << "the right hand in avatar space" << worldRHandAzimuth << " " << getWorldPosition(); - DebugDraw::getInstance().drawRay(getWorldPosition(), worldRHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); - DebugDraw::getInstance().drawRay(getWorldPosition(), worldLHandAzimuth, glm::vec4(0.0f, 1.0f, 0.0f, 1.0f)); DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } #endif @@ -502,7 +496,11 @@ void MyAvatar::update(float deltaTime) { if (_goToPending) { setWorldPosition(_goToPosition); setWorldOrientation(_goToOrientation); - _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average + if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { + _headControllerFacingMovingAverage = headFacingPlusHandHipAzimuthMix; + } else { + _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average + } _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). @@ -824,31 +822,29 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { } } +// Find the vector halfway between the hip to hand azimuth vectors +// This midpoint hand azimuth is in Avatar space void MyAvatar::computeHandAzimuth() { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); glm::vec3 localLookat(0.0f, 0.0f, 1.0f); - // glm::vec3 rightHandRigSpace = rightHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), rightHandPoseAvatarSpace.translation); - // glm::vec3 leftHandRigSpace = leftHandPoseAvatarSpace.translation;// transformVectorFast(getTransform().getMatrix(), leftHandPoseAvatarSpace.translation); + const float HALFWAY = 0.50f; - // qCDebug(interfaceapp) << "the right hand in avatar space" << rightHandRigSpace; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + // we need the old azimuth reading to prevent flipping the facing direction 180 + // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. vec2 oldAzimuthReading = _hipToHandController; - _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), 0.5f); - - //if they are 180 apart + _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + + // check the angular distance from forward and back float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); + // if we are now closer to the 180 flip of the previous chest forward + // then we negate our computed _hipToHandController to keep the chest in the same direction. if (cosBackwardAngle > cosForwardAngle) { // this means we have lost continuity with the current chest position _hipToHandController = -_hipToHandController; } - - - //need to use easing function here. - //var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0) / 3.0) * leftRightMidpoint; - } else { - // _hipToHandController = glm::vec2(0.0f, -1.0f); } } @@ -3382,11 +3378,9 @@ glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; glm::vec3 hipToHandRigSpace = RIG_CHANGE_OF_BASIS * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); - //to do: check for zero before normalizing. glm::vec3 u, v, w; generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); - return spine2RigSpace; } @@ -3867,7 +3861,7 @@ void MyAvatar::lateUpdatePalms() { } -static const float FOLLOW_TIME = 0.1f; +static const float FOLLOW_TIME = 0.5f; MyAvatar::FollowHelper::FollowHelper() { deactivate(); @@ -3972,11 +3966,6 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 defaultHeadPosition = myAvatar.getAbsoluteDefaultJointTranslationInObjectFrame(myAvatar.getJointIndex("Head")); glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); - //if (!isActive(Horizontal) && - // (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + DEFAULT_AVATAR_SPINE_STRETCH_LIMIT))) { - // myAvatar.setResetMode(true); - // stepDetected = true; - //} if (!isActive(Horizontal) && (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) { myAvatar.setResetMode(true); @@ -3997,23 +3986,15 @@ bool MyAvatar::FollowHelper::shouldActivateVertical(const MyAvatar& myAvatar, co void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat4& desiredBodyMatrix, const glm::mat4& currentBodyMatrix, bool hasDriveInput) { - const float VELOCITY_THRESHHOLD = 1.0f; - float currentVelocity = glm::length(myAvatar.getLocalVelocity() / myAvatar.getSensorToWorldScale()); - if (myAvatar.getHMDLeanRecenterEnabled() && qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { - //if (!isActive(Rotation) && getForceActivateRotation()) { - // activate(Rotation); - // myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); - // setForceActivateRotation(false); - //} if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); } if (myAvatar.getCenterOfGravityModelEnabled()) { - if ((!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) || (isActive(Horizontal) && (currentVelocity > VELOCITY_THRESHHOLD))) { + if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); @@ -4029,7 +4010,6 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } } } - if (!isActive(Vertical) && (shouldActivateVertical(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Vertical); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ea5059bf39..f819c417de 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -285,7 +285,7 @@ public: Q_INVOKABLE void centerBody(); // thread-safe - /**jsdoc + /**jsdoc_hmdSensorMatrix * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent * contorted joints. * @function MyAvatar.clearIKJointLimitHistory @@ -914,6 +914,8 @@ public: virtual void rebuildCollisionShape() override; + const glm::vec2& getHeadControllerFacing() const { return _headControllerFacing; } + void setHeadControllerFacing(glm::vec2 currentHeadControllerFacing) { _headControllerFacing = currentHeadControllerFacing; } const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } void setHeadControllerFacingMovingAverage(glm::vec2 currentHeadControllerFacing) { _headControllerFacingMovingAverage = currentHeadControllerFacing; } float getCurrentStandingHeight() const { return _currentStandingHeight; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 158f9bb0b6..f150379f0c 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -238,13 +238,20 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerPoses[Rig::PrimaryControllerType_Hips] = sensorToRigPose * hips; params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; - - // spine 2 hack to be improved. + // set spine2 if we have hand controllers AnimPose currentPose; bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose); - AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); - currentPose.rot() = newSpinePose.rot(); - //newSpinePose.rot() = myAvatar->getSpine2RotationRigSpace();// *newSpinePose.rot(); + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); + glm::vec3 u, v, w; + glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 up = currentPose.rot() * glm::vec3(0.0f, 1.0f, 0.0f); + generateBasisVectors(up, fwd, u, v, w); + AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); + + if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) { + AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); + currentPose.rot() = newSpinePose.rot(); + } params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose; params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; diff --git a/libraries/shared/src/AvatarConstants.h b/libraries/shared/src/AvatarConstants.h index b97f586d32..c3e8a3f173 100644 --- a/libraries/shared/src/AvatarConstants.h +++ b/libraries/shared/src/AvatarConstants.h @@ -34,7 +34,7 @@ const float DEFAULT_HANDS_ANGULAR_VELOCITY_STEPPING_THRESHOLD = 3.3f; const float DEFAULT_HEAD_VELOCITY_STEPPING_THRESHOLD = 0.18f; const float DEFAULT_HEAD_PITCH_STEPPING_TOLERANCE = 7.0f; const float DEFAULT_HEAD_ROLL_STEPPING_TOLERANCE = 7.0f; -const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.05f; +const float DEFAULT_AVATAR_SPINE_STRETCH_LIMIT = 0.04f; const float DEFAULT_AVATAR_FORWARD_DAMPENING_FACTOR = 0.5f; const float DEFAULT_AVATAR_LATERAL_DAMPENING_FACTOR = 2.0f; const float DEFAULT_AVATAR_HIPS_MASS = 40.0f; From b6edb2c9d6afcc07d266b45fa63b3536e35469eb Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 14:53:13 -0700 Subject: [PATCH 191/207] platform states, edit controller mapping, separate mac/win delete --- interface/src/Application.cpp | 27 ++++++++++++++++++- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 ++ scripts/system/edit.js | 17 +++++++++--- 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e290531471..12aed213c9 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -726,6 +726,9 @@ static const QString STATE_SNAP_TURN = "SnapTurn"; static const QString STATE_ADVANCED_MOVEMENT_CONTROLS = "AdvancedMovement"; static const QString STATE_GROUNDED = "Grounded"; static const QString STATE_NAV_FOCUSED = "NavigationFocused"; +static const QString STATE_PLATFORM_WINDOWS = "PlatformWindows"; +static const QString STATE_PLATFORM_MAC = "PlatformMac"; +static const QString STATE_PLATFORM_ANDROID = "PlatformAndroid"; // Statically provided display and input plugins extern DisplayPluginList getDisplayPlugins(); @@ -909,7 +912,8 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { DependencyManager::set(); controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, - STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED } }); + STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, + STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_WINDOWS } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -1683,6 +1687,27 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo _applicationStateDevice->setInputVariant(STATE_NAV_FOCUSED, []() -> float { return DependencyManager::get()->navigationFocused() ? 1 : 0; }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_WINDOWS, []() -> float { +#if defined(Q_OS_WIN) + return 1; +#else + return 0; +#endif + }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_MAC, []() -> float { +#if defined(Q_OS_MAC) + return 1; +#else + return 0; +#endif + }); + _applicationStateDevice->setInputVariant(STATE_PLATFORM_ANDROID, []() -> float { +#if defined(Q_OS_ANDROID) + return 1; +#else + return 0; +#endif + }); // Setup the _keyboardMouseDevice, _touchscreenDevice, _touchscreenVirtualPadDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 650c9675a7..ddd51e9b9b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -302,6 +302,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_PageDown), QKeySequence(Qt::Key_PageDown).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Tab), QKeySequence(Qt::Key_Tab).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index adee7c6236..776c09addd 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -20,6 +20,8 @@ var EDIT_TOGGLE_BUTTON = "com.highfidelity.interface.system.editButton"; +var CONTROLLER_MAPPING_NAME = "com.highfidelity.editMode"; + Script.include([ "libraries/stringHelpers.js", "libraries/dataViewHelpers.js", @@ -860,6 +862,7 @@ var toolBar = (function () { cameraManager.disable(); selectionDisplay.triggerMapping.disable(); tablet.landscape = false; + Controller.disableMapping(CONTROLLER_MAPPING_NAME); } else { if (shouldUseEditTabletApp()) { tablet.loadQMLSource("hifi/tablet/Edit.qml", true); @@ -876,6 +879,7 @@ var toolBar = (function () { selectionDisplay.triggerMapping.enable(); print("starting tablet in landscape mode"); tablet.landscape = true; + Controller.enableMapping(CONTROLLER_MAPPING_NAME); // Not sure what the following was meant to accomplish, but it currently causes // everybody else to think that Interface has lost focus overall. fogbugzid:558 // Window.setFocus(); @@ -1859,9 +1863,7 @@ var keyReleaseEvent = function (event) { cameraManager.keyReleaseEvent(event); } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text === "DELETE") { - deleteSelectedEntities(); - } else if (event.text === 'd' && event.isControl) { + if (event.text === 'd' && event.isControl) { selectionManager.clearSelections(); } else if (event.text === "t") { selectionDisplay.toggleSpaceMode(); @@ -1893,6 +1895,15 @@ var keyReleaseEvent = function (event) { Controller.keyReleaseEvent.connect(keyReleaseEvent); Controller.keyPressEvent.connect(keyPressEvent); +function deleteKey(value) { + if (value === 0) { // on release + deleteSelectedEntities(); + } +} +var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).peek().to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).peek().to(deleteKey); + function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { var children = parentData.children; From aae06e8f49d2c0ecf33ccd8eb343992cb5d6379f Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Mon, 27 Aug 2018 15:40:33 -0700 Subject: [PATCH 192/207] faster aabox ray intersection and pre-computed inverse direction --- .../ui/overlays/ContextOverlayInterface.cpp | 2 +- interface/src/ui/overlays/Volume3DOverlay.cpp | 2 +- .../src/RenderablePolyVoxEntityItem.cpp | 4 +- libraries/entities/src/EntityTree.cpp | 5 +- libraries/entities/src/EntityTreeElement.cpp | 2 +- libraries/render-utils/src/Model.cpp | 9 +- libraries/render-utils/src/PickItemsJob.cpp | 3 +- libraries/shared/src/AABox.cpp | 6 +- libraries/shared/src/AABox.h | 2 +- libraries/shared/src/AACube.cpp | 6 +- libraries/shared/src/AACube.h | 6 +- libraries/shared/src/GeometryUtil.cpp | 89 +++++++------------ libraries/shared/src/GeometryUtil.h | 4 +- libraries/shared/src/TriangleSet.cpp | 16 ++-- libraries/shared/src/TriangleSet.h | 4 +- 15 files changed, 69 insertions(+), 91 deletions(-) diff --git a/interface/src/ui/overlays/ContextOverlayInterface.cpp b/interface/src/ui/overlays/ContextOverlayInterface.cpp index 789b1f9969..ba9a1f9fc9 100644 --- a/interface/src/ui/overlays/ContextOverlayInterface.cpp +++ b/interface/src/ui/overlays/ContextOverlayInterface.cpp @@ -180,7 +180,7 @@ bool ContextOverlayInterface::createOrDestroyContextOverlay(const EntityItemID& float distance; BoxFace face; glm::vec3 normal; - boundingBox.findRayIntersection(cameraPosition, direction, distance, face, normal); + boundingBox.findRayIntersection(cameraPosition, direction, 1.0f / direction, distance, face, normal); float offsetAngle = -CONTEXT_OVERLAY_OFFSET_ANGLE; if (event.getID() == 1) { // "1" is left hand offsetAngle *= -1.0f; diff --git a/interface/src/ui/overlays/Volume3DOverlay.cpp b/interface/src/ui/overlays/Volume3DOverlay.cpp index c87650a77b..a307d445c0 100644 --- a/interface/src/ui/overlays/Volume3DOverlay.cpp +++ b/interface/src/ui/overlays/Volume3DOverlay.cpp @@ -88,7 +88,7 @@ bool Volume3DOverlay::findRayIntersection(const glm::vec3& origin, const glm::ve // we can use the AABox's ray intersection by mapping our origin and direction into the overlays frame // and testing intersection there. - bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, distance, face, surfaceNormal); + bool hit = _localBoundingBox.findRayIntersection(overlayFrameOrigin, overlayFrameDirection, 1.0f / overlayFrameDirection, distance, face, surfaceNormal); if (hit) { surfaceNormal = transform.getRotation() * surfaceNormal; diff --git a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp index af8d4fac86..2da5c30dc0 100644 --- a/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp +++ b/libraries/entities-renderer/src/RenderablePolyVoxEntityItem.cpp @@ -596,8 +596,8 @@ bool RenderablePolyVoxEntityItem::findDetailedRayIntersection(const glm::vec3& o voxelBox += result3 - Vectors::HALF; voxelBox += result3 + Vectors::HALF; - glm::vec4 directionInVoxel = wtvMatrix * glm::vec4(direction, 0.0f); - return voxelBox.findRayIntersection(glm::vec3(originInVoxel), glm::vec3(directionInVoxel), + glm::vec3 directionInVoxel = vec3(wtvMatrix * glm::vec4(direction, 0.0f)); + return voxelBox.findRayIntersection(glm::vec3(originInVoxel), directionInVoxel, 1.0f / directionInVoxel, distance, face, surfaceNormal); } diff --git a/libraries/entities/src/EntityTree.cpp b/libraries/entities/src/EntityTree.cpp index 1cf6e45d5b..a7c88ddc7d 100644 --- a/libraries/entities/src/EntityTree.cpp +++ b/libraries/entities/src/EntityTree.cpp @@ -48,6 +48,7 @@ public: // Inputs glm::vec3 origin; glm::vec3 direction; + glm::vec3 invDirection; const QVector& entityIdsToInclude; const QVector& entityIdsToDiscard; bool visibleOnly; @@ -847,7 +848,7 @@ float findRayIntersectionSortingOp(const OctreeElementPointer& element, void* ex float boundDistance = FLT_MAX; BoxFace face; glm::vec3 surfaceNormal; - if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, boundDistance, face, surfaceNormal)) { + if (entityTreeElementPointer->getAACube().findRayIntersection(args->origin, args->direction, args->invDirection, boundDistance, face, surfaceNormal)) { // Don't add this cell if it's already farther than our best distance so far if (boundDistance < args->distance) { distance = boundDistance; @@ -863,7 +864,7 @@ EntityItemID EntityTree::findRayIntersection(const glm::vec3& origin, const glm: OctreeElementPointer& element, float& distance, BoxFace& face, glm::vec3& surfaceNormal, QVariantMap& extraInfo, Octree::lockType lockType, bool* accurateResult) { - RayArgs args = { origin, direction, entityIdsToInclude, entityIdsToDiscard, + RayArgs args = { origin, direction, 1.0f / direction, entityIdsToInclude, entityIdsToDiscard, visibleOnly, collidableOnly, precisionPicking, element, distance, face, surfaceNormal, extraInfo, EntityItemID() }; distance = FLT_MAX; diff --git a/libraries/entities/src/EntityTreeElement.cpp b/libraries/entities/src/EntityTreeElement.cpp index b7288755a6..efe5dafccf 100644 --- a/libraries/entities/src/EntityTreeElement.cpp +++ b/libraries/entities/src/EntityTreeElement.cpp @@ -215,7 +215,7 @@ EntityItemID EntityTreeElement::findDetailedRayIntersection(const glm::vec3& ori float localDistance; BoxFace localFace; glm::vec3 localSurfaceNormal; - if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, localDistance, + if (entityFrameBox.findRayIntersection(entityFrameOrigin, entityFrameDirection, 1.0f / entityFrameDirection, localDistance, localFace, localSurfaceNormal)) { if (entityFrameBox.contains(entityFrameOrigin) || localDistance < distance) { // now ask the entity if we actually intersect diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index ae3ee6e9a2..18ee05ddd4 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -376,7 +376,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g // we can use the AABox's intersection by mapping our origin and direction into the model frame // and testing intersection there. - if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, distance, face, surfaceNormal)) { + if (modelFrameBox.findRayIntersection(modelFrameOrigin, modelFrameDirection, 1.0f / modelFrameDirection, distance, face, surfaceNormal)) { QMutexLocker locker(&_mutex); float bestDistance = FLT_MAX; @@ -400,6 +400,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g glm::vec3 meshFrameOrigin = glm::vec3(worldToMeshMatrix * glm::vec4(origin, 1.0f)); glm::vec3 meshFrameDirection = glm::vec3(worldToMeshMatrix * glm::vec4(direction, 0.0f)); + glm::vec3 meshFrameInvDirection = 1.0f / meshFrameDirection; int shapeID = 0; int subMeshIndex = 0; @@ -415,8 +416,8 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float partBoundDistance = FLT_MAX; BoxFace partBoundFace; glm::vec3 partBoundNormal; - if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, partBoundDistance, - partBoundFace, partBoundNormal)) { + if (partTriangleSet.getBounds().findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, + partBoundDistance, partBoundFace, partBoundNormal)) { priority = partBoundDistance; } } @@ -444,7 +445,7 @@ bool Model::findRayIntersectionAgainstSubMeshes(const glm::vec3& origin, const g float triangleSetDistance = FLT_MAX; BoxFace triangleSetFace; Triangle triangleSetTriangle; - if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, triangleSetDistance, triangleSetFace, + if (sortedTriangleSet.triangleSet->findRayIntersection(meshFrameOrigin, meshFrameDirection, meshFrameInvDirection, triangleSetDistance, triangleSetFace, triangleSetTriangle, pickAgainstTriangles, allowBackface)) { if (triangleSetDistance < bestDistance) { bestDistance = triangleSetDistance; diff --git a/libraries/render-utils/src/PickItemsJob.cpp b/libraries/render-utils/src/PickItemsJob.cpp index 860262a969..f41222d358 100644 --- a/libraries/render-utils/src/PickItemsJob.cpp +++ b/libraries/render-utils/src/PickItemsJob.cpp @@ -33,6 +33,7 @@ void PickItemsJob::run(const render::RenderContextPointer& renderContext, const render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPointer& renderContext, const render::ItemBounds& inputs, float& minIsectDistance) const { const glm::vec3 rayOrigin = renderContext->args->getViewFrustum().getPosition(); const glm::vec3 rayDirection = renderContext->args->getViewFrustum().getDirection(); + const glm::vec3 rayInvDirection = 1.0f / rayDirection; BoxFace face; glm::vec3 normal; float isectDistance; @@ -42,7 +43,7 @@ render::ItemBound PickItemsJob::findNearestItem(const render::RenderContextPoint render::ItemKey itemKey; for (const auto& itemBound : inputs) { - if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, isectDistance, face, normal)) { + if (!itemBound.bound.contains(rayOrigin) && itemBound.bound.findRayIntersection(rayOrigin, rayDirection, rayInvDirection, isectDistance, face, normal)) { auto& item = renderContext->_scene->getItem(itemBound.id); itemKey = item.getKey(); if (itemKey.isWorldSpace() && isectDistance>minDistance && isectDistance < minIsectDistance && isectDistance= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.x > 0 ? MAX_X_FACE : MIN_X_FACE; - surfaceNormal = glm::vec3(direction.x > 0 ? 1.0f : -1.0f, 0.0f, 0.0f); - return true; - } - if ((findInsideOutIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.y > 0 ? MAX_Y_FACE : MIN_Y_FACE; - surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? 1.0f : -1.0f, 0.0f); - return true; - } - if ((findInsideOutIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) { - distance = axisDistance; - face = direction.z > 0 ? MAX_Z_FACE : MIN_Z_FACE; - surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? 1.0f : -1.0f); - return true; - } - // This case is unexpected, but mimics the previous behavior for inside out intersections - distance = 0; - return true; +// https://tavianator.com/fast-branchless-raybounding-box-intersections/ +bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal) { + float t1, t2, newTmin, newTmax, tmin = -INFINITY, tmax = INFINITY; + int minAxis = -1, maxAxis = -1; + + for (int i = 0; i < 3; ++i) { + t1 = (corner[i] - origin[i]) * invDirection[i]; + t2 = (corner[i] + scale[i] - origin[i]) * invDirection[i]; + + newTmin = glm::min(t1, t2); + newTmax = glm::max(t1, t2); + + minAxis = newTmin > tmin ? i : minAxis; + tmin = glm::max(tmin, newTmin); + maxAxis = newTmax < tmax ? i : maxAxis; + tmax = glm::min(tmax, newTmax); } - // check each axis - float axisDistance; - if ((findIntersection(origin.x, direction.x, corner.x, scale.x, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.x > 0 ? MIN_X_FACE : MAX_X_FACE; - surfaceNormal = glm::vec3(direction.x > 0 ? -1.0f : 1.0f, 0.0f, 0.0f); - return true; - } - if ((findIntersection(origin.y, direction.y, corner.y, scale.y, axisDistance) && axisDistance >= 0 && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x) && - isWithin(origin.z + axisDistance * direction.z, corner.z, scale.z))) { - distance = axisDistance; - face = direction.y > 0 ? MIN_Y_FACE : MAX_Y_FACE; - surfaceNormal = glm::vec3(0.0f, direction.y > 0 ? -1.0f : 1.0f, 0.0f); - return true; - } - if ((findIntersection(origin.z, direction.z, corner.z, scale.z, axisDistance) && axisDistance >= 0 && - isWithin(origin.y + axisDistance * direction.y, corner.y, scale.y) && - isWithin(origin.x + axisDistance * direction.x, corner.x, scale.x))) { - distance = axisDistance; - face = direction.z > 0 ? MIN_Z_FACE : MAX_Z_FACE; - surfaceNormal = glm::vec3(0.0f, 0.0f, direction.z > 0 ? -1.0f : 1.0f); + bool inside = tmin < 0.0f; + if (tmax >= glm::max(tmin, 0.0f)) { + if (inside) { + distance = tmax; + bool positiveDirection = direction[maxAxis] > 0.0f; + surfaceNormal = glm::vec3(0.0f); + surfaceNormal[maxAxis] = positiveDirection ? -1.0f : 1.0f; + face = positiveDirection ? BoxFace(2 * maxAxis + 1) : BoxFace(2 * maxAxis); + } else { + distance = tmin; + bool positiveDirection = direction[minAxis] > 0.0f; + surfaceNormal = glm::vec3(0.0f); + surfaceNormal[minAxis] = positiveDirection ? -1.0f : 1.0f; + face = positiveDirection ? BoxFace(2 * minAxis) : BoxFace(2 * minAxis + 1); + } return true; } return false; diff --git a/libraries/shared/src/GeometryUtil.h b/libraries/shared/src/GeometryUtil.h index 54f9062469..8ec75f71bd 100644 --- a/libraries/shared/src/GeometryUtil.h +++ b/libraries/shared/src/GeometryUtil.h @@ -76,8 +76,8 @@ glm::vec3 addPenetrations(const glm::vec3& currentPenetration, const glm::vec3& bool findIntersection(float origin, float direction, float corner, float size, float& distance); bool findInsideOutIntersection(float origin, float direction, float corner, float size, float& distance); -bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& corner, const glm::vec3& scale, float& distance, - BoxFace& face, glm::vec3& surfaceNormal); +bool findRayAABoxIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + const glm::vec3& corner, const glm::vec3& scale, float& distance, BoxFace& face, glm::vec3& surfaceNormal); bool findRaySphereIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& center, float radius, float& distance); diff --git a/libraries/shared/src/TriangleSet.cpp b/libraries/shared/src/TriangleSet.cpp index 1dc8c5657a..02a8d458a9 100644 --- a/libraries/shared/src/TriangleSet.cpp +++ b/libraries/shared/src/TriangleSet.cpp @@ -176,7 +176,7 @@ void TriangleSet::TriangleTreeCell::insert(size_t triangleIndex) { _triangleIndices.push_back(triangleIndex); } -bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, +bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface) { if (!_isBalanced) { balanceTree(); @@ -184,7 +184,7 @@ bool TriangleSet::findRayIntersection(const glm::vec3& origin, const glm::vec3& float localDistance = distance; int trianglesTouched = 0; - bool hit = _triangleTree.findRayIntersection(origin, direction, localDistance, face, triangle, precision, trianglesTouched, allowBackface); + bool hit = _triangleTree.findRayIntersection(origin, direction, invDirection, localDistance, face, triangle, precision, trianglesTouched, allowBackface); if (hit) { distance = localDistance; } @@ -232,9 +232,9 @@ bool TriangleSet::TriangleTreeCell::findRayIntersectionInternal(const glm::vec3& return intersectedSomething; } -bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, float& distance, - BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, - bool allowBackface) { +bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, + float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, + bool allowBackface) { if (_population < 1) { return false; // no triangles below here, so we can't intersect } @@ -270,7 +270,7 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, float childBoundDistance = FLT_MAX; BoxFace childBoundFace; glm::vec3 childBoundNormal; - if (child->getBounds().findRayIntersection(origin, direction, childBoundDistance, childBoundFace, childBoundNormal)) { + if (child->getBounds().findRayIntersection(origin, direction, invDirection, childBoundDistance, childBoundFace, childBoundNormal)) { // We only need to add this cell if it's closer than the local triangle set intersection (if there was one) if (childBoundDistance < bestLocalDistance) { priority = childBoundDistance; @@ -301,11 +301,11 @@ bool TriangleSet::TriangleTreeCell::findRayIntersection(const glm::vec3& origin, if (!precision && childDistance < EPSILON) { BoxFace childBoundFace; glm::vec3 childBoundNormal; - sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, childDistance, childBoundFace, childBoundNormal); + sortedTriangleCell.second->getBounds().findRayIntersection(origin, direction, invDirection, childDistance, childBoundFace, childBoundNormal); } BoxFace childFace; Triangle childTriangle; - if (sortedTriangleCell.second->findRayIntersection(origin, direction, childDistance, childFace, childTriangle, precision, trianglesTouched)) { + if (sortedTriangleCell.second->findRayIntersection(origin, direction, invDirection, childDistance, childFace, childTriangle, precision, trianglesTouched)) { if (childDistance < bestLocalDistance) { bestLocalDistance = childDistance; bestLocalFace = childFace; diff --git a/libraries/shared/src/TriangleSet.h b/libraries/shared/src/TriangleSet.h index 84e362f0d0..3417b36b4a 100644 --- a/libraries/shared/src/TriangleSet.h +++ b/libraries/shared/src/TriangleSet.h @@ -28,7 +28,7 @@ class TriangleSet { void reset(const AABox& bounds, int depth = 0); void clear(); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, int& trianglesTouched, bool allowBackface = false); bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, @@ -69,7 +69,7 @@ public: void insert(const Triangle& t); - bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, + bool findRayIntersection(const glm::vec3& origin, const glm::vec3& direction, const glm::vec3& invDirection, float& distance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); bool findParabolaIntersection(const glm::vec3& origin, const glm::vec3& velocity, const glm::vec3& acceleration, float& parabolicDistance, BoxFace& face, Triangle& triangle, bool precision, bool allowBackface = false); From 52bbb4d2abac51afe38fe5050a168464625d1ce3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 27 Aug 2018 12:47:17 -0700 Subject: [PATCH 193/207] fix ray-pick results on entities with non-default registration point. clean up grab scripts to correctly use this fix. --- interface/src/raypick/PathPointer.cpp | 4 +-- .../controllerModules/farActionGrabEntity.js | 18 +++-------- .../farActionGrabEntityDynOnly.js | 17 +++------- .../controllerModules/farParentGrabEntity.js | 18 +++-------- .../libraries/controllerDispatcherUtils.js | 32 +++++++++++++++++-- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index 685611d77b..d434c667de 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -105,7 +105,7 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick glm::mat4 entityMat = createMatFromQuatAndPos(props.getRotation(), props.getPosition()); glm::mat4 finalPosAndRotMat = entityMat * _lockEndObject.offsetMat; pos = extractTranslation(finalPosAndRotMat); - rot = glmExtractRotation(finalPosAndRotMat); + rot = props.getRotation(); dim = props.getDimensions(); registrationPoint = props.getRegistrationPoint(); } @@ -350,4 +350,4 @@ glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::ve default: return glm::vec2(NAN); } -} \ No newline at end of file +} diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntity.js b/scripts/system/controllers/controllerModules/farActionGrabEntity.js index e4563fda14..5e798ed680 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntity.js @@ -14,7 +14,8 @@ PICK_MAX_DISTANCE, COLORS_GRAB_SEARCHING_HALF_SQUEEZE, COLORS_GRAB_SEARCHING_FULL_SQUEEZE, COLORS_GRAB_DISTANCE_HOLD, DEFAULT_SEARCH_SPHERE_DISTANCE, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, ensureDynamic, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Reticle, Overlays, isPointingAtUI - Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST + Picks, makeLaserLockInfo Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -593,18 +594,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js index a080e75325..78abcb9b20 100644 --- a/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js +++ b/scripts/system/controllers/controllerModules/farActionGrabEntityDynOnly.js @@ -12,7 +12,7 @@ makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, getEntityParents, Selection, DISPATCHER_HOVERING_LIST, - Uuid + Uuid, worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -572,18 +572,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/controllers/controllerModules/farParentGrabEntity.js b/scripts/system/controllers/controllerModules/farParentGrabEntity.js index 439b5e5f51..a9ec246a32 100644 --- a/scripts/system/controllers/controllerModules/farParentGrabEntity.js +++ b/scripts/system/controllers/controllerModules/farParentGrabEntity.js @@ -11,7 +11,8 @@ Entities, enableDispatcherModule, disableDispatcherModule, entityIsGrabbable, makeDispatcherModuleParameters, MSECS_PER_SEC, HAPTIC_PULSE_STRENGTH, HAPTIC_PULSE_DURATION, TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE, ZERO_VEC, getControllerWorldLocation, projectOntoEntityXYPlane, ContextOverlay, HMD, Picks, makeLaserLockInfo, Xform, makeLaserParams, AddressManager, - getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent + getEntityParents, Selection, DISPATCHER_HOVERING_LIST, unhighlightTargetEntity, Messages, Uuid, findGroupParent, + worldPositionToRegistrationFrameMatrix */ Script.include("/~/system/libraries/controllerDispatcherUtils.js"); @@ -615,18 +616,9 @@ Script.include("/~/system/libraries/Xform.js"); this.calculateOffset = function(controllerData) { if (this.distanceHolding || this.distanceRotating) { - var targetProps = Entities.getEntityProperties(this.targetObject.entityID, [ - "position", - "rotation" - ]); - var zeroVector = { x: 0, y: 0, z:0, w: 0 }; - var intersection = controllerData.rayPicks[this.hand].intersection; - var intersectionMat = new Xform(zeroVector, intersection); - var modelMat = new Xform(targetProps.rotation, targetProps.position); - var modelMatInv = modelMat.inv(); - var xformMat = Xform.mul(modelMatInv, intersectionMat); - var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); - return offsetMat; + var targetProps = Entities.getEntityProperties(this.targetObject.entityID, + [ "position", "rotation", "registrationPoint", "dimensions" ]); + return worldPositionToRegistrationFrameMatrix(targetProps, controllerData.rayPicks[this.hand].intersection); } return undefined; }; diff --git a/scripts/system/libraries/controllerDispatcherUtils.js b/scripts/system/libraries/controllerDispatcherUtils.js index a386dcf5b4..c34fd76802 100644 --- a/scripts/system/libraries/controllerDispatcherUtils.js +++ b/scripts/system/libraries/controllerDispatcherUtils.js @@ -5,7 +5,7 @@ // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html -/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, +/* global module, Camera, HMD, MyAvatar, controllerDispatcherPlugins:true, Quat, Vec3, Overlays, Xform, Mat4, Selection, Uuid, MSECS_PER_SEC:true , LEFT_HAND:true, RIGHT_HAND:true, FORBIDDEN_GRAB_TYPES:true, HAPTIC_PULSE_STRENGTH:true, HAPTIC_PULSE_DURATION:true, ZERO_VEC:true, ONE_VEC:true, @@ -58,7 +58,8 @@ highlightTargetEntity:true, clearHighlightedEntities:true, unhighlightTargetEntity:true, - distanceBetweenEntityLocalPositionAndBoundingBox: true + distanceBetweenEntityLocalPositionAndBoundingBox: true, + worldPositionToRegistrationFrameMatrix: true */ MSECS_PER_SEC = 1000.0; @@ -487,6 +488,30 @@ entityIsFarGrabbedByOther = function(entityID) { return false; }; + +worldPositionToRegistrationFrameMatrix = function(wptrProps, pos) { + // get world matrix for intersection point + var intersectionMat = new Xform({ x: 0, y: 0, z:0, w: 1 }, pos); + + // calculate world matrix for registrationPoint addjusted entity + var DEFAULT_REGISTRATION_POINT = { x: 0.5, y: 0.5, z: 0.5 }; + var regRatio = Vec3.subtract(DEFAULT_REGISTRATION_POINT, wptrProps.registrationPoint); + var regOffset = Vec3.multiplyVbyV(regRatio, wptrProps.dimensions); + var regOffsetRot = Vec3.multiplyQbyV(wptrProps.rotation, regOffset); + var modelMat = new Xform(wptrProps.rotation, Vec3.sum(wptrProps.position, regOffsetRot)); + + // get inverse of model matrix + var modelMatInv = modelMat.inv(); + + // transform world intersection point into object's registrationPoint frame + var xformMat = Xform.mul(modelMatInv, intersectionMat); + + // convert to Mat4 + var offsetMat = Mat4.createFromRotAndTrans(xformMat.rot, xformMat.pos); + return offsetMat; +}; + + if (typeof module !== 'undefined') { module.exports = { makeDispatcherModuleParameters: makeDispatcherModuleParameters, @@ -508,6 +533,7 @@ if (typeof module !== 'undefined') { projectOntoEntityXYPlane: projectOntoEntityXYPlane, TRIGGER_OFF_VALUE: TRIGGER_OFF_VALUE, TRIGGER_ON_VALUE: TRIGGER_ON_VALUE, - DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST + DISPATCHER_HOVERING_LIST: DISPATCHER_HOVERING_LIST, + worldPositionToRegistrationFrameMatrix: worldPositionToRegistrationFrameMatrix }; } From f81fe6b29f3998c4dbb1a941b38b7d93d7f2c7c3 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 16:35:36 -0700 Subject: [PATCH 194/207] add more inputs to controller mapping --- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 + scripts/system/edit.js | 64 ++++++++++++------- 2 files changed, 42 insertions(+), 24 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index ddd51e9b9b..539546dd96 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -304,6 +304,8 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketLeft), "BracketLeft")); + availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketRight), "BracketRight")); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index 776c09addd..e145c1ab3d 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1863,28 +1863,7 @@ var keyReleaseEvent = function (event) { cameraManager.keyReleaseEvent(event); } // since sometimes our menu shortcut keys don't work, trap our menu items here also and fire the appropriate menu items - if (event.text === 'd' && event.isControl) { - selectionManager.clearSelections(); - } else if (event.text === "t") { - selectionDisplay.toggleSpaceMode(); - } else if (event.text === "f") { - if (isActive) { - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } - } else if (event.text === '[') { - if (isActive) { - cameraManager.enable(); - } - } else if (event.text === 'g') { - if (isActive && selectionManager.hasSelection()) { - grid.moveToSelection(); - } - } else if (event.key === KEY_P && event.isControl && !event.isAutoRepeat ) { + if (event.key === KEY_P && event.isControl && !event.isAutoRepeat) { if (event.isShifted) { unparentSelectedEntities(); } else { @@ -1900,9 +1879,46 @@ function deleteKey(value) { deleteSelectedEntities(); } } +function deselectKey(value) { + if (value === 0) { // on release + selectionManager.clearSelections(); + } +} +function toggleKey(value) { + if (value === 0) { // on release + selectionDisplay.toggleSpaceMode(); + } +} +function focusKey(value) { + if (value === 0) { // on release + if (selectionManager.hasSelection()) { + cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, + selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } + } +} +function cameraKey(value) { + if (value === 0) { // on release + cameraManager.enable(); + } +} +function gridKey(value) { + if (value === 0) { // on release + if (selectionManager.hasSelection()) { + grid.moveToSelection(); + } + } +} var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).peek().to(deleteKey); -mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).peek().to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); +mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); +mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); +mapping.from([Controller.Hardware.Keyboard.BracketLeft]).to(cameraKey); +mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); function recursiveAdd(newParentID, parentData) { if (parentData.children !== undefined) { From c19bd8968ad315df6abdfc336ba58fcafe47e041 Mon Sep 17 00:00:00 2001 From: luiscuenca Date: Mon, 27 Aug 2018 16:48:47 -0700 Subject: [PATCH 195/207] Fix mixer crash --- libraries/avatars/src/AvatarHashMap.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 8dfee3551e..c437b56f32 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -239,7 +239,6 @@ AvatarSharedPointer AvatarHashMap::parseAvatarData(QSharedPointer Date: Mon, 27 Aug 2018 17:57:50 -0700 Subject: [PATCH 196/207] added some comment to explain rig to avatar transform --- interface/src/avatar/MyAvatar.cpp | 38 +++++++++++++++++-------------- interface/src/avatar/MyAvatar.h | 4 ++-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index c4d855ee77..23feff88dd 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -453,13 +453,16 @@ void MyAvatar::update(float deltaTime) { // put the spine facing in sensor space. // then mix it with head facing to determine rotation recenter - glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); - glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); - glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); - glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, glm::vec3(handHipAzimuthWorldSpace.x, 0.0f, handHipAzimuthWorldSpace.z)); - glm::vec2 normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { + glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); + glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); + glm::mat4 worldToSensorMat = glm::inverse(sensorToWorldMat); + glm::vec3 handHipAzimuthSensorSpace = transformVectorFast(worldToSensorMat, handHipAzimuthWorldSpace); + glm::vec2 normedHandHipAzimuthSensorSpace(0.0f, 1.0f); + if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { + normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); + } + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { _headControllerFacingMovingAverage = _headControllerFacing; @@ -475,7 +478,6 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - computeHandAzimuth(); #ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE @@ -495,12 +497,8 @@ void MyAvatar::update(float deltaTime) { if (_goToPending) { setWorldPosition(_goToPosition); - setWorldOrientation(_goToOrientation); - if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { - _headControllerFacingMovingAverage = headFacingPlusHandHipAzimuthMix; - } else { - _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average - } + setWorldOrientation(_goToOrientation); + _headControllerFacingMovingAverage = _headControllerFacing; _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). @@ -827,15 +825,17 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { void MyAvatar::computeHandAzimuth() { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); - glm::vec3 localLookat(0.0f, 0.0f, 1.0f); const float HALFWAY = 0.50f; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. vec2 oldAzimuthReading = _hipToHandController; - _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); - + if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { + _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + } else { + _hipToHandController = glm::vec2(0.0f, 1.0f); + } // check the angular distance from forward and back float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); @@ -3376,7 +3376,11 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; - glm::vec3 hipToHandRigSpace = RIG_CHANGE_OF_BASIS * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + // RIG_CHANGE_OF_BASIS * INITIAL_RIG_ROTATION * Quaternions::IDENTITY(explicit rig rotation) * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180(avatar Space); + // INITIAL_RIG_ROTATION = Quaternions::Y_180; + static const glm::quat INITIAL_RIG_ROTATION = Quaternions::Y_180; + glm::quat avatarToRigSpace = INITIAL_RIG_ROTATION * Quaternions::IDENTITY; + glm::vec3 hipToHandRigSpace = avatarToRigSpace * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); glm::vec3 u, v, w; generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f819c417de..8cccb03ea5 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -285,7 +285,7 @@ public: Q_INVOKABLE void centerBody(); // thread-safe - /**jsdoc_hmdSensorMatrix + /**jsdoc * The internal inverse-kinematics system maintains a record of which joints are "locked". Sometimes it is useful to forget this history, to prevent * contorted joints. * @function MyAvatar.clearIKJointLimitHistory @@ -1650,7 +1650,7 @@ private: std::atomic _hasScriptedBlendShapes { false }; std::atomic _rotationRecenterFilterLength { 4.0f }; std::atomic _rotationThreshold { 0.5235f }; // 30 degrees in radians - std::atomic _stepResetRotationEnabled { false }; + std::atomic _stepResetRotationEnabled { true }; std::atomic _drawAverageFacingEnabled { false }; // working copy -- see AvatarData for thread-safe _sensorToWorldMatrixCache, used for outward facing access From 511478a563841ec944121c967bbc219409ea9874 Mon Sep 17 00:00:00 2001 From: David Back Date: Mon, 27 Aug 2018 18:11:19 -0700 Subject: [PATCH 197/207] use not Mac instead of Windows --- scripts/system/edit.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index e145c1ab3d..dd53e8c395 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1912,7 +1912,7 @@ function gridKey(value) { } } var mapping = Controller.newMapping(CONTROLLER_MAPPING_NAME); -mapping.from([Controller.Hardware.Keyboard.Delete]).when([Controller.Hardware.Application.PlatformWindows]).to(deleteKey); +mapping.from([Controller.Hardware.Keyboard.Delete]).when([!Controller.Hardware.Application.PlatformMac]).to(deleteKey); mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware.Application.PlatformMac]).to(deleteKey); mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); From 3c792a04c1dfe8096ab789ed0b146d50d4f31c89 Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 28 Aug 2018 11:25:42 -0700 Subject: [PATCH 198/207] fixed roll rotation added to azimuth in MySkeletonModel.cpp --- interface/src/avatar/MyAvatar.cpp | 35 +-- interface/src/avatar/MyAvatar.h | 2 + interface/src/avatar/MySkeletonModel.cpp | 33 +- scripts/developer/rotateApp.js | 385 ----------------------- 4 files changed, 38 insertions(+), 417 deletions(-) delete mode 100644 scripts/developer/rotateApp.js diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 23feff88dd..32822fc1b9 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -447,12 +447,12 @@ void MyAvatar::reset(bool andRecenter, bool andReload, bool andHead) { void MyAvatar::update(float deltaTime) { // update moving average of HMD facing in xz plane. const float HMD_FACING_TIMESCALE = getRotationRecenterFilterLength(); - const float PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH = 0.0f; // 100 percent shoulders + const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders float tau = deltaTime / HMD_FACING_TIMESCALE; - // put the spine facing in sensor space. - // then mix it with head facing to determine rotation recenter + // put the average hand azimuth into sensor space. + // then mix it with head facing direction to determine rotation recenter if (getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid()) { glm::vec3 handHipAzimuthWorldSpace = transformVectorFast(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); glm::mat4 sensorToWorldMat = getSensorToWorldMatrix(); @@ -462,7 +462,7 @@ void MyAvatar::update(float deltaTime) { if (glm::length(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)) > 0.0f) { normedHandHipAzimuthSensorSpace = glm::normalize(glm::vec2(handHipAzimuthSensorSpace.x, handHipAzimuthSensorSpace.z)); } - glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_SHOULDERS_VS_HEAD_AZIMUTH); + glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { _headControllerFacingMovingAverage = _headControllerFacing; @@ -497,7 +497,7 @@ void MyAvatar::update(float deltaTime) { if (_goToPending) { setWorldPosition(_goToPosition); - setWorldOrientation(_goToOrientation); + setWorldOrientation(_goToOrientation); _headControllerFacingMovingAverage = _headControllerFacing; _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes @@ -834,15 +834,14 @@ void MyAvatar::computeHandAzimuth() { if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); } else { - _hipToHandController = glm::vec2(0.0f, 1.0f); + _hipToHandController = glm::vec2(0.0f, -1.0f); } // check the angular distance from forward and back float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); // if we are now closer to the 180 flip of the previous chest forward - // then we negate our computed _hipToHandController to keep the chest in the same direction. + // then we negate our computed _hipToHandController to keep the chest from flipping. if (cosBackwardAngle > cosForwardAngle) { - // this means we have lost continuity with the current chest position _hipToHandController = -_hipToHandController; } } @@ -3375,12 +3374,10 @@ glm::mat4 MyAvatar::deriveBodyFromHMDSensor() const { glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { - static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; - // RIG_CHANGE_OF_BASIS * INITIAL_RIG_ROTATION * Quaternions::IDENTITY(explicit rig rotation) * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180(avatar Space); - // INITIAL_RIG_ROTATION = Quaternions::Y_180; - static const glm::quat INITIAL_RIG_ROTATION = Quaternions::Y_180; - glm::quat avatarToRigSpace = INITIAL_RIG_ROTATION * Quaternions::IDENTITY; - glm::vec3 hipToHandRigSpace = avatarToRigSpace * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); + // static const glm::quat RIG_CHANGE_OF_BASIS = Quaternions::Y_180; + // RIG_CHANGE_OF_BASIS * AVATAR_TO_RIG_ROTATION * inverse(RIG_CHANGE_OF_BASIS) = Quaternions::Y_180; //avatar Space; + const glm::quat AVATAR_TO_RIG_ROTATION = Quaternions::Y_180; + glm::vec3 hipToHandRigSpace = AVATAR_TO_RIG_ROTATION * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); glm::vec3 u, v, w; generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); @@ -3971,7 +3968,7 @@ bool MyAvatar::FollowHelper::shouldActivateHorizontalCG(MyAvatar& myAvatar) cons glm::vec3 currentHeadPosition = currentHeadPose.getTranslation(); float anatomicalHeadToHipsDistance = glm::length(defaultHeadPosition - defaultHipsPosition); if (!isActive(Horizontal) && - (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance * myAvatar.getAvatarScale())))) { + (glm::length(currentHeadPosition - defaultHipsPosition) > (anatomicalHeadToHipsDistance + (DEFAULT_AVATAR_SPINE_STRETCH_LIMIT * anatomicalHeadToHipsDistance)))) { myAvatar.setResetMode(true); stepDetected = true; } @@ -3995,14 +3992,14 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat qApp->getCamera().getMode() != CAMERA_MODE_MIRROR) { if (!isActive(Rotation) && (shouldActivateRotation(myAvatar, desiredBodyMatrix, currentBodyMatrix) || hasDriveInput)) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } if (myAvatar.getCenterOfGravityModelEnabled()) { if (!isActive(Horizontal) && (shouldActivateHorizontalCG(myAvatar) || hasDriveInput)) { activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } } } else { @@ -4010,7 +4007,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat activate(Horizontal); if (myAvatar.getEnableStepResetRotation()) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); } } } @@ -4020,7 +4017,7 @@ void MyAvatar::FollowHelper::prePhysicsUpdate(MyAvatar& myAvatar, const glm::mat } else { if (!isActive(Rotation) && getForceActivateRotation()) { activate(Rotation); - myAvatar.setHeadControllerFacingMovingAverage(myAvatar._headControllerFacing); + myAvatar.setHeadControllerFacingMovingAverage(myAvatar.getHeadControllerFacing()); setForceActivateRotation(false); } if (!isActive(Horizontal) && getForceActivateHorizontal()) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8cccb03ea5..8b5d9d8c55 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -914,6 +914,8 @@ public: virtual void rebuildCollisionShape() override; + const glm::vec2& getHipToHandController() const { return _hipToHandController; } + void setHipToHandController(glm::vec2 currentHipToHandController) { _hipToHandController = currentHipToHandController; } const glm::vec2& getHeadControllerFacing() const { return _headControllerFacing; } void setHeadControllerFacing(glm::vec2 currentHeadControllerFacing) { _headControllerFacing = currentHeadControllerFacing; } const glm::vec2& getHeadControllerFacingMovingAverage() const { return _headControllerFacingMovingAverage; } diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index f150379f0c..e06a06cf0e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -239,21 +239,28 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - AnimPose currentPose; - bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"),currentPose); - AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); - glm::vec3 u, v, w; - glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); - glm::vec3 up = currentPose.rot() * glm::vec3(0.0f, 1.0f, 0.0f); - generateBasisVectors(up, fwd, u, v, w); - AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); - if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) { - AnimPose newSpinePose(myAvatar->getSpine2RotationRigSpace()); - currentPose.rot() = newSpinePose.rot(); + AnimPose currentSpine2Pose; + AnimPose currentHeadPose; + AnimPose currentHipsPose; + bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); + bool ret1 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); + bool ret2 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); + glm::vec3 u, v, w; + glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); + if (glm::length(up) > 0.0f) { + up = glm::normalize(up); + } else { + up = glm::vec3(0.0f, 1.0f, 0.0f); + } + generateBasisVectors(up, fwd, u, v, w); + AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); + currentSpine2Pose.rot() = newSpinePose.rot(); + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; + params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } - params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentPose; - params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } else { _prevHipsValid = false; diff --git a/scripts/developer/rotateApp.js b/scripts/developer/rotateApp.js deleted file mode 100644 index 030385b6fb..0000000000 --- a/scripts/developer/rotateApp.js +++ /dev/null @@ -1,385 +0,0 @@ -/* global Script, Vec3, MyAvatar, Tablet, Messages, Quat, -DebugDraw, Mat4, Entities, Xform, Controller, Camera, console, document*/ - -Script.registerValue("ROTATEAPP", true); - -var TABLET_BUTTON_NAME = "ROTATE"; -var CHANGE_OF_BASIS_ROTATION = { x: 0, y: 1, z: 0, w: 0 }; -var DEFAULT_HEAD_TURN_THRESHOLD = 0.5333; -var DEFAULT_HEAD_TURN_FILTER_LENGTH = 4.0; -var LOADING_DELAY = 2000; -var AVERAGING_RATE = 0.03; -var INCREASING = 1.0; -var DECREASING = -1.0; -var DEGREES_PER_PI_RADIANS = 180.0; -var FILTER_FUDGE_RANGE = 0.9; - -var activated = false; -var documentLoaded = false; -var sciptLoaded = false; -var headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; -var hipToLeftHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; -var hipToRightHandAverage = 0.0; // { x: 0, y: 0, z: 0, w: 1 }; -var averageAzimuth = 0.0; -var hipsPositionRigSpace = { x: 0, y: 0, z: 0 }; -var spine2PositionRigSpace = { x: 0, y: 0, z: 0 }; -var hipsRotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; -var spine2RotationRigSpace = { x: 0, y: 0, z: 0, w: 1 }; -var spine2Rotation = { x: 0, y: 0, z: 0, w: 1 }; -var hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; -var hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; - -/* -var ikTypes = { - RotationAndPosition: 0, - RotationOnly: 1, - HmdHead: 2, - HipsRelativeRotationAndPosition: 3, - Spline: 4, - Unknown: 5 -}; - - -var ANIM_VARS = [ - //"headType", - "spine2Type", - //"hipsType", - "spine2Position", - "spine2Rotation" - //"hipsPosition", - //"hipsRotation" -]; - -var handlerId = MyAvatar.addAnimationStateHandler(function (props) { - //print("in callback"); - //print("props spine2 pos: " + props.spine2Position.x + " " + props.spine2Position.y + " " + props.spine2Position.z); - //print("props hip pos: " + props.hipsPosition.x + " " + props.hipsPosition.y + " " + props.hipsPosition.z); - var result = {}; - //{x:0,y:0,z:0} - //result.headType = ikTypes.HmdHead; - //result.hipsType = ikTypes.RotationAndPosition; - //result.hipsPosition = hipsPositionRigSpace; // { x: 0, y: 0, z: 0 }; - //result.hipsRotation = hipsRotationRigSpace;//{ x: 0, y: 0, z: 0, w: 1 }; // - result.spine2Type = ikTypes.Spline; - result.spine2Position = spine2PositionRigSpace; // { x: 0, y: 1.3, z: 0 }; - result.spine2Rotation = spine2Rotation; - - return result; -}, ANIM_VARS); -*/ -// define state readings constructor -function StateReading(headPose, rhandPose, lhandPose, diffFromAverageEulers) { - this.headPose = headPose; - this.rhandPose = rhandPose; - this.lhandPose = lhandPose; - this.diffFromAverageEulers = diffFromAverageEulers; -} - -// define current state readings object for holding tracker readings and current differences from averages -var currentStateReadings = new StateReading(Controller.getPoseValue(Controller.Standard.Head), - Controller.getPoseValue(Controller.Standard.RightHand), - Controller.getPoseValue(Controller.Standard.LeftHand), - { x: 0, y: 0, z: 0 }); - -// declare the checkbox constructor -function AppCheckbox(type,id,eventType,isChecked) { - this.type = type; - this.id = id; - this.eventType = eventType; - this.data = {value: isChecked}; -} - -var usingStepResetRotationDirection = new AppCheckbox("checkboxtick", "stepReset", "onStepResetCheckBox", false); -var usingDrawAverageFacing = new AppCheckbox("checkboxtick", "drawAverage", "onDrawAverageFacingCheckBox", false); -var checkBoxArray = new Array(usingStepResetRotationDirection, usingDrawAverageFacing); - -// declare the html slider constructor -function AppProperty(name, type, eventType, signalType, setFunction, initValue, convertToThreshold, convertToSlider, signalOn) { - this.name = name; - this.type = type; - this.eventType = eventType; - this.signalType = signalType; - this.setValue = setFunction; - this.value = initValue; - this.get = function () { - return this.value; - }; - this.convertToThreshold = convertToThreshold; - this.convertToSlider = convertToSlider; -} - -// var HTML_URL = Script.resolvePath("file:///c:/dev/hifi_fork/hifi/scripts/developer/rotateRecenterApp.html"); -var HTML_URL = Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/rotateRecenterApp.html"); -var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); - -// define the sliders -var filterLengthProperty = new AppProperty("#filterLength-slider", "slider", "onFilterLengthSlider", "filterSignal", - setFilterLength, DEFAULT_HEAD_TURN_FILTER_LENGTH, function (num) { - var base = 5; - var shift = 0; - return convertExponential(base, num, INCREASING, shift); - }, function (num) { - var base = 5; - var shift = 0; - return convertLog(base, num, INCREASING, shift); - }, true); -var angleThresholdProperty = new AppProperty("#angleThreshold-slider", "slider", "onAngleThresholdSlider", "angleSignal", - setAngleThreshold, DEFAULT_HEAD_TURN_THRESHOLD, function (num) { - return convertToRadians(num); - }, function (num) { - return convertToDegrees(num); - }, true); - -var propArray = new Array(filterLengthProperty, angleThresholdProperty); - -function setFilterLength(num) { - filterLengthProperty.value = num; - MyAvatar.rotationRecenterFilterLength = filterLengthProperty.value; - -} - -function setAngleThreshold(num) { - angleThresholdProperty.value = num; - MyAvatar.rotationThreshold = angleThresholdProperty.value; -} - -function convertToRadians(num) { - return (num / DEGREES_PER_PI_RADIANS) * Math.PI; -} - -function convertToDegrees(num) { - return (num / Math.PI) * DEGREES_PER_PI_RADIANS; -} - -function getLog(x, y) { - return Math.log(y) / Math.log(x); -} - -function convertLog(base, num, direction, shift) { - return direction * getLog(base, (num + FILTER_FUDGE_RANGE)) + shift; -} - -function convertExponential(base, num, direction, shift) { - return Math.pow(base, (direction * num + shift)) - FILTER_FUDGE_RANGE; -} - -function manageClick() { - if (activated) { - tablet.gotoHomeScreen(); - } else { - tablet.gotoWebScreen(HTML_URL); - } -} - -var tabletButton = tablet.addButton({ - text: TABLET_BUTTON_NAME, - icon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg"), - activeIcon: Script.resolvePath("http://hifi-content.s3.amazonaws.com/angus/stepApp/foot.svg") -}); - -function onKeyPress(event) { - if (event.text === "'") { - // when the sensors are reset, then reset the mode. - } -} - -function onWebEventReceived(msg) { - var message = JSON.parse(msg); - print(" we have a message from html dialog " + message.type); - propArray.forEach(function (prop) { - if (prop.eventType === message.type) { - prop.setValue(prop.convertToThreshold(message.data.value)); - print("message from " + prop.name); - // break; - } - }); - checkBoxArray.forEach(function(cbox) { - if (cbox.eventType === message.type) { - cbox.data.value = message.data.value; - if (cbox.id === "stepReset") { - MyAvatar.enableStepResetRotation = cbox.data.value; - } - if (cbox.id === "drawAverage") { - MyAvatar.enableDrawAverageFacing = cbox.data.value; - } - // break; - } - - }); - if (message.type === "onCreateRotateApp") { - print("document loaded"); - documentLoaded = true; - Script.setTimeout(initAppForm, LOADING_DELAY); - } -} - -function initAppForm() { - print("step app is loaded: " + documentLoaded); - if (documentLoaded === true) { - propArray.forEach(function (prop) { - print(prop.name); - tablet.emitScriptEvent(JSON.stringify({ - "type": "slider", - "id": prop.name, - "data": { "value": prop.convertToSlider(prop.value) } - })); - }); - checkBoxArray.forEach(function (cbox) { - tablet.emitScriptEvent(JSON.stringify({ - "type": "checkboxtick", - "id": cbox.id, - "data": { value: cbox.data.value } - })); - }); - } - -} - - -function onScreenChanged(type, url) { - print("Screen changed"); - if (type === "Web" && url === HTML_URL) { - if (!activated) { - // hook up to event bridge - tablet.webEventReceived.connect(onWebEventReceived); - print("after connect web event"); - MyAvatar.hmdLeanRecenterEnabled = true; - } - activated = true; - } else { - if (activated) { - // disconnect from event bridge - tablet.webEventReceived.disconnect(onWebEventReceived); - documentLoaded = false; - } - activated = false; - } -} - -function limitAngle(angle) { - return (angle + 180) % 360 - 180; -} - -function computeHandAzimuths(timeElapsed) { - - // var leftHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.lhandPose.translation); - // var rightHandPositionRigSpace = Vec3.multiplyQbyV(Quat.inverse(CHANGE_OF_BASIS_ROTATION), currentStateReadings.rhandPose.translation); - - - // var hipToLeftHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: leftHandPositionRigSpace.x, y: 0, z: leftHandPositionRigSpace.z }); - // var hipToRightHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, { x: rightHandPositionRigSpace.x, y: 0, z: rightHandPositionRigSpace.z }); - // var hipToHandHalfway = Quat.slerp(hipToLeftHand, hipToRightHand, 0.5); - - var leftHand = currentStateReadings.lhandPose.translation; - var rightHand = currentStateReadings.rhandPose.translation; - var head = currentStateReadings.headPose.translation; - var lHandMinusHead = Vec3.subtract(leftHand, head); - lHandMinusHead.y = 0.0; - var rHandMinusHead = Vec3.subtract(rightHand, head); - rHandMinusHead.y = 0.0; - //print(JSON.stringify(leftHand)); - //print(JSON.stringify(head)); - var avatarZAxis = { x: 0.0, y: 0.0, z: 1.0 }; - var hipToLHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, lHandMinusHead); - var hipToRHand = Quat.lookAtSimple({ x: 0, y: 0, z: 0 }, rHandMinusHead); - var tau = timeElapsed / filterLengthProperty.value; - hipToLHandAverage = Quat.slerp(hipToLHandAverage, hipToLHand, tau); - hipToRHandAverage = Quat.slerp(hipToRHandAverage, hipToRHand, tau); - - // var angleToLeft = limitAngle(Quat.safeEulerAngles(hipToLHandAverage).y); - // var angleToRight = limitAngle(Quat.safeEulerAngles(hipToRHandAverage).y); - var leftRightMidpoint = (Quat.safeEulerAngles(hipToLHand).y + Quat.safeEulerAngles(hipToRHand).y) / 2.0; - var leftRightMidpointAverage = (Quat.safeEulerAngles(hipToLHandAverage).y + Quat.safeEulerAngles(hipToRHandAverage).y) / 2.0; - - // limit the angle because we are flipped by 180, fix this tomorrow. - // print(leftRightMidpointAverage/180.0); - // print("threshold value " + angleThresholdProperty.value); - - - var raySpineRotation = Quat.fromVec3Degrees({ x: 0, y: leftRightMidpointAverage, z: 0 }); - var zAxisSpineRotation = Vec3.multiplyQbyV(raySpineRotation, { x: 0, y: 0, z: -1 }); - var zAxisWorldSpace = Vec3.multiplyQbyV(MyAvatar.orientation, zAxisSpineRotation); - // DebugDraw.drawRay(MyAvatar.position, Vec3.sum(MyAvatar.position, zAxisWorldSpace), { x: 1, y: 0, z: 0, w: 1 }); - - //print(leftRightMidpoint); - - var headPoseRigSpace = Quat.multiply(CHANGE_OF_BASIS_ROTATION, currentStateReadings.head.rotation); - headPoseAverageOrientation = Quat.slerp(headPoseAverageOrientation, headPoseRigSpace, tau); - var headPoseAverageEulers = Quat.safeEulerAngles(headPoseAverageOrientation); - - // get it into radians too!! - // average head and hands 50/50 - // print("hands azimuth " + leftRightMidpointAverage + " head azimuth " + headPoseAverageEulers.y); - var headPlusHands = (leftRightMidpointAverage + headPoseAverageEulers.y) / 2.0; - if ((Math.abs(headPlusHands / 180.0) * Math.PI) > angleThresholdProperty.value) { - //print("recenter the feet under the head"); - // MyAvatar.triggerRotationRecenter(); - hipToLHandAverage = { x: 0, y: 0, z: 0, w: 1 }; - hipToRHandAverage = { x: 0, y: 0, z: 0, w: 1 }; - headPoseAverageOrientation = { x: 0, y: 0, z: 0, w: 1 }; - } - - // put a hard max on this easing function. - var rotateAngle = ((Math.cos((leftRightMidpoint / 180.0) * Math.PI) + 2.0)/3.0) * leftRightMidpoint; - //print("rotate angle " + rotateAngle); - - return Quat.fromVec3Degrees({ x: 0, y: rotateAngle, z: 0 }); - - -} - -function update(dt) { - //update state readings - currentStateReadings.head = Controller.getPoseValue(Controller.Standard.Head); - currentStateReadings.rhandPose = Controller.getPoseValue(Controller.Standard.RightHand); - currentStateReadings.lhandPose = Controller.getPoseValue(Controller.Standard.LeftHand); - - //print(JSON.stringify(currentStateReadings.head)); - - - - var latestSpineRotation = computeHandAzimuths(dt); - - spine2Rotation = latestSpineRotation; - var spine2Pos = MyAvatar.getAbsoluteJointTranslationInObjectFrame(MyAvatar.getJointIndex("Spine2")); - spine2PositionRigSpace = Vec3.multiplyQbyV(CHANGE_OF_BASIS_ROTATION, spine2Pos); - /* - if (HMD.active && !scriptLoaded) { - //Script.load("rotateApp.js"); - scriptLoaded = true; - } - - if (!HMD.active) { - scriptLoaded = false; - } - */ - - // handle the azimuth of the arms - -} - -function shutdownTabletApp() { - tablet.removeButton(tabletButton); - if (activated) { - tablet.webEventReceived.disconnect(onWebEventReceived); - tablet.gotoHomeScreen(); - } - tablet.screenChanged.disconnect(onScreenChanged); -} - -Script.setTimeout(function () { - tabletButton.clicked.connect(manageClick); - tablet.screenChanged.connect(onScreenChanged); - Script.update.connect(update); - Controller.keyPressEvent.connect(onKeyPress); -}, (LOADING_DELAY)); - -Script.scriptEnding.connect(function () { - // if (handlerId) { - // print("removing animation state handler"); - // handlerId = MyAvatar.removeAnimationStateHandler(handlerId); - // } - MyAvatar.hmdLeanRecenterEnabled = true; - Script.update.disconnect(update); - shutdownTabletApp(); -}); \ No newline at end of file From ee33f5dc97610c96a8e29b89049bc9adc608311e Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Wed, 29 Aug 2018 01:57:49 +0100 Subject: [PATCH 199/207] normalize the hip hand vector in getSpine2RotationRigSpace, made computeHandAzimuth a const function that returns a vec2 --- interface/src/avatar/MyAvatar.cpp | 36 +++++++++++++----------- interface/src/avatar/MyAvatar.h | 2 +- interface/src/avatar/MySkeletonModel.cpp | 34 +++++++++++----------- 3 files changed, 38 insertions(+), 34 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 32822fc1b9..75becd2c6d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -91,8 +91,6 @@ const float MIN_SCALE_CHANGED_DELTA = 0.001f; const int MODE_READINGS_RING_BUFFER_SIZE = 500; const float CENTIMETERS_PER_METER = 100.0f; -#define DEBUG_DRAW_HMD_MOVING_AVERAGE - MyAvatar::MyAvatar(QThread* thread) : Avatar(thread), _yawSpeed(YAW_SPEED_DEFAULT), @@ -450,6 +448,7 @@ void MyAvatar::update(float deltaTime) { const float PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH = 0.0f; // 100 percent shoulders float tau = deltaTime / HMD_FACING_TIMESCALE; + setHipToHandController(computeHandAzimuth()); // put the average hand azimuth into sensor space. // then mix it with head facing direction to determine rotation recenter @@ -465,7 +464,7 @@ void MyAvatar::update(float deltaTime) { glm::vec2 headFacingPlusHandHipAzimuthMix = lerp(normedHandHipAzimuthSensorSpace, _headControllerFacing, PERCENTAGE_WEIGHT_HEAD_VS_SHOULDERS_AZIMUTH); _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, headFacingPlusHandHipAzimuthMix, tau); } else { - _headControllerFacingMovingAverage = _headControllerFacing; + _headControllerFacingMovingAverage = lerp(_headControllerFacingMovingAverage, _headControllerFacing, tau); } if (_smoothOrientationTimer < SMOOTH_TIME_ORIENTATION) { @@ -478,10 +477,8 @@ void MyAvatar::update(float deltaTime) { _recentModeReadings.insert(newHeightReadingInCentimeters); setCurrentStandingHeight(computeStandingHeightMode(getControllerPoseInAvatarFrame(controller::Action::HEAD))); setAverageHeadRotation(computeAverageHeadRotation(getControllerPoseInAvatarFrame(controller::Action::HEAD))); - computeHandAzimuth(); -#ifdef DEBUG_DRAW_HMD_MOVING_AVERAGE - if (_drawAverageFacingEnabled) { + if (_drawAverageFacingEnabled) { auto sensorHeadPose = getControllerPoseInSensorFrame(controller::Action::HEAD); glm::vec3 worldHeadPos = transformPoint(getSensorToWorldMatrix(), sensorHeadPose.getTranslation()); glm::vec3 worldFacingAverage = transformVectorFast(getSensorToWorldMatrix(), glm::vec3(_headControllerFacingMovingAverage.x, 0.0f, _headControllerFacingMovingAverage.y)); @@ -493,12 +490,11 @@ void MyAvatar::update(float deltaTime) { glm::vec3 handAzimuthMidpoint = transformPoint(getTransform().getMatrix(), glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y)); DebugDraw::getInstance().drawRay(getWorldPosition(), handAzimuthMidpoint, glm::vec4(0.0f, 1.0f, 1.0f, 1.0f)); } -#endif if (_goToPending) { setWorldPosition(_goToPosition); setWorldOrientation(_goToOrientation); - _headControllerFacingMovingAverage = _headControllerFacing; + _headControllerFacingMovingAverage = _headControllerFacing; // reset moving average _goToPending = false; // updateFromHMDSensorMatrix (called from paintGL) expects that the sensorToWorldMatrix is updated for any position changes // that happen between render and Application::update (which calls updateSensorToWorldMatrix to do so). @@ -822,29 +818,30 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // Find the vector halfway between the hip to hand azimuth vectors // This midpoint hand azimuth is in Avatar space -void MyAvatar::computeHandAzimuth() { +glm::vec2 MyAvatar::computeHandAzimuth() const { auto leftHandPoseAvatarSpace = getLeftHandPose(); auto rightHandPoseAvatarSpace = getRightHandPose(); const float HALFWAY = 0.50f; + glm::vec2 latestHipToHandController = _hipToHandController; if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. - vec2 oldAzimuthReading = _hipToHandController; + glm::vec2 oldAzimuthReading = _hipToHandController; if ((glm::length(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)) > 0.0f) && (glm::length(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)) > 0.0f)) { - _hipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); + latestHipToHandController = lerp(glm::normalize(glm::vec2(rightHandPoseAvatarSpace.translation.x, rightHandPoseAvatarSpace.translation.z)), glm::normalize(glm::vec2(leftHandPoseAvatarSpace.translation.x, leftHandPoseAvatarSpace.translation.z)), HALFWAY); } else { - _hipToHandController = glm::vec2(0.0f, -1.0f); + latestHipToHandController = glm::vec2(0.0f, -1.0f); } // check the angular distance from forward and back - float cosForwardAngle = glm::dot(_hipToHandController, oldAzimuthReading); - float cosBackwardAngle = glm::dot(_hipToHandController, -oldAzimuthReading); + float cosForwardAngle = glm::dot(latestHipToHandController, oldAzimuthReading); // if we are now closer to the 180 flip of the previous chest forward - // then we negate our computed _hipToHandController to keep the chest from flipping. - if (cosBackwardAngle > cosForwardAngle) { - _hipToHandController = -_hipToHandController; + // then we negate our computed latestHipToHandController to keep the chest from flipping. + if (cosForwardAngle < 0.0f) { + latestHipToHandController = -latestHipToHandController; } } + return latestHipToHandController; } void MyAvatar::updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache) { @@ -3380,6 +3377,11 @@ glm::mat4 MyAvatar::getSpine2RotationRigSpace() const { glm::vec3 hipToHandRigSpace = AVATAR_TO_RIG_ROTATION * glm::vec3(_hipToHandController.x, 0.0f, _hipToHandController.y); glm::vec3 u, v, w; + if (glm::length(hipToHandRigSpace) > 0.0f) { + hipToHandRigSpace = glm::normalize(hipToHandRigSpace); + } else { + hipToHandRigSpace = glm::vec3(0.0f, 0.0f, 1.0f); + } generateBasisVectors(glm::vec3(0.0f,1.0f,0.0f), hipToHandRigSpace, u, v, w); glm::mat4 spine2RigSpace(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f)); return spine2RigSpace; diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8b5d9d8c55..f82c5b4018 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -321,7 +321,7 @@ public: void updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix); // compute the hip to hand average azimuth. - void computeHandAzimuth(); + glm::vec2 computeHandAzimuth() const; // read the location of a hand controller and save the transform void updateJointFromController(controller::Action poseKey, ThreadSafeValueCache& matrixCache); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index e06a06cf0e..8fd7d31956 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -243,23 +243,25 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { AnimPose currentSpine2Pose; AnimPose currentHeadPose; AnimPose currentHipsPose; - bool ret = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); - bool ret1 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); - bool ret2 = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); - AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); - glm::vec3 u, v, w; - glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); - glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); - if (glm::length(up) > 0.0f) { - up = glm::normalize(up); - } else { - up = glm::vec3(0.0f, 1.0f, 0.0f); + bool spine2Exists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Spine2"), currentSpine2Pose); + bool headExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Head"), currentHeadPose); + bool hipsExists = _rig.getAbsoluteJointPoseInRigFrame(_rig.indexOfJoint("Hips"), currentHipsPose); + if (spine2Exists && headExists && hipsExists) { + AnimPose rigSpaceYaw(myAvatar->getSpine2RotationRigSpace()); + glm::vec3 u, v, w; + glm::vec3 fwd = rigSpaceYaw.rot() * glm::vec3(0.0f, 0.0f, 1.0f); + glm::vec3 up = currentHeadPose.trans() - currentHipsPose.trans(); + if (glm::length(up) > 0.0f) { + up = glm::normalize(up); + } else { + up = glm::vec3(0.0f, 1.0f, 0.0f); + } + generateBasisVectors(up, fwd, u, v, w); + AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); + currentSpine2Pose.rot() = newSpinePose.rot(); + params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; + params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } - generateBasisVectors(up, fwd, u, v, w); - AnimPose newSpinePose(glm::mat4(glm::vec4(w, 0.0f), glm::vec4(u, 0.0f), glm::vec4(v, 0.0f), glm::vec4(glm::vec3(0.0f, 0.0f, 0.0f), 1.0f))); - currentSpine2Pose.rot() = newSpinePose.rot(); - params.primaryControllerPoses[Rig::PrimaryControllerType_Spine2] = currentSpine2Pose; - params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; } } else { From dcf2074d7eb85bb35b60f89dbf97737f92b798cd Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 29 Aug 2018 11:56:37 -0700 Subject: [PATCH 200/207] remove [ camera key, allow F to enter camera without selection --- .../src/input-plugins/KeyboardMouseDevice.cpp | 2 -- scripts/system/edit.js | 13 ++----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp index 539546dd96..ddd51e9b9b 100755 --- a/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp +++ b/libraries/input-plugins/src/input-plugins/KeyboardMouseDevice.cpp @@ -304,8 +304,6 @@ controller::Input::NamedVector KeyboardMouseDevice::InputDevice::getAvailableInp availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Control), "Control")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Delete), "Delete")); availableInputs.append(Input::NamedPair(makeInput(Qt::Key_Backspace), QKeySequence(Qt::Key_Backspace).toString())); - availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketLeft), "BracketLeft")); - availableInputs.append(Input::NamedPair(makeInput(Qt::Key_BracketRight), "BracketRight")); availableInputs.append(Input::NamedPair(makeInput(Qt::LeftButton), "LeftMouseButton")); availableInputs.append(Input::NamedPair(makeInput(Qt::MiddleButton), "MiddleMouseButton")); diff --git a/scripts/system/edit.js b/scripts/system/edit.js index dd53e8c395..b086b5624c 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1890,18 +1890,10 @@ function toggleKey(value) { } } function focusKey(value) { - if (value === 0) { // on release - if (selectionManager.hasSelection()) { - cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, - selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); - } - } -} -function cameraKey(value) { if (value === 0) { // on release cameraManager.enable(); + cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); } } function gridKey(value) { @@ -1917,7 +1909,6 @@ mapping.from([Controller.Hardware.Keyboard.Backspace]).when([Controller.Hardware mapping.from([Controller.Hardware.Keyboard.D]).when([Controller.Hardware.Keyboard.Control]).to(deselectKey); mapping.from([Controller.Hardware.Keyboard.T]).to(toggleKey); mapping.from([Controller.Hardware.Keyboard.F]).to(focusKey); -mapping.from([Controller.Hardware.Keyboard.BracketLeft]).to(cameraKey); mapping.from([Controller.Hardware.Keyboard.G]).to(gridKey); function recursiveAdd(newParentID, parentData) { From 0e53fa3f8b8a2edb850652e8b461b967a7669fd2 Mon Sep 17 00:00:00 2001 From: David Back Date: Wed, 29 Aug 2018 12:05:46 -0700 Subject: [PATCH 201/207] only focus with selection --- scripts/system/edit.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/system/edit.js b/scripts/system/edit.js index b086b5624c..e340c75a8b 100644 --- a/scripts/system/edit.js +++ b/scripts/system/edit.js @@ -1892,8 +1892,10 @@ function toggleKey(value) { function focusKey(value) { if (value === 0) { // on release cameraManager.enable(); - cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, - Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + if (selectionManager.hasSelection()) { + cameraManager.focus(selectionManager.worldPosition, selectionManager.worldDimensions, + Menu.isOptionChecked(MENU_EASE_ON_FOCUS)); + } } } function gridKey(value) { From f7d0cdda025964ae4d8680d2fdbdc0870c97e4bc Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Wed, 29 Aug 2018 20:44:21 +0100 Subject: [PATCH 202/207] added a check for whether the head to shoulder azimuth is greater than 100 degrees. If so unflip the chest azimuth --- interface/src/avatar/MyAvatar.cpp | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 75becd2c6d..78affe2052 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -819,12 +819,13 @@ void MyAvatar::updateFromHMDSensorMatrix(const glm::mat4& hmdSensorMatrix) { // Find the vector halfway between the hip to hand azimuth vectors // This midpoint hand azimuth is in Avatar space glm::vec2 MyAvatar::computeHandAzimuth() const { - auto leftHandPoseAvatarSpace = getLeftHandPose(); - auto rightHandPoseAvatarSpace = getRightHandPose(); + controller::Pose leftHandPoseAvatarSpace = getLeftHandPose(); + controller::Pose rightHandPoseAvatarSpace = getRightHandPose(); + controller::Pose headPoseAvatarSpace = getControllerPoseInAvatarFrame(controller::Action::HEAD); const float HALFWAY = 0.50f; glm::vec2 latestHipToHandController = _hipToHandController; - if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid()) { + if (leftHandPoseAvatarSpace.isValid() && rightHandPoseAvatarSpace.isValid() && headPoseAvatarSpace.isValid()) { // we need the old azimuth reading to prevent flipping the facing direction 180 // in the case where the hands go from being slightly less than 180 apart to slightly more than 180 apart. glm::vec2 oldAzimuthReading = _hipToHandController; @@ -833,11 +834,23 @@ glm::vec2 MyAvatar::computeHandAzimuth() const { } else { latestHipToHandController = glm::vec2(0.0f, -1.0f); } + + glm::vec3 headLookAtAvatarSpace = transformVectorFast(headPoseAvatarSpace.getMatrix(), glm::vec3(0.0f, 0.0f, 1.0f)); + glm::vec2 headAzimuthAvatarSpace = glm::vec2(headLookAtAvatarSpace.x, headLookAtAvatarSpace.z); + if (glm::length(headAzimuthAvatarSpace) > 0.0f) { + headAzimuthAvatarSpace = glm::normalize(headAzimuthAvatarSpace); + } else { + headAzimuthAvatarSpace = -latestHipToHandController; + } + // check the angular distance from forward and back float cosForwardAngle = glm::dot(latestHipToHandController, oldAzimuthReading); + float cosHeadShoulder = glm::dot(-latestHipToHandController, headAzimuthAvatarSpace); // if we are now closer to the 180 flip of the previous chest forward // then we negate our computed latestHipToHandController to keep the chest from flipping. - if (cosForwardAngle < 0.0f) { + // also check the head to shoulder azimuth difference if we negate. + // don't negate the chest azimuth if this is greater than 100 degrees. + if ((cosForwardAngle < 0.0f) && !(cosHeadShoulder < -0.2f)) { latestHipToHandController = -latestHipToHandController; } } From cda8b82ead7a74b411e4219905f5ca29e8a0f8e3 Mon Sep 17 00:00:00 2001 From: SamGondelman Date: Wed, 29 Aug 2018 13:32:53 -0700 Subject: [PATCH 203/207] improve pointer performance --- interface/src/raypick/PathPointer.cpp | 77 ++++++++++++++----------- interface/src/raypick/PathPointer.h | 4 ++ interface/src/raypick/StylusPointer.cpp | 6 +- interface/src/raypick/StylusPointer.h | 2 + 4 files changed, 54 insertions(+), 35 deletions(-) diff --git a/interface/src/raypick/PathPointer.cpp b/interface/src/raypick/PathPointer.cpp index d434c667de..0301136bfa 100644 --- a/interface/src/raypick/PathPointer.cpp +++ b/interface/src/raypick/PathPointer.cpp @@ -54,11 +54,13 @@ PathPointer::~PathPointer() { void PathPointer::setRenderState(const std::string& state) { withWriteLock([&] { if (!_currentRenderState.empty() && state != _currentRenderState) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + auto renderState = _renderStates.find(_currentRenderState); + if (renderState != _renderStates.end()) { + renderState->second->disable(); } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + auto defaultRenderState = _defaultRenderStates.find(_currentRenderState); + if (defaultRenderState != _defaultRenderStates.end()) { + defaultRenderState->second.second->disable(); } } _currentRenderState = state; @@ -142,52 +144,57 @@ PickResultPointer PathPointer::getVisualPickResult(const PickResultPointer& pick void PathPointer::updateVisuals(const PickResultPointer& pickResult) { IntersectionType type = getPickedObjectType(pickResult); - if (_enabled && !_currentRenderState.empty() && _renderStates.find(_currentRenderState) != _renderStates.end() && + auto renderState = _renderStates.find(_currentRenderState); + auto defaultRenderState = _defaultRenderStates.find(_currentRenderState); + if (_enabled && !_currentRenderState.empty() && renderState != _renderStates.end() && (type != IntersectionType::NONE || _pathLength > 0.0f)) { glm::vec3 origin = getPickOrigin(pickResult); glm::vec3 end = getPickEnd(pickResult, _pathLength); glm::vec3 surfaceNormal = getPickedObjectNormal(pickResult); - _renderStates[_currentRenderState]->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar, - _followNormal, _followNormalStrength, _pathLength, pickResult); - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + renderState->second->update(origin, end, surfaceNormal, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, _faceAvatar, + _followNormal, _followNormalStrength, _pathLength, pickResult); + if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) { + defaultRenderState->second.second->disable(); } - } else if (_enabled && !_currentRenderState.empty() && _defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + } else if (_enabled && !_currentRenderState.empty() && defaultRenderState != _defaultRenderStates.end()) { + if (renderState != _renderStates.end() && renderState->second->isEnabled()) { + renderState->second->disable(); } glm::vec3 origin = getPickOrigin(pickResult); - glm::vec3 end = getPickEnd(pickResult, _defaultRenderStates[_currentRenderState].first); - _defaultRenderStates[_currentRenderState].second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, - _faceAvatar, _followNormal, _followNormalStrength, _defaultRenderStates[_currentRenderState].first, pickResult); + glm::vec3 end = getPickEnd(pickResult, defaultRenderState->second.first); + defaultRenderState->second.second->update(origin, end, Vectors::UP, _scaleWithAvatar, _distanceScaleEnd, _centerEndY, + _faceAvatar, _followNormal, _followNormalStrength, defaultRenderState->second.first, pickResult); } else if (!_currentRenderState.empty()) { - if (_renderStates.find(_currentRenderState) != _renderStates.end()) { - _renderStates[_currentRenderState]->disable(); + if (renderState != _renderStates.end() && renderState->second->isEnabled()) { + renderState->second->disable(); } - if (_defaultRenderStates.find(_currentRenderState) != _defaultRenderStates.end()) { - _defaultRenderStates[_currentRenderState].second->disable(); + if (defaultRenderState != _defaultRenderStates.end() && defaultRenderState->second.second->isEnabled()) { + defaultRenderState->second.second->disable(); } } } void PathPointer::editRenderState(const std::string& state, const QVariant& startProps, const QVariant& pathProps, const QVariant& endProps) { withWriteLock([&] { - updateRenderStateOverlay(_renderStates[state]->getStartID(), startProps); - updateRenderStateOverlay(_renderStates[state]->getEndID(), endProps); - QVariant startDim = startProps.toMap()["dimensions"]; - if (startDim.isValid()) { - _renderStates[state]->setStartDim(vec3FromVariant(startDim)); - } - QVariant endDim = endProps.toMap()["dimensions"]; - if (endDim.isValid()) { - _renderStates[state]->setEndDim(vec3FromVariant(endDim)); - } - QVariant rotation = endProps.toMap()["rotation"]; - if (rotation.isValid()) { - _renderStates[state]->setEndRot(quatFromVariant(rotation)); - } + auto renderState = _renderStates.find(state); + if (renderState != _renderStates.end()) { + updateRenderStateOverlay(renderState->second->getStartID(), startProps); + updateRenderStateOverlay(renderState->second->getEndID(), endProps); + QVariant startDim = startProps.toMap()["dimensions"]; + if (startDim.isValid()) { + renderState->second->setStartDim(vec3FromVariant(startDim)); + } + QVariant endDim = endProps.toMap()["dimensions"]; + if (endDim.isValid()) { + renderState->second->setEndDim(vec3FromVariant(endDim)); + } + QVariant rotation = endProps.toMap()["rotation"]; + if (rotation.isValid()) { + renderState->second->setEndRot(quatFromVariant(rotation)); + } - editRenderStatePath(state, pathProps); + editRenderStatePath(state, pathProps); + } }); } @@ -271,6 +278,7 @@ void StartEndRenderState::disable() { endProps.insert("ignoreRayIntersection", true); qApp->getOverlays().editOverlay(getEndID(), endProps); } + _enabled = false; } void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY, @@ -337,6 +345,7 @@ void StartEndRenderState::update(const glm::vec3& origin, const glm::vec3& end, endProps.insert("ignoreRayIntersection", doesEndIgnoreRays()); qApp->getOverlays().editOverlay(getEndID(), endProps); } + _enabled = true; } glm::vec2 PathPointer::findPos2D(const PickedObject& pickedObject, const glm::vec3& origin) { diff --git a/interface/src/raypick/PathPointer.h b/interface/src/raypick/PathPointer.h index 44c1b7f82b..b3638d1f7d 100644 --- a/interface/src/raypick/PathPointer.h +++ b/interface/src/raypick/PathPointer.h @@ -47,6 +47,8 @@ public: virtual void update(const glm::vec3& origin, const glm::vec3& end, const glm::vec3& surfaceNormal, bool scaleWithAvatar, bool distanceScaleEnd, bool centerEndY, bool faceAvatar, bool followNormal, float followNormalStrength, float distance, const PickResultPointer& pickResult); + bool isEnabled() const { return _enabled; } + protected: OverlayID _startID; OverlayID _endID; @@ -59,6 +61,8 @@ protected: glm::quat _avgEndRot; bool _avgEndRotInitialized { false }; + + bool _enabled { true }; }; typedef std::unordered_map> RenderStateMap; diff --git a/interface/src/raypick/StylusPointer.cpp b/interface/src/raypick/StylusPointer.cpp index 8c0fb59106..c74b6d8509 100644 --- a/interface/src/raypick/StylusPointer.cpp +++ b/interface/src/raypick/StylusPointer.cpp @@ -64,7 +64,9 @@ void StylusPointer::updateVisuals(const PickResultPointer& pickResult) { return; } } - hide(); + if (_showing) { + hide(); + } } void StylusPointer::show(const StylusTip& tip) { @@ -80,6 +82,7 @@ void StylusPointer::show(const StylusTip& tip) { props["visible"] = true; qApp->getOverlays().editOverlay(_stylusOverlay, props); } + _showing = true; } void StylusPointer::hide() { @@ -88,6 +91,7 @@ void StylusPointer::hide() { props.insert("visible", false); qApp->getOverlays().editOverlay(_stylusOverlay, props); } + _showing = false; } bool StylusPointer::shouldHover(const PickResultPointer& pickResult) { diff --git a/interface/src/raypick/StylusPointer.h b/interface/src/raypick/StylusPointer.h index 950b03b7c9..b1903840b3 100644 --- a/interface/src/raypick/StylusPointer.h +++ b/interface/src/raypick/StylusPointer.h @@ -76,6 +76,8 @@ private: static glm::vec3 findIntersection(const PickedObject& pickedObject, const glm::vec3& origin, const glm::vec3& direction); static glm::vec2 findPos2D(const PickedObject& pickedObject, const glm::vec3& origin); + bool _showing { true }; + }; #endif // hifi_StylusPointer_h From 9fb0f8b7c00fef665b94b7e56005063b394832f0 Mon Sep 17 00:00:00 2001 From: Angus Antley Date: Thu, 30 Aug 2018 00:25:53 +0100 Subject: [PATCH 204/207] added condition to prevent overwriting tracker input with estimated orientation for spine2 joint --- interface/src/avatar/MySkeletonModel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 8fd7d31956..77d1a87195 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -239,7 +239,10 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { params.primaryControllerFlags[Rig::PrimaryControllerType_Hips] = (uint8_t)Rig::ControllerFlags::Enabled | (uint8_t)Rig::ControllerFlags::Estimated; // set spine2 if we have hand controllers - if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid()) { + if (myAvatar->getControllerPoseInAvatarFrame(controller::Action::RIGHT_HAND).isValid() && + myAvatar->getControllerPoseInAvatarFrame(controller::Action::LEFT_HAND).isValid() && + !(params.primaryControllerFlags[Rig::PrimaryControllerType_Spine2] & (uint8_t)Rig::ControllerFlags::Enabled)) { + AnimPose currentSpine2Pose; AnimPose currentHeadPose; AnimPose currentHipsPose; From aaf5f593c598ab1b0df24b89a2cbf52beac39e86 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 29 Aug 2018 14:29:15 -0700 Subject: [PATCH 205/207] send MyAvatar avatar entity updates through entity tree --- interface/src/AvatarBookmarks.cpp | 18 ++--------- interface/src/avatar/MyAvatar.cpp | 52 +++++++++++++++++++++++++------ interface/src/avatar/MyAvatar.h | 5 ++- 3 files changed, 48 insertions(+), 27 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 4119b843e1..5c79bedc9a 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -145,20 +145,9 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { emit bookmarkDeleted(bookmarkName); } -bool isWearableEntity(const EntityItemPointer& entity) { - return entity->isVisible() && (entity->getParentJointIndex() != INVALID_JOINT_INDEX || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) - && (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); -} - void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); - + myAvatar->removeWearableAvatarEntities(); addAvatarEntities(avatarEntities); } @@ -183,10 +172,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { auto myAvatar = DependencyManager::get()->getMyAvatar(); auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); + myAvatar->removeWearableAvatarEntities(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 923d071cc0..7bf18f468c 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,18 +1703,50 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } -void MyAvatar::removeAvatarEntities(const std::function& condition) { +bool isWearableEntity(const EntityItemPointer& entity) { + return entity->isVisible() + && (entity->getParentJointIndex() != INVALID_JOINT_INDEX + || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) + && (entity->getParentID() == DependencyManager::get()->getSessionUUID() + || entity->getParentID() == AVATAR_SELF_ID); +} + +void MyAvatar::clearAvatarEntities() { auto treeRenderer = DependencyManager::get(); EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (entityTree) { - entityTree->withWriteLock([&] { - AvatarEntityMap avatarEntities = getAvatarEntityData(); - for (auto entityID : avatarEntities.keys()) { - if (!condition || condition(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } + + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + entityTree->withWriteLock([&entityID, &entityTree] { + // remove this entity first from the entity tree + entityTree->deleteEntity(entityID, true, true); }); + + // remove the avatar entity from our internal list + // (but indicate it doesn't need to be pulled from the tree) + clearAvatarEntity(entityID, false); + } +} + +void MyAvatar::removeWearableAvatarEntities() { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + + if (entityTree) { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + auto entity = entityTree->findEntityByID(entityID); + if (entity && isWearableEntity(entity)) { + entityTree->withWriteLock([&entityID, &entityTree] { + // remove this entity first from the entity tree + entityTree->deleteEntity(entityID, true, true); + }); + + // remove the avatar entity from our internal list + // (but indicate it doesn't need to be pulled from the tree) + clearAvatarEntity(entityID, false); + } + } } } @@ -2116,7 +2148,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) } // clear any existing avatar entities - setAvatarEntityData(AvatarEntityMap()); + clearAvatarEntities(); for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ba6348cc22..bb0df1af62 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,8 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void removeAvatarEntities(const std::function& condition = {}); + void clearAvatarEntities(); + void removeWearableAvatarEntities(); /**jsdoc * @function MyAvatar.isFlying @@ -1782,4 +1783,6 @@ void audioListenModeFromScriptValue(const QScriptValue& object, AudioListenerMod QScriptValue driveKeysToScriptValue(QScriptEngine* engine, const MyAvatar::DriveKeys& driveKeys); void driveKeysFromScriptValue(const QScriptValue& object, MyAvatar::DriveKeys& driveKeys); +bool isWearableEntity(const EntityItemPointer& entity); + #endif // hifi_MyAvatar_h From b8cd5dd8b97fef9a22fee72dcc5a13df69b7a6e7 Mon Sep 17 00:00:00 2001 From: Zach Fox Date: Wed, 29 Aug 2018 16:58:19 -0700 Subject: [PATCH 206/207] Fixup PR13870: Make the feature work in Master --- interface/src/Application.cpp | 10 +++++----- libraries/networking/src/AccountManager.h | 3 ++- scripts/system/commerce/wallet.js | 6 ++++-- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 44bba6250b..af29ea0bd1 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1735,11 +1735,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo QTimer* settingsTimer = new QTimer(); moveToNewNamedThread(settingsTimer, "Settings Thread", [this, settingsTimer]{ connect(qApp, &Application::beforeAboutToQuit, [this, settingsTimer]{ - bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); - if (autoLogout) { - auto accountManager = DependencyManager::get(); - accountManager->logout(); - } // Disconnect the signal from the save settings QObject::disconnect(settingsTimer, &QTimer::timeout, this, &Application::saveSettings); // Stop the settings timer @@ -2491,6 +2486,11 @@ void Application::cleanupBeforeQuit() { } DependencyManager::destroy(); + bool autoLogout = Setting::Handle(AUTO_LOGOUT_SETTING_NAME, false).get(); + if (autoLogout) { + DependencyManager::get()->removeAccountFromFile(); + } + _displayPlugin.reset(); PluginManager::getInstance()->shutdown(); diff --git a/libraries/networking/src/AccountManager.h b/libraries/networking/src/AccountManager.h index a79b69fe2b..b122115dd0 100644 --- a/libraries/networking/src/AccountManager.h +++ b/libraries/networking/src/AccountManager.h @@ -96,6 +96,8 @@ public: QUrl getMetaverseServerURL() { return NetworkingConstants::METAVERSE_SERVER_URL(); } + void removeAccountFromFile(); + public slots: void requestAccessToken(const QString& login, const QString& password); void requestAccessTokenWithSteam(QByteArray authSessionTicket); @@ -133,7 +135,6 @@ private: void operator=(AccountManager const& other) = delete; void persistAccountToFile(); - void removeAccountFromFile(); void passSuccessToCallback(QNetworkReply* reply); void passErrorToCallback(QNetworkReply* reply); diff --git a/scripts/system/commerce/wallet.js b/scripts/system/commerce/wallet.js index 8730273e7c..5939b36438 100644 --- a/scripts/system/commerce/wallet.js +++ b/scripts/system/commerce/wallet.js @@ -407,8 +407,10 @@ } function onUsernameChanged() { - Settings.setValue("wallet/autoLogout", false); - Settings.setValue("wallet/savedUsername", ""); + if (Account.username !== Settings.getValue("wallet/savedUsername")) { + Settings.setValue("wallet/autoLogout", false); + Settings.setValue("wallet/savedUsername", ""); + } } // Function Name: fromQml() From 8b1fcfcce909c735b448df52b2ada545e0f042c1 Mon Sep 17 00:00:00 2001 From: David Back Date: Thu, 30 Aug 2018 10:16:27 -0700 Subject: [PATCH 207/207] fix double windows platform --- interface/src/Application.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index c365e61624..eae42ea32e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -913,7 +913,7 @@ bool setupEssentials(int& argc, char** argv, bool runningMarkerExisted) { controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_CAMERA_FULL_SCREEN_MIRROR, STATE_CAMERA_FIRST_PERSON, STATE_CAMERA_THIRD_PERSON, STATE_CAMERA_ENTITY, STATE_CAMERA_INDEPENDENT, STATE_SNAP_TURN, STATE_ADVANCED_MOVEMENT_CONTROLS, STATE_GROUNDED, STATE_NAV_FOCUSED, - STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_WINDOWS } }); + STATE_PLATFORM_WINDOWS, STATE_PLATFORM_MAC, STATE_PLATFORM_ANDROID } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set();