From 8a5fb07ca77aea2c6f6bd5562cd7a501509e027d Mon Sep 17 00:00:00 2001 From: Chris Collins Date: Thu, 8 Dec 2016 21:12:47 -0800 Subject: [PATCH 01/48] Merge pull request #9169 from samcake/red Fix the highlight issue introduced RC 29 - 5774 --- libraries/render-utils/src/local_lights_shading.slf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index d3542fcdfa..0b884d8bc0 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -85,7 +85,7 @@ void main(void) { vec3 fragEyeDir = normalize(fragEyeVector.xyz); // COmpute the rougness into gloss2 once: - float fragGloss2 = pow(frag.roughness + 0.001, 2.0); + float fragGloss2 = pow(frag.roughness + 0.001, 4.0); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); int numLightTouching = 0; From 9b65acc54e558ccf3bec8d98914e14c9933729a1 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 9 Dec 2016 09:28:31 -0800 Subject: [PATCH 02/48] Merge pull request #9177 from samcake/blue Fix the missing lighting on AMD --- .../render-utils/src/LightClusterGrid.slh | 14 +++++++++----- libraries/render-utils/src/LightClusters.cpp | 18 +++++++++--------- .../render-utils/src/local_lights_shading.slf | 5 ++--- 3 files changed, 20 insertions(+), 17 deletions(-) diff --git a/libraries/render-utils/src/LightClusterGrid.slh b/libraries/render-utils/src/LightClusterGrid.slh index f4ec35a75c..722d37814d 100644 --- a/libraries/render-utils/src/LightClusterGrid.slh +++ b/libraries/render-utils/src/LightClusterGrid.slh @@ -24,7 +24,7 @@ struct FrustumGrid { mat4 eyeToWorldMat; }; -uniform frustumGridBuffer { +layout(std140) uniform frustumGridBuffer { FrustumGrid frustumGrid; }; @@ -51,16 +51,20 @@ float projection_getFar(mat4 projection) { #define GRID_INDEX_TYPE ivec4 #define GRID_FETCH_BUFFER(i) i / 4][i % 4 <@else@> -#define GRID_NUM_ELEMENTS 16384 +#define GRID_NUM_ELEMENTS 4096 +#define GRID_INDEX_TYPE ivec4 +#define GRID_FETCH_BUFFER(i) i / 4][i % 4 + + <@endif@> -uniform clusterGridBuffer { +layout(std140) uniform clusterGridBuffer { GRID_INDEX_TYPE _clusterGridTable[GRID_NUM_ELEMENTS]; }; -uniform clusterContentBuffer { +layout(std140) uniform clusterContentBuffer { GRID_INDEX_TYPE _clusterGridContent[GRID_NUM_ELEMENTS]; }; diff --git a/libraries/render-utils/src/LightClusters.cpp b/libraries/render-utils/src/LightClusters.cpp index d3a384f1df..8dc95da65b 100644 --- a/libraries/render-utils/src/LightClusters.cpp +++ b/libraries/render-utils/src/LightClusters.cpp @@ -27,21 +27,21 @@ enum LightClusterGridShader_MapSlot { DEFERRED_BUFFER_LINEAR_DEPTH_UNIT = 0, - DEFERRED_BUFFER_COLOR_UNIT, - DEFERRED_BUFFER_NORMAL_UNIT, - DEFERRED_BUFFER_EMISSIVE_UNIT, - DEFERRED_BUFFER_DEPTH_UNIT, + DEFERRED_BUFFER_COLOR_UNIT = 1, + DEFERRED_BUFFER_NORMAL_UNIT = 2, + DEFERRED_BUFFER_EMISSIVE_UNIT = 3, + DEFERRED_BUFFER_DEPTH_UNIT = 4, }; enum LightClusterGridShader_BufferSlot { LIGHT_CLUSTER_GRID_FRUSTUM_GRID_SLOT = 0, - DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT, - CAMERA_CORRECTION_BUFFER_SLOT, + DEFERRED_FRAME_TRANSFORM_BUFFER_SLOT =1, + CAMERA_CORRECTION_BUFFER_SLOT = 2, LIGHT_GPU_SLOT = render::ShapePipeline::Slot::LIGHT, - LIGHT_INDEX_GPU_SLOT, + LIGHT_INDEX_GPU_SLOT = 5, - LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT, - LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT, + LIGHT_CLUSTER_GRID_CLUSTER_GRID_SLOT = 6, + LIGHT_CLUSTER_GRID_CLUSTER_CONTENT_SLOT = 7, }; FrustumGrid::FrustumGrid(const FrustumGrid& source) : diff --git a/libraries/render-utils/src/local_lights_shading.slf b/libraries/render-utils/src/local_lights_shading.slf index 0b884d8bc0..a4e28a9757 100644 --- a/libraries/render-utils/src/local_lights_shading.slf +++ b/libraries/render-utils/src/local_lights_shading.slf @@ -34,7 +34,6 @@ in vec2 _texCoord0; out vec4 _fragColor; void main(void) { - // Grab the fragment data from the uv vec2 texCoord = _texCoord0.st; @@ -49,7 +48,7 @@ void main(void) { // Frag pos in world mat4 invViewMat = getViewInverse(); vec4 fragPos = invViewMat * fragPosition; - + // From frag world pos find the cluster vec4 clusterEyePos = frustumGrid_worldToEye(fragPos); ivec3 clusterPos = frustumGrid_eyeToClusterPos(clusterEyePos.xyz); @@ -84,7 +83,7 @@ void main(void) { vec4 fragEyeVector = invViewMat * vec4(-frag.position.xyz, 0.0); vec3 fragEyeDir = normalize(fragEyeVector.xyz); - // COmpute the rougness into gloss2 once: + // Compute the rougness into gloss2 once: float fragGloss2 = pow(frag.roughness + 0.001, 4.0); bool withScattering = (frag.scattering * isScatteringEnabled() > 0.0); From a4126b3ee000054c76fd069b8a15e008632bb7f3 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 14:23:18 -0800 Subject: [PATCH 03/48] revert "fix competition between seek mouse and lasers" --- .../controllers/handControllerPointer.js | 27 ++++--------------- 1 file changed, 5 insertions(+), 22 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 4d478fbdd5..0623ddf100 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,9 +20,6 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. -var systemLaserOn = false; - - Script.include("../libraries/controllers.js"); // UTILITIES ------------- @@ -124,12 +121,6 @@ function ignoreMouseActivity() { if (!Reticle.allowMouseCapture) { return true; } - - // if the lasers are on, then reticle/mouse should be hidden and we can ignore it for seeking or depth updating - if (systemLaserOn) { - return true; - } - var pos = Reticle.position; if (!pos || (pos.x == -1 && pos.y == -1)) { return true; @@ -270,12 +261,6 @@ var ONE_MINUS_WEIGHTING = 1 - WEIGHTING; var AVERAGE_MOUSE_VELOCITY_FOR_SEEK_TO = 20; function isShakingMouse() { // True if the person is waving the mouse around trying to find it. var now = Date.now(), mouse = Reticle.position, isShaking = false; - - // if the lasers are on, then we ignore mouse shaking - if (systemLaserOn) { - return false; - } - if (lastIntegration && (lastIntegration !== now)) { var velocity = Vec3.length(Vec3.subtract(mouse, lastMouse)) / (now - lastIntegration); averageMouseVelocity = (ONE_MINUS_WEIGHTING * averageMouseVelocity) + (WEIGHTING * velocity); @@ -290,14 +275,9 @@ function isShakingMouse() { // True if the person is waving the mouse around try var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { - // if the lasers are on, then we never do seeking - if (systemLaserOn) { - isSeeking = false; - return; - } - if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) { if (!isSeeking) { + print('Start seeking mouse.'); isSeeking = true; } } // e.g., if we're about to turn it on with first movement. @@ -307,6 +287,7 @@ function updateSeeking(doNotStartSeeking) { averageMouseVelocity = lastIntegration = 0; var lookAt2D = HMD.getHUDLookAtPosition2D(); if (!lookAt2D) { // If this happens, something has gone terribly wrong. + print('Cannot seek without lookAt position'); isSeeking = false; return; // E.g., if parallel to location in HUD } @@ -322,6 +303,7 @@ function updateSeeking(doNotStartSeeking) { } var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. if (okX && okY) { + print('Finished seeking mouse'); isSeeking = false; } else { Reticle.setPosition(copy); // Not setReticlePosition @@ -340,7 +322,7 @@ function updateMouseActivity(isClick) { return; } // Bug: mouse clicks should keep going. Just not hand controller clicks handControllerLockOut.update(now); - Reticle.visible = !systemLaserOn; + Reticle.visible = true; } function expireMouseCursor(now) { if (!isPointingAtOverlay() && mouseCursorActivity.expired(now)) { @@ -492,6 +474,7 @@ var LASER_ALPHA = 0.5; var LASER_SEARCH_COLOR_XYZW = {x: 10 / 255, y: 10 / 255, z: 255 / 255, w: LASER_ALPHA}; var LASER_TRIGGER_COLOR_XYZW = {x: 250 / 255, y: 10 / 255, z: 10 / 255, w: LASER_ALPHA}; var SYSTEM_LASER_DIRECTION = {x: 0, y: 0, z: -1}; +var systemLaserOn = false; function clearSystemLaser() { if (!systemLaserOn) { return; From dc2a42df4f277c76a9adbb9d73471f34e36b1bee Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 14:24:24 -0800 Subject: [PATCH 04/48] fix mouse vs lasers, again --- scripts/system/controllers/handControllerPointer.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 0623ddf100..2a64b856bc 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -20,6 +20,10 @@ // When partially squeezing over a HUD element, a laser or the reticle is shown where the active hand // controller beam intersects the HUD. +var activeTrigger; +function isLaserOn() { + return activeTrigger.partial(); +} Script.include("../libraries/controllers.js"); // UTILITIES ------------- @@ -275,7 +279,7 @@ function isShakingMouse() { // True if the person is waving the mouse around try var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { - if (!doNotStartSeeking && (!Reticle.visible || isShakingMouse())) { + if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { if (!isSeeking) { print('Start seeking mouse.'); isSeeking = true; @@ -374,7 +378,7 @@ setupHandler(Controller.mouseDoublePressEvent, onMouseClick); var leftTrigger = new Trigger('left'); var rightTrigger = new Trigger('right'); -var activeTrigger = rightTrigger; +activeTrigger = rightTrigger; var activeHand = Controller.Standard.RightHand; var LEFT_HUD_LASER = 1; var RIGHT_HUD_LASER = 2; From 4813324b76ea03e0017d4bd2db04761c29672c84 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 16:24:05 -0800 Subject: [PATCH 05/48] use update signal instead of timer --- scripts/system/controllers/handControllerPointer.js | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 2a64b856bc..1f8f0f53e6 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -571,12 +571,6 @@ function update() { Reticle.visible = false; } -var BASIC_TIMER_INTERVAL = 20; // 20ms = 50hz good enough -var updateIntervalTimer = Script.setInterval(function(){ - update(); -}, BASIC_TIMER_INTERVAL); - - // Check periodically for changes to setup. var SETTINGS_CHANGE_RECHECK_INTERVAL = 10 * 1000; // 10 seconds function checkSettings() { @@ -586,9 +580,10 @@ function checkSettings() { checkSettings(); var settingsChecker = Script.setInterval(checkSettings, SETTINGS_CHANGE_RECHECK_INTERVAL); +Script.update.connect(update); Script.scriptEnding.connect(function () { Script.clearInterval(settingsChecker); - Script.clearInterval(updateIntervalTimer); + Script.update.disconnect(update); OffscreenFlags.navigationFocusDisabled = false; }); From b3c64d4693fc662df773b9199b6a12d9da4e1e8a Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 16:32:15 -0800 Subject: [PATCH 06/48] remove log spam --- scripts/system/controllers/handControllerPointer.js | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index 1f8f0f53e6..b7dfcaeb56 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -280,10 +280,7 @@ var NON_LINEAR_DIVISOR = 2; var MINIMUM_SEEK_DISTANCE = 0.1; function updateSeeking(doNotStartSeeking) { if (!doNotStartSeeking && !isLaserOn() && (!Reticle.visible || isShakingMouse())) { - if (!isSeeking) { - print('Start seeking mouse.'); - isSeeking = true; - } + isSeeking = true; } // e.g., if we're about to turn it on with first movement. if (!isSeeking) { return; @@ -291,7 +288,6 @@ function updateSeeking(doNotStartSeeking) { averageMouseVelocity = lastIntegration = 0; var lookAt2D = HMD.getHUDLookAtPosition2D(); if (!lookAt2D) { // If this happens, something has gone terribly wrong. - print('Cannot seek without lookAt position'); isSeeking = false; return; // E.g., if parallel to location in HUD } @@ -307,7 +303,6 @@ function updateSeeking(doNotStartSeeking) { } var okX = !updateDimension('x'), okY = !updateDimension('y'); // Evaluate both. Don't short-circuit. if (okX && okY) { - print('Finished seeking mouse'); isSeeking = false; } else { Reticle.setPosition(copy); // Not setReticlePosition From aba5be4dd21c53c99eaaf870cde6856ba9f3df49 Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Wed, 11 Jan 2017 17:01:46 -0800 Subject: [PATCH 07/48] don't set bogus values --- scripts/system/controllers/handControllerPointer.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/system/controllers/handControllerPointer.js b/scripts/system/controllers/handControllerPointer.js index b7dfcaeb56..2c732b4cdc 100644 --- a/scripts/system/controllers/handControllerPointer.js +++ b/scripts/system/controllers/handControllerPointer.js @@ -482,7 +482,6 @@ function clearSystemLaser() { HMD.disableExtraLaser(); systemLaserOn = false; weMovedReticle = true; - Reticle.position = { x: -1, y: -1 }; } function setColoredLaser() { // answer trigger state if lasers supported, else falsey. var color = (activeTrigger.state === 'full') ? LASER_TRIGGER_COLOR_XYZW : LASER_SEARCH_COLOR_XYZW; From 698d63bef9bd80a470879c2fe97a14021cdc14c6 Mon Sep 17 00:00:00 2001 From: Ken Cooke Date: Sat, 14 Jan 2017 10:26:13 -0800 Subject: [PATCH 08/48] Fix audio failures on systems that return incorrect/unsupported mixFormat --- libraries/audio-client/src/AudioClient.cpp | 42 +++++++++------------- 1 file changed, 16 insertions(+), 26 deletions(-) diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 1aa4439de9..1e3dc11338 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -424,35 +424,25 @@ bool adjustedFormatForAudioDevice(const QAudioDeviceInfo& audioDevice, adjustedAudioFormat = desiredAudioFormat; // - // Attempt the device sample rate in decreasing order of preference. + // Attempt the device sample rate and channel count in decreasing order of preference. // - if (audioDevice.supportedSampleRates().contains(48000)) { - adjustedAudioFormat.setSampleRate(48000); - } else if (audioDevice.supportedSampleRates().contains(44100)) { - adjustedAudioFormat.setSampleRate(44100); - } else if (audioDevice.supportedSampleRates().contains(32000)) { - adjustedAudioFormat.setSampleRate(32000); - } else if (audioDevice.supportedSampleRates().contains(24000)) { - adjustedAudioFormat.setSampleRate(24000); - } else if (audioDevice.supportedSampleRates().contains(16000)) { - adjustedAudioFormat.setSampleRate(16000); - } else if (audioDevice.supportedSampleRates().contains(96000)) { - adjustedAudioFormat.setSampleRate(96000); - } else if (audioDevice.supportedSampleRates().contains(192000)) { - adjustedAudioFormat.setSampleRate(192000); - } else if (audioDevice.supportedSampleRates().contains(88200)) { - adjustedAudioFormat.setSampleRate(88200); - } else if (audioDevice.supportedSampleRates().contains(176400)) { - adjustedAudioFormat.setSampleRate(176400); + const int sampleRates[] = { 48000, 44100, 32000, 24000, 16000, 96000, 192000, 88200, 176400 }; + const int inputChannels[] = { 1, 2, 4, 6, 8 }; // prefer mono + const int outputChannels[] = { 2, 4, 6, 8, 1 }; // prefer stereo, downmix as last resort + + for (int channelCount : (desiredAudioFormat.channelCount() == 1 ? inputChannels : outputChannels)) { + for (int sampleRate : sampleRates) { + + adjustedAudioFormat.setChannelCount(channelCount); + adjustedAudioFormat.setSampleRate(sampleRate); + + if (audioDevice.isFormatSupported(adjustedAudioFormat)) { + return true; + } + } } - if (adjustedAudioFormat != desiredAudioFormat) { - // return the nearest in case it needs 2 channels - adjustedAudioFormat = audioDevice.nearestFormat(adjustedAudioFormat); - return true; - } else { - return false; - } + return false; // a supported format could not be found } bool sampleChannelConversion(const int16_t* sourceSamples, int16_t* destinationSamples, unsigned int numSourceSamples, From bb715bf7adb0c8b167709c85b970e47ae7ce41e0 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Mon, 16 Jan 2017 10:09:47 -0800 Subject: [PATCH 09/48] Fix bug in FBX parse changes --- libraries/fbx/src/FBXReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 5272969e6b..42922ce226 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -972,7 +972,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS static const QVariant EMISSIVE = QByteArray("Emissive"); static const QVariant AMBIENT_FACTOR = QByteArray("AmbientFactor"); static const QVariant SHININESS = QByteArray("Shininess"); - static const QVariant OPACITY = QByteArray("Shininess"); + static const QVariant OPACITY = QByteArray("Opacity"); static const QVariant MAYA_USE_NORMAL_MAP = QByteArray("Maya|use_normal_map"); static const QVariant MAYA_BASE_COLOR = QByteArray("Maya|base_color"); static const QVariant MAYA_USE_COLOR_MAP = QByteArray("Maya|use_color_map"); From 167aa7620e3f28d527e3454f3b5ec1df321b3624 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Mon, 16 Jan 2017 15:13:28 -0800 Subject: [PATCH 10/48] fix cauterization hack --- .../render-utils/src/MeshPartPayload.cpp | 19 ++++++++++++++++--- libraries/render-utils/src/MeshPartPayload.h | 5 ++++- libraries/render-utils/src/Model.cpp | 2 +- 3 files changed, 21 insertions(+), 5 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 5c4c0890a7..e3b2527e67 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -359,8 +359,11 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices) { _transform = transform; + _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = AABox(); @@ -373,6 +376,11 @@ void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transf _worldBound.transform(transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); + if (cauterizedClusterMatrices.size() != 0) { + _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); + } else { + _cauterizedTransform = _transform; + } } } } @@ -527,9 +535,14 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: } else { batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::SKINNING, state.clusterBuffer); } + batch.setModelTransform(_transform); + } else { + if (canCauterize && _model->getCauterizeBones()) { + batch.setModelTransform(_cauterizedTransform); + } else { + batch.setModelTransform(_transform); + } } - - batch.setModelTransform(_transform); } void ModelMeshPartPayload::startFade() { diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index b048dc903f..b7a8cf63f0 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -85,7 +85,9 @@ public: typedef Payload::DataPointer Pointer; void notifyLocationChanged() override; - void updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices); + void updateTransformForSkinnedMesh(const Transform& transform, + const QVector& clusterMatrices, + const QVector& cauterizedClusterMatrices); // Entity fade in void startFade(); @@ -106,6 +108,7 @@ public: Model* _model; + Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 7c1a6e14d0..3a3095458c 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -238,7 +238,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); } } }); From ef6e83788eb3f3c7451e39d49095a79e8e97974f Mon Sep 17 00:00:00 2001 From: howard-stearns Date: Mon, 16 Jan 2017 14:54:32 -0800 Subject: [PATCH 11/48] handControllerGrab update loop change --- scripts/system/controllers/handControllerGrab.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 2ed09232e6..57698bd0dc 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -3082,7 +3082,7 @@ var handleHandMessages = function(channel, message, sender) { Messages.messageReceived.connect(handleHandMessages); -var TARGET_UPDATE_HZ = 50; // 50hz good enough (no change in logic) +var TARGET_UPDATE_HZ = 60; // 50hz good enough, but we're using update var BASIC_TIMER_INTERVAL_MS = 1000 / TARGET_UPDATE_HZ; var lastInterval = Date.now(); @@ -3095,7 +3095,7 @@ var updateTotalWork = 0; var UPDATE_PERFORMANCE_DEBUGGING = false; -var updateIntervalTimer = Script.setInterval(function(){ +function updateWrapper(){ intervalCount++; var thisInterval = Date.now(); @@ -3141,11 +3141,12 @@ var updateIntervalTimer = Script.setInterval(function(){ updateTotalWork = 0; } -}, BASIC_TIMER_INTERVAL_MS); +} +Script.update.connect(updateWrapper); function cleanup() { Menu.removeMenuItem("Developer", "Show Grab Sphere"); - Script.clearInterval(updateIntervalTimer); + Script.update.disconnect(updateWrapper); rightController.cleanup(); leftController.cleanup(); Controller.disableMapping(MAPPING_NAME); From ec93a663e7c523902d20d3e04e0ae744eabbfbed Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 14:05:23 -0800 Subject: [PATCH 12/48] don't send AVATAR_SELF_ID over wire --- libraries/entities/src/EntityEditPacketSender.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index e05db07d0d..97648c7079 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -100,7 +100,18 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, QByteArray bufferOut(NLPacket::maxPayloadSize(type), 0); - if (EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut)) { + bool success; + if (properties.parentIDChanged() && properties.getParentID() == AVATAR_SELF_ID) { + EntityItemProperties propertiesCopy = properties; + auto nodeList = DependencyManager::get(); + const QUuid myNodeID = nodeList->getSessionUUID(); + propertiesCopy.setParentID(myNodeID); + success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, propertiesCopy, bufferOut); + } else { + success = EntityItemProperties::encodeEntityEditPacket(type, entityItemID, properties, bufferOut); + } + + if (success) { #ifdef WANT_DEBUG qCDebug(entities) << "calling queueOctreeEditMessage()..."; qCDebug(entities) << " id:" << entityItemID; From 6b4c800c2ee98e59ba1ba7aa6ea6b1e33e058d86 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 15:50:59 -0800 Subject: [PATCH 13/48] don't call stepKinematicMotion on kinematic entities --- libraries/physics/src/EntityMotionState.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e9891020b3..c90ca5a1a9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,15 +199,12 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC) { + if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - _entity->setAcceleration(glm::vec3(0.0f)); - } else { - _entity->setAcceleration(_entity->getGravity()); - } + _entity->setAcceleration(_entity->getGravity()); + uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); From c0e313830c829543e341eb0ae45b8fa1c2982605 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:14:15 -0800 Subject: [PATCH 14/48] make hasAncestorOfType understand AVATAR_SELF_ID --- libraries/physics/src/EntityMotionState.cpp | 9 ++++++--- libraries/shared/src/SpatiallyNestable.cpp | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c90ca5a1a9..e9891020b3 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,12 +199,15 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { + if (_motionType == MOTION_TYPE_KINEMATIC) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - _entity->setAcceleration(_entity->getGravity()); - + if (_entity->hasAncestorOfType(NestableType::Avatar)) { + _entity->setAcceleration(glm::vec3(0.0f)); + } else { + _entity->setAcceleration(_entity->getGravity()); + } uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index 35e574bf06..f071bda71f 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1034,6 +1034,13 @@ AACube SpatiallyNestable::getQueryAACube() const { bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { bool success; + if (nestableType == NestableType::Avatar) { + QUuid parentID = getParentID(); + if (parentID == AVATAR_SELF_ID) { + return true; + } + } + SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { return false; From 933aed47194f5a9491561939c65f419709ae69d3 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:36:06 -0800 Subject: [PATCH 15/48] don't call stepKinematicMotion on kinematic entities when they are children of avatars --- libraries/physics/src/EntityMotionState.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index e9891020b3..c90ca5a1a9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -199,15 +199,12 @@ void EntityMotionState::getWorldTransform(btTransform& worldTrans) const { return; } assert(entityTreeIsLocked()); - if (_motionType == MOTION_TYPE_KINEMATIC) { + if (_motionType == MOTION_TYPE_KINEMATIC && !_entity->hasAncestorOfType(NestableType::Avatar)) { BT_PROFILE("kinematicIntegration"); // This is physical kinematic motion which steps strictly by the subframe count // of the physics simulation and uses full gravity for acceleration. - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - _entity->setAcceleration(glm::vec3(0.0f)); - } else { - _entity->setAcceleration(_entity->getGravity()); - } + _entity->setAcceleration(_entity->getGravity()); + uint32_t thisStep = ObjectMotionState::getWorldSimulationStep(); float dt = (thisStep - _lastKinematicStep) * PHYSICS_ENGINE_FIXED_SUBSTEP; _entity->stepKinematicMotion(dt); From f13fdf2a4c62b8cd4b3b70504e5d7403710ff030 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:43:36 -0800 Subject: [PATCH 16/48] make findAncestorOfType understand AVATAR_SELF_ID --- libraries/shared/src/SpatiallyNestable.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/shared/src/SpatiallyNestable.cpp b/libraries/shared/src/SpatiallyNestable.cpp index f071bda71f..ddc3f416e0 100644 --- a/libraries/shared/src/SpatiallyNestable.cpp +++ b/libraries/shared/src/SpatiallyNestable.cpp @@ -1055,6 +1055,14 @@ bool SpatiallyNestable::hasAncestorOfType(NestableType nestableType) const { const QUuid SpatiallyNestable::findAncestorOfType(NestableType nestableType) const { bool success; + + if (nestableType == NestableType::Avatar) { + QUuid parentID = getParentID(); + if (parentID == AVATAR_SELF_ID) { + return AVATAR_SELF_ID; // TODO -- can we put nodeID here? + } + } + SpatiallyNestablePointer parent = getParentPointer(success); if (!success || !parent) { return QUuid(); From b90d7e26899067b6c4ba56cde235fddced1b3372 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Fri, 3 Feb 2017 16:47:03 -0800 Subject: [PATCH 17/48] another AVATAR_SELF_ID adjustment --- libraries/entities/src/EntityItem.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 233ce7d88e..2503687dce 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1828,7 +1828,8 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask // "bootstrapping" problem where you can shoot yourself across the room by grabbing something // and holding it against your own avatar. QUuid ancestorID = findAncestorOfType(NestableType::Avatar); - if (!ancestorID.isNull() && ancestorID == Physics::getSessionUUID()) { + if (!ancestorID.isNull() && + (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; } } From 0dc7152dcca44bbba6237e4b82bf60e09ec03055 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 09:50:48 -0800 Subject: [PATCH 18/48] if an entity has an avatar parent but ends up in EntityMotionState::setWorldTransform, ignore the update from bullet --- libraries/physics/src/EntityMotionState.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index c90ca5a1a9..d652e78f6a 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -230,6 +230,9 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(entityTreeIsLocked()); measureBodyAcceleration(); bool positionSuccess; + if (_entity->hasAncestorOfType(NestableType::Avatar)) { + return; + } _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); if (!positionSuccess) { static QString repeatedMessage = From f68fcbb9cd89a37da03f305044d1df8fa87a0421 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 12:10:26 -0800 Subject: [PATCH 19/48] when the parentID is changed by a network update, set physics flags --- libraries/entities/src/EntityItem.cpp | 2 +- libraries/physics/src/EntityMotionState.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index 2503687dce..a656827d5e 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -828,7 +828,7 @@ int EntityItem::readEntityDataFromBuffer(const unsigned char* data, int bytesLef { // parentID and parentJointIndex are also protected by simulation ownership bool oldOverwrite = overwriteLocalData; overwriteLocalData = overwriteLocalData && !weOwnSimulation; - READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, setParentID); + READ_ENTITY_PROPERTY(PROP_PARENT_ID, QUuid, updateParentID); READ_ENTITY_PROPERTY(PROP_PARENT_JOINT_INDEX, quint16, setParentJointIndex); overwriteLocalData = oldOverwrite; } diff --git a/libraries/physics/src/EntityMotionState.cpp b/libraries/physics/src/EntityMotionState.cpp index d652e78f6a..c90ca5a1a9 100644 --- a/libraries/physics/src/EntityMotionState.cpp +++ b/libraries/physics/src/EntityMotionState.cpp @@ -230,9 +230,6 @@ void EntityMotionState::setWorldTransform(const btTransform& worldTrans) { assert(entityTreeIsLocked()); measureBodyAcceleration(); bool positionSuccess; - if (_entity->hasAncestorOfType(NestableType::Avatar)) { - return; - } _entity->setPosition(bulletToGLM(worldTrans.getOrigin()) + ObjectMotionState::getWorldOffset(), positionSuccess, false); if (!positionSuccess) { static QString repeatedMessage = From 40730c1d19376b455e8350d671696529372fd970 Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 14:16:47 -0800 Subject: [PATCH 20/48] avoid bootstrapping self with something that has someone else as simulation owner --- libraries/entities/src/EntityItem.cpp | 50 +++++++++++++++------------ 1 file changed, 27 insertions(+), 23 deletions(-) diff --git a/libraries/entities/src/EntityItem.cpp b/libraries/entities/src/EntityItem.cpp index a656827d5e..6543af5355 100644 --- a/libraries/entities/src/EntityItem.cpp +++ b/libraries/entities/src/EntityItem.cpp @@ -1823,29 +1823,6 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask } uint8_t userMask = getCollisionMask(); - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the - // "bootstrapping" problem where you can shoot yourself across the room by grabbing something - // and holding it against your own avatar. - QUuid ancestorID = findAncestorOfType(NestableType::Avatar); - if (!ancestorID.isNull() && - (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - } - } - if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { - // also, don't bootstrap our own avatar with a hold action - QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); - QList::const_iterator i = holdActions.begin(); - while (i != holdActions.end()) { - EntityActionPointer action = *i; - if (action->isMine()) { - userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; - break; - } - i++; - } - } if ((bool)(userMask & USER_COLLISION_GROUP_MY_AVATAR) != (bool)(userMask & USER_COLLISION_GROUP_OTHER_AVATAR)) { @@ -1855,6 +1832,33 @@ void EntityItem::computeCollisionGroupAndFinalMask(int16_t& group, int16_t& mask userMask ^= USER_COLLISION_MASK_AVATARS | ~userMask; } } + + if (userMask & USER_COLLISION_GROUP_MY_AVATAR) { + bool iAmHoldingThis = false; + // if this entity is a descendant of MyAvatar, don't collide with MyAvatar. This avoids the + // "bootstrapping" problem where you can shoot yourself across the room by grabbing something + // and holding it against your own avatar. + QUuid ancestorID = findAncestorOfType(NestableType::Avatar); + if (!ancestorID.isNull() && + (ancestorID == Physics::getSessionUUID() || ancestorID == AVATAR_SELF_ID)) { + iAmHoldingThis = true; + } + // also, don't bootstrap our own avatar with a hold action + QList holdActions = getActionsOfType(ACTION_TYPE_HOLD); + QList::const_iterator i = holdActions.begin(); + while (i != holdActions.end()) { + EntityActionPointer action = *i; + if (action->isMine()) { + iAmHoldingThis = true; + break; + } + i++; + } + + if (iAmHoldingThis) { + userMask &= ~USER_COLLISION_GROUP_MY_AVATAR; + } + } mask = Physics::getDefaultCollisionMask(group) & (int16_t)(userMask); } } From 725917af0d29615b0fdfdbd321f99d3ee9d5dfff Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Mon, 6 Feb 2017 16:49:26 -0800 Subject: [PATCH 21/48] send UserActivityLogger events when tablet is opened or closed --- .../src/UserActivityLoggerScriptingInterface.cpp | 8 ++++++++ .../networking/src/UserActivityLoggerScriptingInterface.h | 2 ++ scripts/system/tablet-ui/tabletUI.js | 5 +++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp index 02d1711230..f38d24c31f 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.cpp @@ -16,6 +16,14 @@ void UserActivityLoggerScriptingInterface::enabledEdit() { logAction("enabled_edit"); } +void UserActivityLoggerScriptingInterface::openedTablet() { + logAction("opened_tablet"); +} + +void UserActivityLoggerScriptingInterface::closedTablet() { + logAction("closed_tablet"); +} + void UserActivityLoggerScriptingInterface::openedMarketplace() { logAction("opened_marketplace"); } diff --git a/libraries/networking/src/UserActivityLoggerScriptingInterface.h b/libraries/networking/src/UserActivityLoggerScriptingInterface.h index a202858a1c..b827b2262a 100644 --- a/libraries/networking/src/UserActivityLoggerScriptingInterface.h +++ b/libraries/networking/src/UserActivityLoggerScriptingInterface.h @@ -21,6 +21,8 @@ class UserActivityLoggerScriptingInterface : public QObject, public Dependency { Q_OBJECT public: Q_INVOKABLE void enabledEdit(); + Q_INVOKABLE void openedTablet(); + Q_INVOKABLE void closedTablet(); Q_INVOKABLE void openedMarketplace(); Q_INVOKABLE void toggledAway(bool isAway); Q_INVOKABLE void tutorialProgress(QString stepName, int stepNumber, float secondsToComplete, diff --git a/scripts/system/tablet-ui/tabletUI.js b/scripts/system/tablet-ui/tabletUI.js index eab3d85adc..dc1d71f402 100644 --- a/scripts/system/tablet-ui/tabletUI.js +++ b/scripts/system/tablet-ui/tabletUI.js @@ -12,7 +12,7 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // -/* global Script, HMD, WebTablet, UIWebTablet */ +/* global Script, HMD, WebTablet, UIWebTablet, UserActivityLogger, Settings, Entities, Messages, Tablet, Overlays, MyAvatar */ (function() { // BEGIN LOCAL_SCOPE var tabletShown = false; @@ -65,8 +65,10 @@ hideTabletUI(); HMD.closeTablet(); } else if (HMD.showTablet && !tabletShown) { + UserActivityLogger.openedTablet(); showTabletUI(); } else if (!HMD.showTablet && tabletShown) { + UserActivityLogger.closedTablet(); hideTabletUI(); } } @@ -86,7 +88,6 @@ var accumulatedLevel = 0.0; // Note: Might have to tweak the following two based on the rate we're getting the data var AVERAGING_RATIO = 0.05; - var MIC_LEVEL_UPDATE_INTERVAL_MS = 100; // Calculate microphone level with the same scaling equation (log scale, exponentially averaged) in AvatarInputs and pal.js function getMicLevel() { From 6d1d36e0f0a01881b2033052367b690aa8d061ad Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Tue, 7 Feb 2017 10:22:43 -0800 Subject: [PATCH 22/48] fix for bad merge around cauterizedClusterMatrices --- libraries/render-utils/src/MeshPartPayload.cpp | 10 +--------- libraries/render-utils/src/MeshPartPayload.h | 4 +--- libraries/render-utils/src/Model.cpp | 2 +- 3 files changed, 3 insertions(+), 13 deletions(-) diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 17a8c07c39..333371ed76 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -370,22 +370,14 @@ void ModelMeshPartPayload::notifyLocationChanged() { } -void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices) { +void ModelMeshPartPayload::updateTransformForSkinnedMesh(const Transform& transform, const QVector& clusterMatrices) { _transform = transform; - _cauterizedTransform = transform; if (clusterMatrices.size() > 0) { _worldBound = _adjustedLocalBound; _worldBound.transform(_transform); if (clusterMatrices.size() == 1) { _transform = _transform.worldTransform(Transform(clusterMatrices[0])); - if (cauterizedClusterMatrices.size() != 0) { - _cauterizedTransform = _cauterizedTransform.worldTransform(Transform(cauterizedClusterMatrices[0])); - } else { - _cauterizedTransform = _transform; - } } } else { _worldBound = _localBound; diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index bdd9d737e9..c585c95025 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -90,8 +90,7 @@ public: void notifyLocationChanged() override; void updateTransformForSkinnedMesh(const Transform& transform, - const QVector& clusterMatrices, - const QVector& cauterizedClusterMatrices); + const QVector& clusterMatrices); float computeFadeAlpha() const; @@ -111,7 +110,6 @@ public: Model* _model; - Transform _cauterizedTransform; int _meshIndex; int _shapeID; diff --git a/libraries/render-utils/src/Model.cpp b/libraries/render-utils/src/Model.cpp index 53e57541c1..e1627f2fd6 100644 --- a/libraries/render-utils/src/Model.cpp +++ b/libraries/render-utils/src/Model.cpp @@ -248,7 +248,7 @@ void Model::updateRenderItems() { // update the model transform and bounding box for this render item. const Model::MeshState& state = data._model->_meshStates.at(data._meshIndex); - data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices, state.cauterizedClusterMatrices); + data.updateTransformForSkinnedMesh(modelTransform, state.clusterMatrices); } } }); From 13b2b6086f224fbcef336ddb6c153d2bb565f054 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 21 Feb 2017 19:47:26 +0100 Subject: [PATCH 23/48] Fix recorder.js playback in interface --- interface/src/avatar/MyAvatar.cpp | 24 +++++-- interface/src/avatar/MyAvatar.h | 1 + libraries/animation/src/Rig.cpp | 6 +- libraries/animation/src/Rig.h | 2 + libraries/audio-client/src/AudioClient.cpp | 62 +++++++++---------- libraries/audio-client/src/AudioClient.h | 13 ++-- libraries/audio/src/AudioInjectorOptions.cpp | 2 +- .../developer/utilities/record/recorder.js | 10 ++- 8 files changed, 71 insertions(+), 49 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 29f41c89fd..b9f673287b 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -148,13 +148,22 @@ MyAvatar::MyAvatar(RigPointer rig) : auto player = DependencyManager::get(); auto recorder = DependencyManager::get(); connect(player.data(), &Deck::playbackStateChanged, [=] { - if (player->isPlaying()) { + bool isPlaying = player->isPlaying(); + if (isPlaying) { auto recordingInterface = DependencyManager::get(); if (recordingInterface->getPlayFromCurrentLocation()) { setRecordingBasis(); } } else { clearRecordingBasis(); + useFullAvatarURL(_fullAvatarURLFromPreferences, _fullAvatarModelName); + } + + auto audioIO = DependencyManager::get(); + audioIO->setPlayingBackRecording(isPlaying); + + if (_rig) { + _rig->setEnableAnimations(!isPlaying); } }); @@ -180,8 +189,8 @@ MyAvatar::MyAvatar(RigPointer rig) : if (recordingInterface->getPlayerUseSkeletonModel() && dummyAvatar.getSkeletonModelURL().isValid() && (dummyAvatar.getSkeletonModelURL() != getSkeletonModelURL())) { - // FIXME - //myAvatar->useFullAvatarURL() + + setSkeletonModelURL(dummyAvatar.getSkeletonModelURL()); } if (recordingInterface->getPlayerUseDisplayName() && dummyAvatar.getDisplayName() != getDisplayName()) { @@ -204,6 +213,11 @@ MyAvatar::MyAvatar(RigPointer rig) : // head orientation _headData->setLookAtPosition(headData->getLookAtPosition()); } + + auto jointData = dummyAvatar.getRawJointData(); + if (jointData.length() > 0 && _rig) { + _rig->copyJointsFromJointData(jointData); + } }); connect(rig.get(), SIGNAL(onLoadComplete()), this, SIGNAL(onLoadComplete())); @@ -472,7 +486,9 @@ void MyAvatar::simulate(float deltaTime) { { PerformanceTimer perfTimer("joints"); // copy out the skeleton joints from the model - _rig->copyJointsIntoJointData(_jointData); + if (_rigEnabled) { + _rig->copyJointsIntoJointData(_jointData); + } } { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index c4fe86356d..5c2c0f2765 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -486,6 +486,7 @@ private: std::unordered_set _headBoneSet; RigPointer _rig; bool _prevShouldDrawHead; + bool _rigEnabled { true }; bool _enableDebugDrawDefaultPose { false }; bool _enableDebugDrawAnimPose { false }; diff --git a/libraries/animation/src/Rig.cpp b/libraries/animation/src/Rig.cpp index ac16b16c1d..07462e9878 100644 --- a/libraries/animation/src/Rig.cpp +++ b/libraries/animation/src/Rig.cpp @@ -483,6 +483,10 @@ void Rig::setEnableInverseKinematics(bool enable) { _enableInverseKinematics = enable; } +void Rig::setEnableAnimations(bool enable) { + _enabledAnimations = enable; +} + AnimPose Rig::getAbsoluteDefaultPose(int index) const { if (_animSkeleton && index >= 0 && index < _animSkeleton->getNumJoints()) { return _absoluteDefaultPoses[index]; @@ -907,7 +911,7 @@ void Rig::updateAnimations(float deltaTime, glm::mat4 rootTransform) { setModelOffset(rootTransform); - if (_animNode) { + if (_animNode && _enabledAnimations) { PerformanceTimer perfTimer("handleTriggers"); updateAnimationStateHandlers(); diff --git a/libraries/animation/src/Rig.h b/libraries/animation/src/Rig.h index aa091fe10c..78a669b249 100644 --- a/libraries/animation/src/Rig.h +++ b/libraries/animation/src/Rig.h @@ -210,6 +210,7 @@ public: void computeAvatarBoundingCapsule(const FBXGeometry& geometry, float& radiusOut, float& heightOut, glm::vec3& offsetOut) const; void setEnableInverseKinematics(bool enable); + void setEnableAnimations(bool enable); const glm::mat4& getGeometryToRigTransform() const { return _geometryToRigTransform; } @@ -314,6 +315,7 @@ protected: int32_t _numOverrides { 0 }; bool _lastEnableInverseKinematics { true }; bool _enableInverseKinematics { true }; + bool _enabledAnimations { true }; mutable uint32_t _jointNameWarningCount { 0 }; diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 2e532d67bf..0e2991ae30 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -39,13 +39,10 @@ #include #include #include -#include #include #include -#include #include -#include "PositionalAudioStream.h" #include "AudioClientLogging.h" #include "AudioLogging.h" @@ -151,17 +148,17 @@ static inline float convertToFloat(int16_t sample) { AudioClient::AudioClient() : AbstractAudioInterface(), _gate(this), - _audioInput(NULL), + _audioInput(nullptr), _desiredInputFormat(), _inputFormat(), _numInputCallbackBytes(0), - _audioOutput(NULL), + _audioOutput(nullptr), _desiredOutputFormat(), _outputFormat(), _outputFrameSize(0), _numOutputCallbackBytes(0), - _loopbackAudioOutput(NULL), - _loopbackOutputDevice(NULL), + _loopbackAudioOutput(nullptr), + _loopbackOutputDevice(nullptr), _inputRingBuffer(0), _localInjectorsStream(0), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), @@ -179,9 +176,9 @@ AudioClient::AudioClient() : _isNoiseGateEnabled(true), _reverb(false), _reverbOptions(&_scriptReverbOptions), - _inputToNetworkResampler(NULL), - _networkToOutputResampler(NULL), - _localToOutputResampler(NULL), + _inputToNetworkResampler(nullptr), + _networkToOutputResampler(nullptr), + _localToOutputResampler(nullptr), _localAudioThread(this), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), @@ -294,12 +291,12 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { IPropertyStore* pPropertyStore; pEndpoint->OpenPropertyStore(STGM_READ, &pPropertyStore); pEndpoint->Release(); - pEndpoint = NULL; + pEndpoint = nullptr; PROPVARIANT pv; PropVariantInit(&pv); HRESULT hr = pPropertyStore->GetValue(PKEY_Device_FriendlyName, &pv); pPropertyStore->Release(); - pPropertyStore = NULL; + pPropertyStore = nullptr; deviceName = QString::fromWCharArray((wchar_t*)pv.pwszVal); if (!IsWindows8OrGreater()) { // Windows 7 provides only the 31 first characters of the device name. @@ -313,9 +310,9 @@ QString friendlyNameForAudioDevice(IMMDevice* pEndpoint) { QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { QString deviceName; HRESULT hr = S_OK; - CoInitialize(NULL); - IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; - CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoInitialize(nullptr); + IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; + CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDevice(guid, &pEndpoint); if (hr == E_NOTFOUND) { @@ -325,7 +322,7 @@ QString AudioClient::friendlyNameForAudioDevice(wchar_t* guid) { deviceName = ::friendlyNameForAudioDevice(pEndpoint); } pMMDeviceEnumerator->Release(); - pMMDeviceEnumerator = NULL; + pMMDeviceEnumerator = nullptr; CoUninitialize(); return deviceName; } @@ -396,9 +393,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } } else { HRESULT hr = S_OK; - CoInitialize(NULL); - IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; - CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoInitialize(nullptr); + IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; + CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint); if (hr == E_NOTFOUND) { @@ -408,7 +405,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { deviceName = friendlyNameForAudioDevice(pEndpoint); } pMMDeviceEnumerator->Release(); - pMMDeviceEnumerator = NULL; + pMMDeviceEnumerator = nullptr; CoUninitialize(); } @@ -968,8 +965,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - - if (!_inputDevice) { + if (!_inputDevice || _playingBackRecording) { return; } @@ -1358,10 +1354,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn // That in turn causes it to be disconnected (see for example // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). _audioInput->stop(); - _inputDevice = NULL; + _inputDevice = nullptr; delete _audioInput; - _audioInput = NULL; + _audioInput = nullptr; _numInputCallbackBytes = 0; _inputAudioDeviceName = ""; @@ -1370,7 +1366,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (_inputToNetworkResampler) { // if we were using an input to network resampler, delete it here delete _inputToNetworkResampler; - _inputToNetworkResampler = NULL; + _inputToNetworkResampler = nullptr; } if (!inputDeviceInfo.isNull()) { @@ -1465,29 +1461,29 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutput->stop(); delete _audioOutput; - _audioOutput = NULL; + _audioOutput = nullptr; - _loopbackOutputDevice = NULL; + _loopbackOutputDevice = nullptr; delete _loopbackAudioOutput; - _loopbackAudioOutput = NULL; + _loopbackAudioOutput = nullptr; delete[] _outputMixBuffer; - _outputMixBuffer = NULL; + _outputMixBuffer = nullptr; delete[] _outputScratchBuffer; - _outputScratchBuffer = NULL; + _outputScratchBuffer = nullptr; delete[] _localOutputMixBuffer; - _localOutputMixBuffer = NULL; + _localOutputMixBuffer = nullptr; } if (_networkToOutputResampler) { // if we were using an input to network resampler, delete it here delete _networkToOutputResampler; - _networkToOutputResampler = NULL; + _networkToOutputResampler = nullptr; delete _localToOutputResampler; - _localToOutputResampler = NULL; + _localToOutputResampler = nullptr; } if (!outputDeviceInfo.isNull()) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 699ba71ef7..9dc86c1ecf 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -145,6 +145,8 @@ public: void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } + void setPlayingBackRecording(bool playingBackRecording) { _playingBackRecording = playingBackRecording; } + Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); void checkDevices(); @@ -326,14 +328,14 @@ private: // for local audio (used by audio injectors thread) float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; - float* _localOutputMixBuffer { NULL }; + float* _localOutputMixBuffer { nullptr }; AudioInjectorsThread _localAudioThread; Mutex _localAudioMutex; // for output audio (used by this thread) int _outputPeriod { 0 }; - float* _outputMixBuffer { NULL }; - int16_t* _outputScratchBuffer { NULL }; + float* _outputMixBuffer { nullptr }; + int16_t* _outputScratchBuffer { nullptr }; AudioLimiter _audioLimiter; @@ -367,13 +369,16 @@ private: QVector _inputDevices; QVector _outputDevices; - bool _hasReceivedFirstPacket = false; + bool _hasReceivedFirstPacket { false }; QVector _activeLocalAudioInjectors; + bool _playingBackRecording { false }; + CodecPluginPointer _codec; QString _selectedCodecName; Encoder* _encoder { nullptr }; // for outbound mic stream + Encoder* _playbackEncoder { nullptr }; QThread* _checkDevicesThread { nullptr }; }; diff --git a/libraries/audio/src/AudioInjectorOptions.cpp b/libraries/audio/src/AudioInjectorOptions.cpp index 1a92697828..0af74a796c 100644 --- a/libraries/audio/src/AudioInjectorOptions.cpp +++ b/libraries/audio/src/AudioInjectorOptions.cpp @@ -91,4 +91,4 @@ void injectorOptionsFromScriptValue(const QScriptValue& object, AudioInjectorOpt qCWarning(audio) << "Unknown audio injector option:" << it.name(); } } - } \ No newline at end of file +} diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index 083037461d..ea0a4505cc 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -1,3 +1,4 @@ +debugger; // // Recorder.js // examples @@ -12,14 +13,14 @@ HIFI_PUBLIC_BUCKET = "http://s3.amazonaws.com/hifi-public/"; Script.include("/~/system/libraries/toolBars.js"); -var recordingFile = "recording.rec"; +var recordingFile = "recording.hfr"; function setPlayerOptions() { Recording.setPlayFromCurrentLocation(true); Recording.setPlayerUseDisplayName(false); Recording.setPlayerUseAttachments(false); Recording.setPlayerUseHeadModel(false); - Recording.setPlayerUseSkeletonModel(false); + Recording.setPlayerUseSkeletonModel(true); } var windowDimensions = Controller.getViewportDimensions(); @@ -142,7 +143,6 @@ function setupTimer() { backgroundAlpha: 1.0, visible: true }); - } function updateTimer() { @@ -272,7 +272,7 @@ function mousePressEvent(event) { } } else if (loadIcon === toolBar.clicked(clickedOverlay)) { if (!Recording.isRecording() && !Recording.isPlaying()) { - recordingFile = Window.browse("Load recorcding from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); + recordingFile = Window.browse("Load recording from file", ".", "Recordings (*.hfr *.rec *.HFR *.REC)"); if (!(recordingFile === "null" || recordingFile === null || recordingFile === "")) { Recording.loadRecording(recordingFile); } @@ -345,5 +345,3 @@ Script.scriptEnding.connect(scriptEnding); // Should be called last to put everything into position moveUI(); - - From 7564b6f0f5690a223ccb0a8fd6bb6d3125ea1de0 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 21 Feb 2017 10:59:36 -0800 Subject: [PATCH 24/48] More consistent highlights/toggle for toolbar buttons When the "app" for a button is visible the button should become active. Also clicking on a highlighted/active button should close the associated "app". This should be noticeable on the following tablet apps. * audio * menu * people * market * users --- scripts/system/audio.js | 48 ++++++++++++++++--- scripts/system/marketplaces/marketplaces.js | 35 ++++++++++---- scripts/system/menu.js | 28 +++++++++-- scripts/system/pal.js | 52 +++++++++++++-------- scripts/system/tablet-goto.js | 27 +++++++++-- scripts/system/tablet-users.js | 31 +++++++++--- 6 files changed, 170 insertions(+), 51 deletions(-) diff --git a/scripts/system/audio.js b/scripts/system/audio.js index c0fdb43b40..6e7e95d659 100644 --- a/scripts/system/audio.js +++ b/scripts/system/audio.js @@ -16,19 +16,51 @@ var TABLET_BUTTON_NAME = "AUDIO"; var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; +var MUTE_ICONS = { + icon: "icons/tablet-icons/mic-mute-i.svg", + activeIcon: "icons/tablet-icons/mic-mute-a.svg" +}; + +var UNMUTE_ICONS = { + icon: "icons/tablet-icons/mic-unmute-i.svg", + activeIcon: "icons/tablet-icons/mic-unmute-a.svg" +}; + function onMuteToggled() { - button.editProperties({ isActive: AudioDevice.getMuted() }); + if (AudioDevice.getMuted()) { + button.editProperties(MUTE_ICONS); + } else { + button.editProperties(UNMUTE_ICONS); + } } -function onClicked(){ - var entity = HMD.tabletID; - Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); - tablet.gotoMenuScreen("Audio"); + +var shouldActivateButton = false; +var onAudioScreen = false; + +function onClicked() { + if (onAudioScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, { textures: JSON.stringify({ "tex.close": HOME_BUTTON_TEXTURE }) }); + shouldActivateButton = true; + tablet.gotoMenuScreen("Audio"); + onAudioScreen = true; + } +} + +function onScreenChanged(type, url) { + // for toolbar mode: change button to active when window is first openend, false otherwise. + button.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onAudioScreen = false; } var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ - icon: "icons/tablet-icons/mic-unmute-i.svg", - activeIcon: "icons/tablet-icons/mic-mute-a.svg", + icon: AudioDevice.getMuted() ? MUTE_ICONS.icon : UNMUTE_ICONS.icon, + activeIcon: AudioDevice.getMuted() ? MUTE_ICONS.activeIcon : UNMUTE_ICONS.activeIcon, text: TABLET_BUTTON_NAME, sortOrder: 1 }); @@ -36,10 +68,12 @@ var button = tablet.addButton({ onMuteToggled(); button.clicked.connect(onClicked); +tablet.screenChanged.connect(onScreenChanged); AudioDevice.muteToggled.connect(onMuteToggled); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); + tablet.screenChanged.disconnect(onScreenChanged); AudioDevice.muteToggled.disconnect(onMuteToggled); tablet.removeButton(button); }); diff --git a/scripts/system/marketplaces/marketplaces.js b/scripts/system/marketplaces/marketplaces.js index c5ce5a634b..68da7696be 100644 --- a/scripts/system/marketplaces/marketplaces.js +++ b/scripts/system/marketplaces/marketplaces.js @@ -52,10 +52,16 @@ function onMessageBoxClosed(id, button) { Window.messageBoxClosed.connect(onMessageBoxClosed); +var shouldActivateButton = false; +var onMarketplaceScreen = false; + function showMarketplace() { UserActivityLogger.openedMarketplace(); + shouldActivateButton = true; tablet.gotoWebScreen(MARKETPLACE_URL_INITIAL, MARKETPLACES_INJECT_SCRIPT_URL); + onMarketplaceScreen = true; + tablet.webEventReceived.connect(function (message) { if (message === GOTO_DIRECTORY) { @@ -98,15 +104,10 @@ function showMarketplace() { }); } -function toggleMarketplace() { - var entity = HMD.tabletID; - Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); - showMarketplace(); -} - var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var marketplaceButton = tablet.addButton({ icon: "icons/tablet-icons/market-i.svg", + activeIcon: "icons/tablet-icons/market-a.svg", text: "MARKET", sortOrder: 9 }); @@ -117,16 +118,30 @@ function onCanWriteAssetsChanged() { } function onClick() { - toggleMarketplace(); + if (onMarketplaceScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); + showMarketplace(); + } +} + +function onScreenChanged(type, url) { + // for toolbar mode: change button to active when window is first openend, false otherwise. + marketplaceButton.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onMarketplaceScreen = false; } marketplaceButton.clicked.connect(onClick); +tablet.screenChanged.connect(onScreenChanged); Entities.canWriteAssetsChanged.connect(onCanWriteAssetsChanged); Script.scriptEnding.connect(function () { - if (tablet) { - tablet.removeButton(marketplaceButton); - } + tablet.removeButton(marketplaceButton); + tablet.screenChanged.disconnect(onScreenChanged); Entities.canWriteAssetsChanged.disconnect(onCanWriteAssetsChanged); }); diff --git a/scripts/system/menu.js b/scripts/system/menu.js index 13c6ce1e0d..1d5f8bccd6 100644 --- a/scripts/system/menu.js +++ b/scripts/system/menu.js @@ -10,26 +10,46 @@ // var HOME_BUTTON_TEXTURE = "http://hifi-content.s3.amazonaws.com/alan/dev/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; -//var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; +// var HOME_BUTTON_TEXTURE = Script.resourcesPath() + "meshes/tablet-with-home-button.fbx/tablet-with-home-button.fbm/button-root.png"; (function() { var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ icon: "icons/tablet-icons/menu-i.svg", + activeIcon: "icons/tablet-icons/menu-a.svg", text: "MENU", sortOrder: 3 }); + var shouldActivateButton = false; + var onMenuScreen = false; + function onClicked() { - var entity = HMD.tabletID; - Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); - tablet.gotoMenuScreen(); + if (onMenuScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + var entity = HMD.tabletID; + Entities.editEntity(entity, {textures: JSON.stringify({"tex.close": HOME_BUTTON_TEXTURE})}); + shouldActivateButton = true; + tablet.gotoMenuScreen(); + onMenuScreen = true; + } + } + + function onScreenChanged(type, url) { + // for toolbar mode: change button to active when window is first openend, false otherwise. + button.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onMenuScreen = false; } button.clicked.connect(onClicked); + tablet.screenChanged.connect(onScreenChanged); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); tablet.removeButton(button); + tablet.screenChanged.disconnect(onScreenChanged); }); }()); diff --git a/scripts/system/pal.js b/scripts/system/pal.js index d47544e0f0..36ecc1f084 100644 --- a/scripts/system/pal.js +++ b/scripts/system/pal.js @@ -477,23 +477,17 @@ var button; var buttonName = "PEOPLE"; var tablet = null; -function onTabletScreenChanged(type, url) { - if (type !== "QML" || url !== "../Pal.qml") { - off(); - } -} - function startup() { tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); button = tablet.addButton({ text: buttonName, icon: "icons/tablet-icons/people-i.svg", + activeIcon: "icons/tablet-icons/people-a.svg", sortOrder: 7 }); tablet.fromQml.connect(fromQml); button.clicked.connect(onTabletButtonClicked); tablet.screenChanged.connect(onTabletScreenChanged); - Users.usernameFromIDReply.connect(usernameFromIDReply); Window.domainChanged.connect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.connect(clearLocalQMLDataAndClosePAL); @@ -524,17 +518,39 @@ function off() { Users.requestsDomainListData = false; } +var onPalScreen = false; +var shouldActivateButton = false; + function onTabletButtonClicked() { - tablet.loadQMLSource("../Pal.qml"); - Users.requestsDomainListData = true; - populateUserList(); - isWired = true; - Script.update.connect(updateOverlays); - Controller.mousePressEvent.connect(handleMouseEvent); - Controller.mouseMoveEvent.connect(handleMouseMoveEvent); - triggerMapping.enable(); - triggerPressMapping.enable(); - audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); + if (onPalScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + shouldActivateButton = true; + tablet.loadQMLSource("../Pal.qml"); + onPalScreen = true; + Users.requestsDomainListData = true; + populateUserList(); + isWired = true; + Script.update.connect(updateOverlays); + Controller.mousePressEvent.connect(handleMouseEvent); + Controller.mouseMoveEvent.connect(handleMouseMoveEvent); + triggerMapping.enable(); + triggerPressMapping.enable(); + audioTimer = createAudioInterval(conserveResources ? AUDIO_LEVEL_CONSERVED_UPDATE_INTERVAL_MS : AUDIO_LEVEL_UPDATE_INTERVAL_MS); + } +} + +function onTabletScreenChanged(type, url) { + // for toolbar mode: change button to active when window is first openend, false otherwise. + button.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onPalScreen = false; + + // disable sphere overlays when not on pal screen. + if (type !== "QML" || url !== "../Pal.qml") { + off(); + } } // @@ -621,14 +637,12 @@ function shutdown() { button.clicked.disconnect(onTabletButtonClicked); tablet.removeButton(button); tablet.screenChanged.disconnect(onTabletScreenChanged); - Users.usernameFromIDReply.disconnect(usernameFromIDReply); Window.domainChanged.disconnect(clearLocalQMLDataAndClosePAL); Window.domainConnectionRefused.disconnect(clearLocalQMLDataAndClosePAL); Messages.subscribe(CHANNEL); Messages.messageReceived.disconnect(receiveMessage); Users.avatarDisconnected.disconnect(avatarDisconnected); - off(); } diff --git a/scripts/system/tablet-goto.js b/scripts/system/tablet-goto.js index 6c3e12cd9b..eb95d9d8a3 100644 --- a/scripts/system/tablet-goto.js +++ b/scripts/system/tablet-goto.js @@ -14,10 +14,27 @@ (function() { // BEGIN LOCAL_SCOPE var gotoQmlSource = "TabletAddressDialog.qml"; var buttonName = "GOTO"; + var onGotoScreen = false; + var shouldActivateButton = false; - function onClicked(){ - tablet.loadQMLSource(gotoQmlSource); + function onClicked() { + if (onGotoScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + shouldActivateButton = true; + tablet.loadQMLSource(gotoQmlSource); + onGotoScreen = true; + } } + + function onScreenChanged(type, url) { + // for toolbar mode: change button to active when window is first openend, false otherwise. + button.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onGotoScreen = false; + } + var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ icon: "icons/tablet-icons/goto-i.svg", @@ -27,12 +44,12 @@ }); button.clicked.connect(onClicked); + tablet.screenChanged.connect(onScreenChanged); Script.scriptEnding.connect(function () { button.clicked.disconnect(onClicked); - if (tablet) { - tablet.removeButton(button); - } + tablet.removeButton(button); + tablet.screenChanged.disconnect(onScreenChanged); }); }()); // END LOCAL_SCOPE diff --git a/scripts/system/tablet-users.js b/scripts/system/tablet-users.js index ce50c4686d..8e89ac74b7 100644 --- a/scripts/system/tablet-users.js +++ b/scripts/system/tablet-users.js @@ -1,7 +1,7 @@ "use strict"; // -// users.js +// tablet-users.js // // Created by Faye Li on 18 Jan 2017. // Copyright 2017 High Fidelity, Inc. @@ -36,16 +36,34 @@ var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ icon: "icons/tablet-icons/users-i.svg", + activeIcon: "icons/tablet-icons/users-a.svg", text: "USERS", sortOrder: 11 }); + var onUsersScreen = false; + var shouldActivateButton = false; + function onClicked() { - var tabletEntity = HMD.tabletID; - if (tabletEntity) { - Entities.editEntity(tabletEntity, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); + if (onUsersScreen) { + // for toolbar-mode: go back to home screen, this will close the window. + tablet.gotoHomeScreen(); + } else { + var tabletEntity = HMD.tabletID; + if (tabletEntity) { + Entities.editEntity(tabletEntity, {textures: JSON.stringify({"tex.close" : HOME_BUTTON_TEXTURE})}); + } + shouldActivateButton = true; + tablet.gotoWebScreen(USERS_URL); + onUsersScreen = true; } - tablet.gotoWebScreen(USERS_URL); + } + + function onScreenChanged() { + // for toolbar mode: change button to active when window is first openend, false otherwise. + button.editProperties({isActive: shouldActivateButton}); + shouldActivateButton = false; + onUsersScreen = false; } function onWebEventReceived(event) { @@ -88,12 +106,13 @@ // update your visibility (all, friends, or none) myVisibility = event.data.visibility; GlobalServices.findableBy = myVisibility; - } + } } } button.clicked.connect(onClicked); tablet.webEventReceived.connect(onWebEventReceived); + tablet.screenChanged.connect(onScreenChanged); function cleanup() { button.clicked.disconnect(onClicked); From 15518e9aa17c970187ba368dc851d7ac2ecc6798 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 21 Feb 2017 20:20:23 +0100 Subject: [PATCH 25/48] remove debugger line --- scripts/developer/utilities/record/recorder.js | 1 - 1 file changed, 1 deletion(-) diff --git a/scripts/developer/utilities/record/recorder.js b/scripts/developer/utilities/record/recorder.js index ea0a4505cc..0e335116d5 100644 --- a/scripts/developer/utilities/record/recorder.js +++ b/scripts/developer/utilities/record/recorder.js @@ -1,4 +1,3 @@ -debugger; // // Recorder.js // examples From 72eb797910733125b6e844de7350260f17258791 Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Tue, 21 Feb 2017 20:22:29 +0100 Subject: [PATCH 26/48] remove unused encoder (was for testing) --- libraries/audio-client/src/AudioClient.h | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 9dc86c1ecf..739c4b459c 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -378,7 +378,6 @@ private: CodecPluginPointer _codec; QString _selectedCodecName; Encoder* _encoder { nullptr }; // for outbound mic stream - Encoder* _playbackEncoder { nullptr }; QThread* _checkDevicesThread { nullptr }; }; From 2017ea4491a91027cc6d3c498b346bc9d36e6c8e Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 21 Feb 2017 14:18:00 -0800 Subject: [PATCH 27/48] hacking on using sorted avatars for bandwidth budget --- .../src/avatars/AvatarMixerClientData.cpp | 12 ++- .../src/avatars/AvatarMixerClientData.h | 11 +++ .../src/avatars/AvatarMixerSlave.cpp | 76 ++++++++++++++++++- interface/src/avatar/Avatar.cpp | 5 -- interface/src/avatar/Avatar.h | 1 - interface/src/avatar/AvatarManager.cpp | 66 ++++------------ libraries/avatars/src/AvatarData.cpp | 63 +++++++++++++++ libraries/avatars/src/AvatarData.h | 27 ++++++- libraries/avatars/src/AvatarHashMap.cpp | 1 + libraries/avatars/src/AvatarHashMap.h | 1 - 10 files changed, 199 insertions(+), 64 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index df6a64e874..1df6c24a60 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -74,14 +74,22 @@ bool AvatarMixerClientData::checkAndSetHasReceivedFirstPacketsFrom(const QUuid& return true; } +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); + if (nodeMatch != _lastBroadcastTimes.end()) { + return nodeMatch->second; + } + return 0; +} + uint16_t AvatarMixerClientData::getLastBroadcastSequenceNumber(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastSequenceNumbers.find(nodeUUID); if (nodeMatch != _lastBroadcastSequenceNumbers.end()) { return nodeMatch->second; - } else { - return 0; } + return 0; } void AvatarMixerClientData::ignoreOther(SharedNodePointer self, SharedNodePointer other) { diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 87f7bb9cc9..7eea31c9ab 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -43,6 +43,7 @@ public: int parseData(ReceivedMessage& message) override; AvatarData& getAvatar() { return *_avatar; } const AvatarData* getConstAvatarData() const { return _avatar.get(); } + AvatarSharedPointer getAvatarSharedPointer() const { return _avatar; } bool checkAndSetHasReceivedFirstPacketsFrom(const QUuid& uuid); @@ -51,6 +52,12 @@ public: { _lastBroadcastSequenceNumbers[nodeUUID] = sequenceNumber; } Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } + uint64_t getLastBroadcastTime(const QUuid& nodeUUID) const; + void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { + _lastBroadcastTimes[nodeUUID] = broadcastTime; + } + Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } + uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } @@ -106,6 +113,9 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } + ViewFrustum getViewFrustom() const { return _currentViewFrustum; } + + quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 result = 0; if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) { @@ -134,6 +144,7 @@ private: uint16_t _lastReceivedSequenceNumber { 0 }; std::unordered_map _lastBroadcastSequenceNumbers; std::unordered_set _hasReceivedFirstPacketsFrom; + std::unordered_map _lastBroadcastTimes; // this is a map of the last time we encoded an "other" avatar for // sending to "this" node diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 46133a221f..4c95c96483 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -187,9 +187,75 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + + QList avatarList; + std::unordered_map avatarDataToNodes; + + //qDebug() << "------------------------------"; + int listItem = 0; + std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + if (otherNodeData) { + listItem++; + AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); + avatarList << otherAvatar; + avatarDataToNodes[otherAvatar] = otherNode; + /* + qDebug() << "listItem [" << listItem << "] " + << "otherNode:" << otherNode.data() + << "otherNode->getUUID():" << otherNode->getUUID() + << "otherNodeData:" << otherNodeData + << "otherAvatar:" << otherAvatar.get(); + + qDebug() << "avatarDataToNodes[" << otherAvatar.get() << "]=" << otherNode.data(); + */ + } + + }); + /* + qDebug() << "------------------------------"; + qDebug() << "avatarList.size:" << avatarList.size(); + qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); + */ + + ViewFrustum cameraView = nodeData->getViewFrustom(); + std::priority_queue sortedAvatars = AvatarData::sortAvatars( + avatarList, cameraView, + + [&](AvatarSharedPointer avatar)->uint64_t{ + auto avatarNode = avatarDataToNodes[avatar]; + if (avatarNode) { + return nodeData->getLastBroadcastTime(avatarNode->getUUID()); + } + return 0; // ??? + }, + + [this](AvatarSharedPointer avatar)->bool{ + // FIXME -- when to ignore this node + return false; + }); + + // this is an AGENT we have received head data from // send back a packet with other active node data to this node - std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + //std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { + + int avatarRank = 0; + while (!sortedAvatars.empty()) { + AvatarPriority sortData = sortedAvatars.top(); + sortedAvatars.pop(); + const auto& avatarData = sortData.avatar; + avatarRank++; + + auto otherNode = avatarDataToNodes[avatarData]; + + //qDebug() << "otherNode (" << otherNode.data() << ")= avatarDataToNodes[" << avatarData.get() << "]"; + + if (!otherNode) { + //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << ":" << avatarData.get() << "... otherNode unknown!!"; + continue; + } + //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << "] avatarData: " << avatarData.get() << " otherNode:" << otherNode->getUUID() << " ... "; bool shouldConsider = false; quint64 startIgnoreCalculation = usecTimestampNow(); @@ -286,7 +352,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // are out of view, this also appears to disable this random distribution. if (distanceToAvatar != 0.0f && !getsOutOfView - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar)) { + // -- && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... no longer doing random + ) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); @@ -395,6 +462,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), otherNodeData->getLastReceivedSequenceNumber()); + // remember the last time we sent details about this other node to the receiver + nodeData->setLastBroadcastTime(otherNode->getUUID(), start); + } } @@ -406,7 +476,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { } } } - }); + }; quint64 startPacketSending = usecTimestampNow(); diff --git a/interface/src/avatar/Avatar.cpp b/interface/src/avatar/Avatar.cpp index ed8f083a41..64ecb1dc29 100644 --- a/interface/src/avatar/Avatar.cpp +++ b/interface/src/avatar/Avatar.cpp @@ -334,11 +334,6 @@ void Avatar::updateAvatarEntities() { setAvatarEntityDataChanged(false); } -bool Avatar::shouldDie() const { - const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; - return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; -} - void Avatar::simulate(float deltaTime, bool inView) { PROFILE_RANGE(simulation, "simulate"); diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 80d387fd33..53f0562bdd 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -178,7 +178,6 @@ public: uint64_t getLastRenderUpdateTime() const { return _lastRenderUpdateTime; } void setLastRenderUpdateTime(uint64_t time) { _lastRenderUpdateTime = time; } - bool shouldDie() const; void animateScaleChanges(float deltaTime); void setTargetScale(float targetScale) override; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index c3fc974365..1a9866aae4 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -148,16 +148,6 @@ float AvatarManager::getAvatarSimulationRate(const QUuid& sessionID, const QStri } - -class AvatarPriority { -public: - AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {} - AvatarSharedPointer avatar; - float priority; - // NOTE: we invert the less-than operator to sort high priorities to front - bool operator<(const AvatarPriority& other) const { return priority > other.priority; } -}; - void AvatarManager::updateOtherAvatars(float deltaTime) { // lock the hash for read to check the size QReadLocker lock(&_hashLock); @@ -173,57 +163,31 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { QList avatarList = avatarMap.values(); ViewFrustum cameraView; qApp->copyDisplayViewFrustum(cameraView); - glm::vec3 frustumCenter = cameraView.getPosition(); - const float OUT_OF_VIEW_PENALTY = -10.0; + std::priority_queue sortedAvatars = AvatarData::sortAvatars( + avatarList, cameraView, - std::priority_queue sortedAvatars; - { - PROFILE_RANGE(simulation, "sort"); - for (int32_t i = 0; i < avatarList.size(); ++i) { - const auto& avatar = std::static_pointer_cast(avatarList.at(i)); - if (avatar == _myAvatar || !avatar->isInitialized()) { + [](AvatarSharedPointer avatar)->uint64_t{ + return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); + }, + + [this](AvatarSharedPointer avatar)->bool{ + const auto& castedAvatar = std::static_pointer_cast(avatar); + if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) { // DO NOT update _myAvatar! Its update has already been done earlier in the main loop. // DO NOT update or fade out uninitialized Avatars - continue; + return true; // ignore it } if (avatar->shouldDie()) { removeAvatar(avatar->getID()); - continue; + return true; // ignore it } if (avatar->isDead()) { - continue; + return true; // ignore it } - // priority = weighted linear combination of: - // (a) apparentSize - // (b) proximity to center of view - // (c) time since last update - // (d) TIME_PENALTY to help recently updated entries sort toward back - glm::vec3 avatarPosition = avatar->getPosition(); - glm::vec3 offset = avatarPosition - frustumCenter; - float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero - float radius = avatar->getBoundingRadius(); - const glm::vec3& forward = cameraView.getDirection(); - float apparentSize = radius / distance; - float cosineAngle = glm::length(offset - glm::dot(offset, forward) * forward) / distance; - const float TIME_PENALTY = 0.080f; // seconds - float age = (float)(startTime - avatar->getLastRenderUpdateTime()) / (float)(USECS_PER_SECOND) - TIME_PENALTY; - // NOTE: we are adding values of different units to get a single measure of "priority". - // Thus we multiply each component by a conversion "weight" that scales its units - // relative to the others. These weights are pure magic tuning and are hard coded in the - // relation below: (hint: unitary weights are not explicityly shown) - float priority = apparentSize + 0.25f * cosineAngle + age; - - // decrement priority of avatars outside keyhole - if (distance > cameraView.getCenterRadius()) { - if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { - priority += OUT_OF_VIEW_PENALTY; - } - } - sortedAvatars.push(AvatarPriority(avatar, priority)); - } - } + return false; + }); render::PendingChanges pendingChanges; const uint64_t RENDER_UPDATE_BUDGET = 1500; // usec @@ -256,7 +220,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t now = usecTimestampNow(); if (now < renderExpiry) { // we're within budget - const float OUT_OF_VIEW_THRESHOLD = 0.5f * OUT_OF_VIEW_PENALTY; + const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; avatar->simulate(deltaTime, inView); avatar->updateRenderItem(pendingChanges); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2263964e8f..dffdc8c1ac 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -2309,3 +2310,65 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra vec3FromScriptValue(intersection, value.intersection); } } + +const float AvatarData::OUT_OF_VIEW_PENALTY = -10.0f; + +std::priority_queue AvatarData::sortAvatars( + QList avatarList, + const ViewFrustum& cameraView, + std::function lastUpdated, + std::function shouldIgnore) { + + uint64_t startTime = usecTimestampNow(); + + glm::vec3 frustumCenter = cameraView.getPosition(); + + std::priority_queue sortedAvatars; + { + PROFILE_RANGE(simulation, "sort"); + for (int32_t i = 0; i < avatarList.size(); ++i) { + const auto& avatar = avatarList.at(i); + + // FIXME - probably some lambda that allows the caller to reject some avatars + + if (shouldIgnore(avatar)) { + continue; + } + + // priority = weighted linear combination of: + // (a) apparentSize + // (b) proximity to center of view + // (c) time since last update + // (d) TIME_PENALTY to help recently updated entries sort toward back + glm::vec3 avatarPosition = avatar->getPosition(); + glm::vec3 offset = avatarPosition - frustumCenter; + float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero + + // FIXME - AvatarData has something equivolent to this + float radius = 1.0f; // avatar->getBoundingRadius(); + + const glm::vec3& forward = cameraView.getDirection(); + float apparentSize = radius / distance; + float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance; + + // FIXME - probably some lambda that allows the caller to specify the "updated since" + uint64_t lastUpdatedTime = lastUpdated(avatar); // avatar->getLastRenderUpdateTime() + float age = (float)(startTime - lastUpdatedTime) / (float)(USECS_PER_SECOND); + + // NOTE: we are adding values of different units to get a single measure of "priority". + // Thus we multiply each component by a conversion "weight" that scales its units + // relative to the others. These weights are pure magic tuning and are hard coded in the + // relation below: (hint: unitary weights are not explicityly shown) + float priority = apparentSize + 0.25f * cosineAngle + age; + + // decrement priority of avatars outside keyhole + if (distance > cameraView.getCenterRadius()) { + if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { + priority += OUT_OF_VIEW_PENALTY; + } + } + sortedAvatars.push(AvatarPriority(avatar, priority)); + } + } + return sortedAvatars; +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 264da75de2..71ade436ff 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -14,6 +14,8 @@ #include #include +#include + /* VS2010 defines stdint.h, but not inttypes.h */ #if defined(_MSC_VER) typedef signed char int8_t; @@ -57,6 +59,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include "AABox.h" #include "HeadData.h" @@ -304,6 +307,14 @@ public: RateCounter<> jointDataRate; }; +class AvatarPriority { +public: + AvatarPriority(AvatarSharedPointer a, float p) : avatar(a), priority(p) {} + AvatarSharedPointer avatar; + float priority; + // NOTE: we invert the less-than operator to sort high priorities to front + bool operator<(const AvatarPriority& other) const { return priority < other.priority; } +}; class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT @@ -539,7 +550,7 @@ public: void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } - const AABox& getLocalAABox() const { return _localAABox; } + //const AABox& getLocalAABox() const { return _localAABox; } int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); } int getAverageBytesReceivedPerSecond() const; @@ -578,6 +589,20 @@ public: } + bool shouldDie() const { + const qint64 AVATAR_SILENCE_THRESHOLD_USECS = 5 * USECS_PER_SECOND; + return _owningAvatarMixer.isNull() || getUsecsSinceLastUpdate() > AVATAR_SILENCE_THRESHOLD_USECS; + } + + static const float OUT_OF_VIEW_PENALTY; + + static std::priority_queue sortAvatars( + QList avatarList, + const ViewFrustum& cameraView, + std::function lastUpdated, + std::function shouldIgnore); + + public slots: void sendAvatarDataPacket(); void sendIdentityPacket(); diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 00c515a635..cdbf5f2a85 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -190,3 +190,4 @@ void AvatarHashMap::sessionUUIDChanged(const QUuid& sessionUUID, const QUuid& ol _lastOwnerSessionUUID = oldUUID; emit avatarSessionChangedEvent(sessionUUID, oldUUID); } + diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index dd097aef22..104ac83261 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -27,7 +27,6 @@ #include "AvatarData.h" - class AvatarHashMap : public QObject, public Dependency { Q_OBJECT SINGLETON_DEPENDENCY From 4866be1f522eb56c93fbbcb705da6a864097f588 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Tue, 21 Feb 2017 16:01:46 -0800 Subject: [PATCH 28/48] More consistent tablet angle when first creating the tablet. The tablet should more constantly be placed above your hand, while facing your head. It fixes the issue where the tablet would appear almost horizontal when your hand was close to your HMD and at eye level. --- scripts/system/libraries/WebTablet.js | 28 ++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js index 74bbd788be..618b70ff20 100644 --- a/scripts/system/libraries/WebTablet.js +++ b/scripts/system/libraries/WebTablet.js @@ -49,24 +49,30 @@ function calcSpawnInfo(hand, height) { var handController = getControllerWorldLocation(hand, true); var controllerPosition = handController.position; - // compute the angle of the chord with length (height / 2) - var theta = Math.asin(height / (2 * Vec3.distance(headPos, controllerPosition))); + // base of the tablet is slightly above controller position + var TABLET_BASE_DISPLACEMENT = {x: 0, y: 0.1, z: 0}; + var tabletBase = Vec3.sum(controllerPosition, TABLET_BASE_DISPLACEMENT); - // then we can use this angle to rotate the vector between the HMD position and the center of the tablet. - // this vector, u, will become our new look at direction. - var d = Vec3.normalize(Vec3.subtract(headPos, controllerPosition)); + var d = Vec3.subtract(headPos, tabletBase); + var theta = Math.acos(d.y / Vec3.length(d)); + d.y = 0; + if (Vec3.length(d) < 0.0001) { + d = {x: 1, y: 0, z: 0}; + } else { + d = Vec3.normalize(d); + } var w = Vec3.normalize(Vec3.cross(Y_AXIS, d)); - var q = Quat.angleAxis(theta * (180 / Math.PI), w); + var ANGLE_OFFSET = 25; + var q = Quat.angleAxis(theta * (180 / Math.PI) - (90 - ANGLE_OFFSET), w); var u = Vec3.multiplyQbyV(q, d); // use u to compute a full lookAt quaternion. - var lookAtRot = Quat.lookAt(controllerPosition, Vec3.sum(controllerPosition, u), Y_AXIS); - - // adjust the tablet position by a small amount. - var yDisplacement = (height / 2) + 0.1; + var lookAtRot = Quat.lookAt(tabletBase, Vec3.sum(tabletBase, u), Y_AXIS); + var yDisplacement = (height / 2); var zDisplacement = 0.05; var tabletOffset = Vec3.multiplyQbyV(lookAtRot, {x: 0, y: yDisplacement, z: zDisplacement}); - finalPosition = Vec3.sum(controllerPosition, tabletOffset); + finalPosition = Vec3.sum(tabletBase, tabletOffset); + return { position: finalPosition, rotation: lookAtRot From 8cc0b383c4c28d27de675a50465f17d8328ab307 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Tue, 21 Feb 2017 16:22:22 -0800 Subject: [PATCH 29/48] wire up bandwidth buget to sorting --- .../src/avatars/AvatarMixerSlave.cpp | 23 ++++++++++++++----- libraries/avatars/src/AvatarData.cpp | 2 ++ .../developer/debugging/debugAvatarMixer.js | 10 +++++--- 3 files changed, 26 insertions(+), 9 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 4c95c96483..5c71b88af9 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -213,11 +213,15 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { }); /* - qDebug() << "------------------------------"; qDebug() << "avatarList.size:" << avatarList.size(); qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); */ + //qDebug() << "------------------------------"; + AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); + + //qDebug() << "thisAvatar:" << thisAvatar.get(); + ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, @@ -230,10 +234,10 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { return 0; // ??? }, - [this](AvatarSharedPointer avatar)->bool{ - // FIXME -- when to ignore this node - return false; + [thisAvatar](AvatarSharedPointer avatar)->bool{ + return (avatar == thisAvatar); // ignore ourselves... }); + //qDebug() << "------------------------------"; // this is an AGENT we have received head data from @@ -269,6 +273,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldConsider = false; + //qDebug() << "shouldConsider = false ... line:" << __LINE__; } else { const AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); @@ -304,6 +309,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(node, otherNode); shouldConsider = getsAnyIgnored; + //qDebug() << "shouldConsider = getsAnyIgnored " << getsAnyIgnored << " ... line:" << __LINE__; + } } // Not close enough to ignore @@ -352,12 +359,13 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // are out of view, this also appears to disable this random distribution. if (distanceToAvatar != 0.0f && !getsOutOfView - // -- && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... no longer doing random + && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... we don't want to do this random stuff ) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; + //qDebug() << "shouldConsider = false ... line:" << __LINE__; } if (shouldConsider) { @@ -380,7 +388,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + //qDebug() << "shouldConsider = false ... line:" << __LINE__; + } + else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; } @@ -399,6 +409,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; + //qDebug() << "shouldConsider = false ... line:" << __LINE__; } if (shouldConsider) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index dffdc8c1ac..9416216c5a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2367,6 +2367,8 @@ std::priority_queue AvatarData::sortAvatars( priority += OUT_OF_VIEW_PENALTY; } } + + //qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age; sortedAvatars.push(AvatarPriority(avatar, priority)); } } diff --git a/scripts/developer/debugging/debugAvatarMixer.js b/scripts/developer/debugging/debugAvatarMixer.js index ebd43fc2f0..90f2de13a9 100644 --- a/scripts/developer/debugging/debugAvatarMixer.js +++ b/scripts/developer/debugging/debugAvatarMixer.js @@ -58,11 +58,14 @@ function updateOverlays() { // setup a position for the overlay that is just above this avatar's head var overlayPosition = avatar.getJointPosition("Head"); - overlayPosition.y += 1.05; + overlayPosition.y += 1.15; + + var rows = 8; var text = avatarID + "\n" +"--- Data from Mixer ---\n" +"All: " + AvatarManager.getAvatarDataRate(avatarID).toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID).toFixed(2) + "hz)" + "\n" + /* +" GP: " + AvatarManager.getAvatarDataRate(avatarID,"globalPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"globalPosition").toFixed(2) + "hz)" + "\n" +" LP: " + AvatarManager.getAvatarDataRate(avatarID,"localPosition").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"localPosition").toFixed(2) + "hz)" + "\n" +" BB: " + AvatarManager.getAvatarDataRate(avatarID,"avatarBoundingBox").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"avatarBoundingBox").toFixed(2) + "hz)" + "\n" @@ -74,11 +77,12 @@ function updateOverlays() { +" AF: " + AvatarManager.getAvatarDataRate(avatarID,"additionalFlags").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"additionalFlags").toFixed(2) + "hz)" + "\n" +" PI: " + AvatarManager.getAvatarDataRate(avatarID,"parentInfo").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"parentInfo").toFixed(2) + "hz)" + "\n" +" FT: " + AvatarManager.getAvatarDataRate(avatarID,"faceTracker").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"faceTracker").toFixed(2) + "hz)" + "\n" + */ +" JD: " + AvatarManager.getAvatarDataRate(avatarID,"jointData").toFixed(2) + "kbps (" + AvatarManager.getAvatarUpdateRate(avatarID,"jointData").toFixed(2) + "hz)" + "\n" +"--- Simulation ---\n" +"All: " + AvatarManager.getAvatarSimulationRate(avatarID,"avatar").toFixed(2) + "hz \n" +" inView: " + AvatarManager.getAvatarSimulationRate(avatarID,"avatarInView").toFixed(2) + "hz \n" - +" SM: " + AvatarManager.getAvatarSimulationRate(avatarID,"skeletonModel").toFixed(2) + "hz \n" + //+" SM: " + AvatarManager.getAvatarSimulationRate(avatarID,"skeletonModel").toFixed(2) + "hz \n" +" JD: " + AvatarManager.getAvatarSimulationRate(avatarID,"jointData").toFixed(2) + "hz \n" if (avatarID in debugOverlays) { @@ -93,7 +97,7 @@ function updateOverlays() { position: overlayPosition, dimensions: { x: 1.25, - y: 19 * 0.13 + y: rows * 0.13 }, lineHeight: 0.1, font:{size:0.1}, From 4c42e95607852cec20d23ffe716290aba8157c16 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 08:13:37 -0800 Subject: [PATCH 30/48] add some debugging --- .../src/avatars/AvatarMixerSlave.cpp | 23 +++++++++++++------ libraries/avatars/src/AvatarData.cpp | 11 ++++++--- libraries/avatars/src/AvatarData.h | 3 ++- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 5c71b88af9..360c6e26fd 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -217,11 +217,19 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); */ - //qDebug() << "------------------------------"; AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); //qDebug() << "thisAvatar:" << thisAvatar.get(); +#ifdef WANT_DEBUG + bool printDebug = nodeData->getAvatarSharedPointer()->getDisplayName() == "ZappoMan"; + + if (printDebug) { + qDebug() << "------------------------------"; + } +#else + bool printDebug = false; +#endif ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, @@ -236,14 +244,15 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { [thisAvatar](AvatarSharedPointer avatar)->bool{ return (avatar == thisAvatar); // ignore ourselves... - }); - //qDebug() << "------------------------------"; + }, printDebug); +#ifdef WANT_DEBUG + if (printDebug) { + qDebug() << "------------------------------"; + } +#endif - // this is an AGENT we have received head data from - // send back a packet with other active node data to this node - //std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { - + // loop through our sorted avatars and allocate our bandwidth to them accordingly int avatarRank = 0; while (!sortedAvatars.empty()) { AvatarPriority sortData = sortedAvatars.top(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 9416216c5a..fd21670853 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2317,7 +2317,8 @@ std::priority_queue AvatarData::sortAvatars( QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore) { + std::function shouldIgnore, + bool printDebug) { uint64_t startTime = usecTimestampNow(); @@ -2359,16 +2360,20 @@ std::priority_queue AvatarData::sortAvatars( // Thus we multiply each component by a conversion "weight" that scales its units // relative to the others. These weights are pure magic tuning and are hard coded in the // relation below: (hint: unitary weights are not explicityly shown) - float priority = apparentSize + 0.25f * cosineAngle + age; + float priority = apparentSize + 10.0f * (0.25f * cosineAngle) + age; // decrement priority of avatars outside keyhole + bool outOfView = false; if (distance > cameraView.getCenterRadius()) { if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { priority += OUT_OF_VIEW_PENALTY; + outOfView = true; } } - //qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age; + if (printDebug) { + qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age << "outOfView:" << outOfView; + } sortedAvatars.push(AvatarPriority(avatar, priority)); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 71ade436ff..a75e81ed3d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -600,7 +600,8 @@ public: QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore); + std::function shouldIgnore, + bool printDebug = false); public slots: From 05995163cf610405f0519511d8009b220697a613 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 09:49:47 -0800 Subject: [PATCH 31/48] add support for tuning sorting --- assignment-client/src/avatars/AvatarMixer.cpp | 20 ++++++++++++++++++- assignment-client/src/avatars/AvatarMixer.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 15 ++++++++++++++ libraries/avatars/src/AvatarData.h | 6 +++--- libraries/networking/src/udt/PacketHeaders.h | 3 ++- 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 3a8aa0182c..59896432e7 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -47,7 +47,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket"); - + packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting"); packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket"); packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket"); @@ -317,6 +317,24 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { } } + +void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode) { + auto start = usecTimestampNow(); + + message->readPrimitive(&AvatarData::_avatarSortCoefficientSize); + message->readPrimitive(&AvatarData::_avatarSortCoefficientCenter); + message->readPrimitive(&AvatarData::_avatarSortCoefficientAge); + + qCDebug(avatars) << "New avatar sorting... " + << "size:" << AvatarData::_avatarSortCoefficientSize + << "center:" << AvatarData::_avatarSortCoefficientCenter + << "age:" << AvatarData::_avatarSortCoefficientAge; + + auto end = usecTimestampNow(); + _handleAdjustAvatarSortingElapsedTime += (end - start); +} + + void AvatarMixer::handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode) { auto start = usecTimestampNow(); getOrCreateClientData(senderNode); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 32b0ffed69..1925ec1ebd 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -39,6 +39,7 @@ public slots: private slots: void queueIncomingPacket(QSharedPointer message, SharedNodePointer node); + void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); void handleViewFrustumPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message); @@ -92,6 +93,7 @@ private: quint64 _broadcastAvatarDataNodeTransform { 0 }; quint64 _broadcastAvatarDataNodeFunctor { 0 }; + quint64 _handleAdjustAvatarSortingElapsedTime { 0 }; quint64 _handleViewFrustumPacketElapsedTime { 0 }; quint64 _handleAvatarIdentityPacketElapsedTime { 0 }; quint64 _handleKillAvatarPacketElapsedTime { 0 }; diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 9dc73d9239..4cdf735e96 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -572,14 +572,29 @@ float AvatarManager::getAvatarSortCoefficient(const QString& name) { // HACK void AvatarManager::setAvatarSortCoefficient(const QString& name, const QScriptValue& value) { + bool somethingChanged = false; if (value.isNumber()) { float numericalValue = (float)value.toNumber(); if (name == "size") { AvatarData::_avatarSortCoefficientSize = numericalValue; + somethingChanged = true; } else if (name == "center") { AvatarData::_avatarSortCoefficientCenter = numericalValue; + somethingChanged = true; } else if (name == "age") { AvatarData::_avatarSortCoefficientAge = numericalValue; + somethingChanged = true; } } + if (somethingChanged) { + size_t packetSize = sizeof(AvatarData::_avatarSortCoefficientSize) + + sizeof(AvatarData::_avatarSortCoefficientCenter) + + sizeof(AvatarData::_avatarSortCoefficientAge); + + auto packet = NLPacket::create(PacketType::AdjustAvatarSorting, packetSize); + packet->writePrimitive(AvatarData::_avatarSortCoefficientSize); + packet->writePrimitive(AvatarData::_avatarSortCoefficientCenter); + packet->writePrimitive(AvatarData::_avatarSortCoefficientAge); + DependencyManager::get()->broadcastToNodes(std::move(packet), NodeSet() << NodeType::AvatarMixer); + } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 9d5bc507e0..daa496f6e1 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -605,9 +605,9 @@ public: // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. - static float _avatarSortCoefficientSize { 0.5f }; - static float _avatarSortCoefficientCenter { 0.25 }; - static float _avatarSortCoefficientAge { 1.0f }; + static float _avatarSortCoefficientSize; + static float _avatarSortCoefficientCenter; + static float _avatarSortCoefficientAge; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 050e3088f8..01973a6786 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -112,7 +112,8 @@ public: ReloadEntityServerScript, EntityPhysics, EntityServerScriptLog, - LAST_PACKET_TYPE = EntityServerScriptLog + AdjustAvatarSorting, + LAST_PACKET_TYPE = AdjustAvatarSorting }; }; From 71b569a697d6fec7c73df2114c515cd55d2d0235 Mon Sep 17 00:00:00 2001 From: "Anthony J. Thibault" Date: Wed, 22 Feb 2017 10:22:12 -0800 Subject: [PATCH 32/48] Bug-fix for crash on tablet when going from web -> home. --- libraries/script-engine/src/TabletScriptingInterface.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/libraries/script-engine/src/TabletScriptingInterface.cpp b/libraries/script-engine/src/TabletScriptingInterface.cpp index c78ce251c8..0d4934c35a 100644 --- a/libraries/script-engine/src/TabletScriptingInterface.cpp +++ b/libraries/script-engine/src/TabletScriptingInterface.cpp @@ -366,6 +366,7 @@ void TabletProxy::gotoWebScreen(const QString& url, const QString& injectedJavaS } if (root) { + removeButtonsFromHomeScreen(); QMetaObject::invokeMethod(root, "loadSource", Q_ARG(const QVariant&, QVariant(WEB_VIEW_SOURCE_URL))); QMetaObject::invokeMethod(root, "setShown", Q_ARG(const QVariant&, QVariant(true))); QMetaObject::invokeMethod(root, "loadWebUrl", Q_ARG(const QVariant&, QVariant(url)), Q_ARG(const QVariant&, QVariant(injectedJavaScriptUrl))); From 618f37eb0622abfdbc5e540956535ef20121a9f7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 11:14:22 -0800 Subject: [PATCH 33/48] fix crash in recording playback when clip loader fails --- libraries/script-engine/src/RecordingScriptingInterface.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/script-engine/src/RecordingScriptingInterface.cpp b/libraries/script-engine/src/RecordingScriptingInterface.cpp index 710f342322..41bb780b47 100644 --- a/libraries/script-engine/src/RecordingScriptingInterface.cpp +++ b/libraries/script-engine/src/RecordingScriptingInterface.cpp @@ -56,6 +56,11 @@ bool RecordingScriptingInterface::loadRecording(const QString& url) { using namespace recording; auto loader = ClipCache::instance().getClipLoader(url); + if (!loader) { + qWarning() << "Clip failed to load from " << url; + return false; + } + if (!loader->isLoaded()) { QEventLoop loop; QObject::connect(loader.data(), &Resource::loaded, &loop, &QEventLoop::quit); From aed1d693776047ba0f6fe001cd9700e7df8aed9d Mon Sep 17 00:00:00 2001 From: Seth Alves Date: Wed, 22 Feb 2017 14:14:10 -0800 Subject: [PATCH 34/48] don't do a haptic pulse when av's hand goes near grabbable tablet --- scripts/system/controllers/handControllerGrab.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/scripts/system/controllers/handControllerGrab.js b/scripts/system/controllers/handControllerGrab.js index 95c05c2717..ea76490b7b 100644 --- a/scripts/system/controllers/handControllerGrab.js +++ b/scripts/system/controllers/handControllerGrab.js @@ -1233,7 +1233,13 @@ function MyController(hand) { }); if (grabbableEntities.length > 0) { if (!this.grabPointIntersectsEntity) { - Controller.triggerHapticPulse(1, 20, this.hand); + // don't do haptic pulse for tablet + var nonTabletEntities = grabbableEntities.filter(function(entityID) { + return entityID != HMD.tabletID && entityID != HMD.homeButtonID; + }); + if (nonTabletEntities.length > 0) { + Controller.triggerHapticPulse(1, 20, this.hand); + } this.grabPointIntersectsEntity = true; this.grabPointSphereOn(); } From 6ac33dbf61382605d77b5a76b2d086a3e4636632 Mon Sep 17 00:00:00 2001 From: Andrew Meadows Date: Wed, 22 Feb 2017 17:23:36 -0800 Subject: [PATCH 35/48] count avatar joint updates and non-updates --- interface/resources/qml/Stats.qml | 4 +-- interface/src/avatar/Avatar.h | 2 ++ interface/src/avatar/AvatarManager.cpp | 42 ++++++++++++++++++++------ interface/src/avatar/AvatarManager.h | 8 ++--- interface/src/ui/Stats.cpp | 4 +-- interface/src/ui/Stats.h | 8 ++--- 6 files changed, 47 insertions(+), 21 deletions(-) diff --git a/interface/resources/qml/Stats.qml b/interface/resources/qml/Stats.qml index faf37d5366..58d589b667 100644 --- a/interface/resources/qml/Stats.qml +++ b/interface/resources/qml/Stats.qml @@ -107,11 +107,11 @@ Item { } StatText { visible: root.expanded - text: "Fully Simulated Avatars: " + root.fullySimulatedAvatarCount + text: "Avatars Updated: " + root.updatedAvatarCount } StatText { visible: root.expanded - text: "Partially Simulated Avatars: " + root.partiallySimulatedAvatarCount + text: "Avatars NOT Updated: " + root.notUpdatedAvatarCount } } } diff --git a/interface/src/avatar/Avatar.h b/interface/src/avatar/Avatar.h index 53f0562bdd..ca4dbd2af8 100644 --- a/interface/src/avatar/Avatar.h +++ b/interface/src/avatar/Avatar.h @@ -183,6 +183,8 @@ public: Q_INVOKABLE float getSimulationRate(const QString& rateName = QString("")) const; + bool hasNewJointData() const { return _hasNewJointData; } + public slots: // FIXME - these should be migrated to use Pose data instead diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 4cdf735e96..af9f915fec 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -195,8 +195,8 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { uint64_t renderExpiry = startTime + RENDER_UPDATE_BUDGET; uint64_t maxExpiry = startTime + MAX_UPDATE_BUDGET; - int fullySimulatedAvatars = 0; - int partiallySimulatedAvatars = 0; + int numAvatarsUpdated = 0; + int numAVatarsNotUpdated = 0; while (!sortedAvatars.empty()) { const AvatarPriority& sortData = sortedAvatars.top(); const auto& avatar = std::static_pointer_cast(sortData.avatar); @@ -217,33 +217,57 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { } avatar->animateScaleChanges(deltaTime); + const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; uint64_t now = usecTimestampNow(); if (now < renderExpiry) { // we're within budget - const float OUT_OF_VIEW_THRESHOLD = 0.5f * AvatarData::OUT_OF_VIEW_PENALTY; bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && avatar->hasNewJointData()) { + numAvatarsUpdated++; + } avatar->simulate(deltaTime, inView); avatar->updateRenderItem(pendingChanges); avatar->setLastRenderUpdateTime(startTime); - fullySimulatedAvatars++; } else if (now < maxExpiry) { // we've spent most of our time budget, but we still simulate() the avatar as it if were out of view // --> some avatars may freeze until their priority trickles up - const bool inView = false; - avatar->simulate(deltaTime, inView); - partiallySimulatedAvatars++; + bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && avatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + avatar->simulate(deltaTime, false); } else { // we've spent ALL of our 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 avatar velocity measurements may be a little off + + // HACK: no time simulate, but we will take the time to count how many were tragically missed + bool inView = sortData.priority > OUT_OF_VIEW_THRESHOLD; + if (!inView) { + break; + } + if (inView && avatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + sortedAvatars.pop(); + while (inView && !sortedAvatars.empty()) { + const AvatarPriority& newSortData = sortedAvatars.top(); + const auto& newAvatar = std::static_pointer_cast(newSortData.avatar); + inView = newSortData.priority > OUT_OF_VIEW_THRESHOLD; + if (inView && newAvatar->hasNewJointData()) { + numAVatarsNotUpdated++; + } + sortedAvatars.pop(); + } break; } sortedAvatars.pop(); } _avatarSimulationTime = (float)(usecTimestampNow() - startTime) / (float)USECS_PER_MSEC; - _fullySimulatedAvatars = fullySimulatedAvatars; - _partiallySimulatedAvatars = partiallySimulatedAvatars; + _numAvatarsUpdated = numAvatarsUpdated; + _numAvatarsNotUpdated = numAVatarsNotUpdated; qApp->getMain3DScene()->enqueuePendingChanges(pendingChanges); simulateAvatarFades(deltaTime); diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h index 8302bf4ed6..e1f5a3b411 100644 --- a/interface/src/avatar/AvatarManager.h +++ b/interface/src/avatar/AvatarManager.h @@ -43,8 +43,8 @@ public: std::shared_ptr getMyAvatar() { return _myAvatar; } AvatarSharedPointer getAvatarBySessionID(const QUuid& sessionID) const override; - int getFullySimulatedAvatars() const { return _fullySimulatedAvatars; } - int getPartiallySimulatedAvatars() const { return _partiallySimulatedAvatars; } + int getNumAvatarsUpdated() const { return _numAvatarsUpdated; } + int getNumAvatarsNotUpdated() const { return _numAvatarsNotUpdated; } float getAvatarSimulationTime() const { return _avatarSimulationTime; } void updateMyAvatar(float deltaTime); @@ -120,8 +120,8 @@ private: VectorOfMotionStates _motionStatesToRemoveFromPhysics; RateCounter<> _myAvatarSendRate; - int _fullySimulatedAvatars { 0 }; - int _partiallySimulatedAvatars { 0 }; + int _numAvatarsUpdated { 0 }; + int _numAvatarsNotUpdated { 0 }; float _avatarSimulationTime { 0.0f }; }; diff --git a/interface/src/ui/Stats.cpp b/interface/src/ui/Stats.cpp index e82f99bed2..1075bbdaa4 100644 --- a/interface/src/ui/Stats.cpp +++ b/interface/src/ui/Stats.cpp @@ -121,8 +121,8 @@ 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(fullySimulatedAvatarCount, avatarManager->getFullySimulatedAvatars()); - STAT_UPDATE(partiallySimulatedAvatarCount, avatarManager->getPartiallySimulatedAvatars()); + STAT_UPDATE(updatedAvatarCount, avatarManager->getNumAvatarsUpdated()); + STAT_UPDATE(notUpdatedAvatarCount, avatarManager->getNumAvatarsNotUpdated()); STAT_UPDATE(serverCount, (int)nodeList->size()); STAT_UPDATE(framerate, qApp->getFps()); if (qApp->getActiveDisplayPlugin()) { diff --git a/interface/src/ui/Stats.h b/interface/src/ui/Stats.h index f501f4b09a..6be084100c 100644 --- a/interface/src/ui/Stats.h +++ b/interface/src/ui/Stats.h @@ -49,8 +49,8 @@ class Stats : public QQuickItem { STATS_PROPERTY(int, simrate, 0) STATS_PROPERTY(int, avatarSimrate, 0) STATS_PROPERTY(int, avatarCount, 0) - STATS_PROPERTY(int, fullySimulatedAvatarCount, 0) - STATS_PROPERTY(int, partiallySimulatedAvatarCount, 0) + STATS_PROPERTY(int, updatedAvatarCount, 0) + STATS_PROPERTY(int, notUpdatedAvatarCount, 0) STATS_PROPERTY(int, packetInCount, 0) STATS_PROPERTY(int, packetOutCount, 0) STATS_PROPERTY(float, mbpsIn, 0) @@ -159,8 +159,8 @@ signals: void simrateChanged(); void avatarSimrateChanged(); void avatarCountChanged(); - void fullySimulatedAvatarCountChanged(); - void partiallySimulatedAvatarCountChanged(); + void updatedAvatarCountChanged(); + void notUpdatedAvatarCountChanged(); void packetInCountChanged(); void packetOutCountChanged(); void mbpsInChanged(); From 4c4506b1f7c12f23a3c3401c9b15cf1641806220 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 21:47:40 -0800 Subject: [PATCH 36/48] some cleanup --- assignment-client/src/avatars/AvatarMixer.cpp | 20 ++++---- .../src/avatars/AvatarMixerClientData.h | 10 ++-- .../src/avatars/AvatarMixerSlave.cpp | 46 ++----------------- 3 files changed, 21 insertions(+), 55 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 59896432e7..50363f62e3 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -309,7 +309,8 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { }, [&](const SharedNodePointer& node) { QMetaObject::invokeMethod(node->getLinkedData(), - "removeLastBroadcastSequenceNumber", + //"removeLastBroadcastSequenceNumber", + "cleanupKilledNode", Qt::AutoConnection, Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); } @@ -321,14 +322,17 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode) { auto start = usecTimestampNow(); - message->readPrimitive(&AvatarData::_avatarSortCoefficientSize); - message->readPrimitive(&AvatarData::_avatarSortCoefficientCenter); - message->readPrimitive(&AvatarData::_avatarSortCoefficientAge); + // only allow admins with kick rights to change this value... + if (senderNode->getCanKick()) { + message->readPrimitive(&AvatarData::_avatarSortCoefficientSize); + message->readPrimitive(&AvatarData::_avatarSortCoefficientCenter); + message->readPrimitive(&AvatarData::_avatarSortCoefficientAge); - qCDebug(avatars) << "New avatar sorting... " - << "size:" << AvatarData::_avatarSortCoefficientSize - << "center:" << AvatarData::_avatarSortCoefficientCenter - << "age:" << AvatarData::_avatarSortCoefficientAge; + qCDebug(avatars) << "New avatar sorting... " + << "size:" << AvatarData::_avatarSortCoefficientSize + << "center:" << AvatarData::_avatarSortCoefficientCenter + << "age:" << AvatarData::_avatarSortCoefficientAge; + } auto end = usecTimestampNow(); _handleAdjustAvatarSortingElapsedTime += (end - start); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 7eea31c9ab..9cf73a94f4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -53,11 +53,14 @@ public: Q_INVOKABLE void removeLastBroadcastSequenceNumber(const QUuid& nodeUUID) { _lastBroadcastSequenceNumbers.erase(nodeUUID); } uint64_t getLastBroadcastTime(const QUuid& nodeUUID) const; - void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { - _lastBroadcastTimes[nodeUUID] = broadcastTime; - } + 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); + } + uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } HRCTime getIdentityChangeTimestamp() const { return _identityChangeTimestamp; } @@ -115,7 +118,6 @@ public: ViewFrustum getViewFrustom() const { return _currentViewFrustum; } - quint64 getLastOtherAvatarEncodeTime(QUuid otherAvatar) { quint64 result = 0; if (_lastOtherAvatarEncodeTime.find(otherAvatar) != _lastOtherAvatarEncodeTime.end()) { diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 360c6e26fd..5ad4561aa9 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -191,7 +191,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { QList avatarList; std::unordered_map avatarDataToNodes; - //qDebug() << "------------------------------"; int listItem = 0; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); @@ -200,36 +199,14 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); avatarList << otherAvatar; avatarDataToNodes[otherAvatar] = otherNode; - /* - qDebug() << "listItem [" << listItem << "] " - << "otherNode:" << otherNode.data() - << "otherNode->getUUID():" << otherNode->getUUID() - << "otherNodeData:" << otherNodeData - << "otherAvatar:" << otherAvatar.get(); - - qDebug() << "avatarDataToNodes[" << otherAvatar.get() << "]=" << otherNode.data(); - */ } }); - /* - qDebug() << "avatarList.size:" << avatarList.size(); - qDebug() << "avatarDataToNodes.size:" << avatarDataToNodes.size(); - */ AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); //qDebug() << "thisAvatar:" << thisAvatar.get(); -#ifdef WANT_DEBUG - bool printDebug = nodeData->getAvatarSharedPointer()->getDisplayName() == "ZappoMan"; - - if (printDebug) { - qDebug() << "------------------------------"; - } -#else - bool printDebug = false; -#endif ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, @@ -239,18 +216,12 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { if (avatarNode) { return nodeData->getLastBroadcastTime(avatarNode->getUUID()); } - return 0; // ??? + return 0; }, [thisAvatar](AvatarSharedPointer avatar)->bool{ return (avatar == thisAvatar); // ignore ourselves... - }, printDebug); - -#ifdef WANT_DEBUG - if (printDebug) { - qDebug() << "------------------------------"; - } -#endif + }); // loop through our sorted avatars and allocate our bandwidth to them accordingly int avatarRank = 0; @@ -262,13 +233,9 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { auto otherNode = avatarDataToNodes[avatarData]; - //qDebug() << "otherNode (" << otherNode.data() << ")= avatarDataToNodes[" << avatarData.get() << "]"; - if (!otherNode) { - //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << ":" << avatarData.get() << "... otherNode unknown!!"; continue; } - //qDebug() << "For viewer:" << node->getUUID() << "... process other avatar [" << avatarRank << "] avatarData: " << avatarData.get() << " otherNode:" << otherNode->getUUID() << " ... "; bool shouldConsider = false; quint64 startIgnoreCalculation = usecTimestampNow(); @@ -282,8 +249,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; - } else { const AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); @@ -318,8 +283,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { if (nodeBox.touches(otherNodeBox)) { nodeData->ignoreOther(node, otherNode); shouldConsider = getsAnyIgnored; - //qDebug() << "shouldConsider = getsAnyIgnored " << getsAnyIgnored << " ... line:" << __LINE__; - } } // Not close enough to ignore @@ -397,9 +360,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; - } - else if (lastSeqFromSender - lastSeqToReceiver > 1) { + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { // this is a skip - we still send the packet but capture the presence of the skip so we see it happening ++numAvatarsWithSkippedFrames; } @@ -418,7 +379,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; } if (shouldConsider) { From edf7c016a12cd51ac5892d495a518142c44233e7 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 22:06:28 -0800 Subject: [PATCH 37/48] more cleanup --- assignment-client/src/avatars/AvatarMixer.cpp | 1 - .../src/avatars/AvatarMixerSlave.cpp | 6 ++---- libraries/avatars/src/AvatarData.cpp | 17 +++-------------- libraries/avatars/src/AvatarData.h | 7 +------ 4 files changed, 6 insertions(+), 25 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 50363f62e3..a347b5ef47 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -309,7 +309,6 @@ void AvatarMixer::nodeKilled(SharedNodePointer killedNode) { }, [&](const SharedNodePointer& node) { QMetaObject::invokeMethod(node->getLinkedData(), - //"removeLastBroadcastSequenceNumber", "cleanupKilledNode", Qt::AutoConnection, Q_ARG(const QUuid&, QUuid(killedNode->getUUID()))); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 5ad4561aa9..9b501a5e31 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -187,7 +187,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); - + // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes + // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes QList avatarList; std::unordered_map avatarDataToNodes; @@ -204,9 +205,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { }); AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); - - //qDebug() << "thisAvatar:" << thisAvatar.get(); - ViewFrustum cameraView = nodeData->getViewFrustom(); std::priority_queue sortedAvatars = AvatarData::sortAvatars( avatarList, cameraView, diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 7c1b1baf26..e2574683a2 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -69,8 +69,7 @@ AvatarData::AvatarData() : _displayNameAlpha(1.0f), _errorLogExpiry(0), _owningAvatarMixer(), - _targetVelocity(0.0f), - _localAABox(DEFAULT_LOCAL_AABOX_CORNER, DEFAULT_LOCAL_AABOX_SCALE) + _targetVelocity(0.0f) { setBodyPitch(0.0f); setBodyYaw(-90.0f); @@ -2321,8 +2320,7 @@ std::priority_queue AvatarData::sortAvatars( QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore, - bool printDebug) { + std::function shouldIgnore) { uint64_t startTime = usecTimestampNow(); @@ -2335,8 +2333,6 @@ std::priority_queue AvatarData::sortAvatars( const auto& avatar = avatarList.at(i); bool outOfView = false; - // FIXME - probably some lambda that allows the caller to reject some avatars - if (shouldIgnore(avatar)) { continue; } @@ -2352,13 +2348,10 @@ std::priority_queue AvatarData::sortAvatars( // FIXME - AvatarData has something equivolent to this float radius = 1.0f; // avatar->getBoundingRadius(); - const glm::vec3& forward = cameraView.getDirection(); float apparentSize = 2.0f * radius / distance; float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance; - - uint64_t lastUpdatedTime = lastUpdated(avatar); // avatar->getLastRenderUpdateTime() - float age = (float)(startTime - lastUpdatedTime) / (float)(USECS_PER_SECOND); + float age = (float)(startTime - lastUpdated(avatar)) / (float)(USECS_PER_SECOND); // NOTE: we are adding values of different units to get a single measure of "priority". // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. @@ -2375,10 +2368,6 @@ std::priority_queue AvatarData::sortAvatars( outOfView = true; } } - - if (printDebug) { - qDebug() << "avatar:" << avatar.get() << "priority:" << priority << "apparentSize:" << apparentSize << "cosineAngle:" << cosineAngle << "age:" << age << "outOfView:" << outOfView; - } sortedAvatars.push(AvatarPriority(avatar, priority)); } } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index daa496f6e1..8608137b20 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -550,8 +550,6 @@ public: void setOwningAvatarMixer(const QWeakPointer& owningAvatarMixer) { _owningAvatarMixer = owningAvatarMixer; } - //const AABox& getLocalAABox() const { return _localAABox; } - int getUsecsSinceLastUpdate() const { return _averageBytesReceived.getUsecsSinceLastEvent(); } int getAverageBytesReceivedPerSecond() const; int getReceiveRate() const; @@ -600,8 +598,7 @@ public: QList avatarList, const ViewFrustum& cameraView, std::function lastUpdated, - std::function shouldIgnore, - bool printDebug = false); + std::function shouldIgnore); // TODO: remove this HACK once we settle on optimal sort coefficients // These coefficients exposed for fine tuning the sort priority for transfering new _jointData to the render pipeline. @@ -692,8 +689,6 @@ protected: glm::vec3 _targetVelocity; - AABox _localAABox; - SimpleMovingAverage _averageBytesReceived; // During recording, this holds the starting position, orientation & scale of the recorded avatar From 06f0087459a56431f40cd6ec72f7501303b9c36d Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 22:36:37 -0800 Subject: [PATCH 38/48] wire up radius properly --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 5 +++++ interface/src/avatar/AvatarManager.cpp | 4 ++++ libraries/avatars/src/AvatarData.cpp | 7 ++++--- libraries/avatars/src/AvatarData.h | 3 ++- 4 files changed, 15 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 9b501a5e31..0004afa97c 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -217,6 +217,11 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { return 0; }, + [&](AvatarSharedPointer avatar)->float{ + glm::vec3 nodeBoxHalfScale = (avatar->getPosition() - avatar->getGlobalBoundingBoxCorner()); + return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); + }, + [thisAvatar](AvatarSharedPointer avatar)->bool{ return (avatar == thisAvatar); // ignore ourselves... }); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index af9f915fec..b1a09b46ce 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -171,6 +171,10 @@ void AvatarManager::updateOtherAvatars(float deltaTime) { return std::static_pointer_cast(avatar)->getLastRenderUpdateTime(); }, + [](AvatarSharedPointer avatar)->float{ + return std::static_pointer_cast(avatar)->getBoundingRadius(); + }, + [this](AvatarSharedPointer avatar)->bool{ const auto& castedAvatar = std::static_pointer_cast(avatar); if (castedAvatar == _myAvatar || !castedAvatar->isInitialized()) { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index e2574683a2..0c145a6a30 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2319,7 +2319,8 @@ float AvatarData::_avatarSortCoefficientAge { 1.0f }; std::priority_queue AvatarData::sortAvatars( QList avatarList, const ViewFrustum& cameraView, - std::function lastUpdated, + std::function getLastUpdated, + std::function getBoundingRadius, std::function shouldIgnore) { uint64_t startTime = usecTimestampNow(); @@ -2346,12 +2347,12 @@ std::priority_queue AvatarData::sortAvatars( float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero // FIXME - AvatarData has something equivolent to this - float radius = 1.0f; // avatar->getBoundingRadius(); + float radius = getBoundingRadius(avatar); const glm::vec3& forward = cameraView.getDirection(); float apparentSize = 2.0f * radius / distance; float cosineAngle = glm::length(glm::dot(offset, forward) * forward) / distance; - float age = (float)(startTime - lastUpdated(avatar)) / (float)(USECS_PER_SECOND); + float age = (float)(startTime - getLastUpdated(avatar)) / (float)(USECS_PER_SECOND); // NOTE: we are adding values of different units to get a single measure of "priority". // Thus we multiply each component by a conversion "weight" that scales its units relative to the others. diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 8608137b20..0eddc29cda 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -597,7 +597,8 @@ public: static std::priority_queue sortAvatars( QList avatarList, const ViewFrustum& cameraView, - std::function lastUpdated, + std::function getLastUpdated, + std::function getBoundingRadius, std::function shouldIgnore); // TODO: remove this HACK once we settle on optimal sort coefficients From 40037bee55d063ae1f81c3811e9c7623370f37c5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Wed, 22 Feb 2017 23:28:40 -0800 Subject: [PATCH 39/48] added some stats for the random drops --- assignment-client/src/avatars/AvatarMixer.cpp | 8 +++++++- assignment-client/src/avatars/AvatarMixerSlave.cpp | 10 +++++++--- assignment-client/src/avatars/AvatarMixerSlave.h | 6 ++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a347b5ef47..a90fdb100c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -506,6 +506,9 @@ void AvatarMixer::sendStatsPacket() { float averageOthersIncluded = averageNodes ? stats.numOthersIncluded / averageNodes : 0.0f; slaveObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); + float averageRandomDrops = averageNodes ? stats.randomDrops / averageNodes : 0.0f; + slaveObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); + slaveObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(stats.processIncomingPacketsElapsedTime); slaveObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(stats.ignoreCalculationElapsedTime); slaveObject["timing_3_toByteArray"] = TIGHT_LOOP_STAT_UINT64(stats.toByteArrayElapsedTime); @@ -533,9 +536,12 @@ void AvatarMixer::sendStatsPacket() { float averageOutboundAvatarKbps = averageNodes ? ((aggregateStats.numBytesSent / secondsSinceLastStats) / BYTES_PER_KILOBIT) / averageNodes : 0.0f; slavesAggregatObject["sent_5_averageOutboundAvatarKbps"] = averageOutboundAvatarKbps; + float averageRandomDrops = averageNodes ? aggregateStats.randomDrops / averageNodes : 0.0f; + slavesAggregatObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); + float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); - + slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime); slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime); slavesAggregatObject["timing_3_toByteArray"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.toByteArrayElapsedTime); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 0004afa97c..5073aab934 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -332,15 +332,19 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // // NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that // are out of view, this also appears to disable this random distribution. + // + // FIXME - This approach for managing the outbound bandwidth is less than ideal, + // it would be better to more directly budget the number of bytes to send + // per frame and simply exit the sorted avatar list once that budget is + // surpassed. We will work on that next. [BHG 2/22/17] if (distanceToAvatar != 0.0f && !getsOutOfView - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) /// FIX ME... we don't want to do this random stuff + && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) ) { - quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; - //qDebug() << "shouldConsider = false ... line:" << __LINE__; + _stats.randomDrops++; } if (shouldConsider) { diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index b1a5b56c4f..50ae4570b5 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -25,6 +25,8 @@ public: int numBytesSent { 0 }; int numIdentityPackets { 0 }; int numOthersIncluded { 0 }; + int randomDrops { 0 }; + quint64 ignoreCalculationElapsedTime { 0 }; quint64 avatarDataPackingElapsedTime { 0 }; quint64 packetSendingElapsedTime { 0 }; @@ -43,6 +45,8 @@ public: numBytesSent = 0; numIdentityPackets = 0; numOthersIncluded = 0; + randomDrops = 0; + ignoreCalculationElapsedTime = 0; avatarDataPackingElapsedTime = 0; packetSendingElapsedTime = 0; @@ -60,6 +64,8 @@ public: numBytesSent += rhs.numBytesSent; numIdentityPackets += rhs.numIdentityPackets; numOthersIncluded += rhs.numOthersIncluded; + randomDrops += rhs.randomDrops; + ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime; avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime; packetSendingElapsedTime += rhs.packetSendingElapsedTime; From d4adee8b388d6c2cb482f8e0792316892594a68c Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 00:15:07 -0800 Subject: [PATCH 40/48] add over bandwidth exit to sorted avatar loop --- assignment-client/src/avatars/AvatarMixer.cpp | 10 ++++++-- .../src/avatars/AvatarMixerSlave.cpp | 23 +++++++++++++++---- .../src/avatars/AvatarMixerSlave.h | 3 +++ 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a90fdb100c..6db9d05234 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -508,6 +508,9 @@ void AvatarMixer::sendStatsPacket() { float averageRandomDrops = averageNodes ? stats.randomDrops / averageNodes : 0.0f; slaveObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); + + float averageOverBudgetAvatars = averageNodes ? stats.overBudgetAvatars / averageNodes : 0.0f; + slaveObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slaveObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(stats.processIncomingPacketsElapsedTime); slaveObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(stats.ignoreCalculationElapsedTime); @@ -536,11 +539,14 @@ void AvatarMixer::sendStatsPacket() { float averageOutboundAvatarKbps = averageNodes ? ((aggregateStats.numBytesSent / secondsSinceLastStats) / BYTES_PER_KILOBIT) / averageNodes : 0.0f; slavesAggregatObject["sent_5_averageOutboundAvatarKbps"] = averageOutboundAvatarKbps; + float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; + slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); + float averageRandomDrops = averageNodes ? aggregateStats.randomDrops / averageNodes : 0.0f; slavesAggregatObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); - float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; - slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); + float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f; + slavesAggregatObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime); slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 5073aab934..21eaebc176 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -129,6 +129,11 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; + // max number of avatarBytes per frame + auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; + + int overBudgetAvatars = 0; + // keep track of the number of other avatars held back in this frame int numAvatarsHeldBack = 0; @@ -333,10 +338,10 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that // are out of view, this also appears to disable this random distribution. // - // FIXME - This approach for managing the outbound bandwidth is less than ideal, - // it would be better to more directly budget the number of bytes to send - // per frame and simply exit the sorted avatar list once that budget is - // surpassed. We will work on that next. [BHG 2/22/17] + // FIXME - This is the old approach for managing the outbound bandwidth. I've left it + // in for now, even though we're also directly managing budget by calculating the + // number of bytes to send per frame and simply exiting the sorted avatar list + // once that budget is surpassed. I need to remove this old logic next. [BHG 2/22/17] if (distanceToAvatar != 0.0f && !getsOutOfView && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) @@ -372,6 +377,16 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { ++numAvatarsWithSkippedFrames; } + // NOTE: Here's where we determine if we are over budget and stop considering avatars after this. + // + // FIXME - revisit this code, remove the random drop logic, and move this code higher up into + // the loop so we don't bother with all these other calculations once over budget. [BHG 2/22/17] + if (numAvatarDataBytes > maxAvatarBytesPerFrame) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + shouldConsider = false; + } + // we're going to send this avatar if (shouldConsider) { diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 50ae4570b5..1bbd367b2b 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -26,6 +26,7 @@ public: int numIdentityPackets { 0 }; int numOthersIncluded { 0 }; int randomDrops { 0 }; + int overBudgetAvatars { 0 }; quint64 ignoreCalculationElapsedTime { 0 }; quint64 avatarDataPackingElapsedTime { 0 }; @@ -46,6 +47,7 @@ public: numIdentityPackets = 0; numOthersIncluded = 0; randomDrops = 0; + overBudgetAvatars = 0; ignoreCalculationElapsedTime = 0; avatarDataPackingElapsedTime = 0; @@ -65,6 +67,7 @@ public: numIdentityPackets += rhs.numIdentityPackets; numOthersIncluded += rhs.numOthersIncluded; randomDrops += rhs.randomDrops; + overBudgetAvatars += rhs.overBudgetAvatars; ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime; avatarDataPackingElapsedTime += rhs.avatarDataPackingElapsedTime; From 4f17498daf5c22d1e67e302d6b77ae248a5d8e49 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 00:19:06 -0800 Subject: [PATCH 41/48] fix warning --- 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 0c145a6a30..4993c0ac5d 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2332,7 +2332,6 @@ std::priority_queue AvatarData::sortAvatars( PROFILE_RANGE(simulation, "sort"); for (int32_t i = 0; i < avatarList.size(); ++i) { const auto& avatar = avatarList.at(i); - bool outOfView = false; if (shouldIgnore(avatar)) { continue; @@ -2366,7 +2365,6 @@ std::priority_queue AvatarData::sortAvatars( if (distance > cameraView.getCenterRadius()) { if (!cameraView.sphereIntersectsFrustum(avatarPosition, radius)) { priority += OUT_OF_VIEW_PENALTY; - outOfView = true; } } sortedAvatars.push(AvatarPriority(avatar, priority)); From 5033e7be11bfd59654416b92662bd0774a56af8b Mon Sep 17 00:00:00 2001 From: Thijs Wenker Date: Thu, 23 Feb 2017 18:22:36 +0100 Subject: [PATCH 42/48] CR fixes --- interface/src/avatar/MyAvatar.cpp | 2 +- libraries/audio-client/src/AudioClient.cpp | 46 +++++++++++----------- libraries/audio-client/src/AudioClient.h | 10 ++--- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index b9f673287b..19d403a5c5 100644 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -160,7 +160,7 @@ MyAvatar::MyAvatar(RigPointer rig) : } auto audioIO = DependencyManager::get(); - audioIO->setPlayingBackRecording(isPlaying); + audioIO->setIsPlayingBackRecording(isPlaying); if (_rig) { _rig->setEnableAnimations(!isPlaying); diff --git a/libraries/audio-client/src/AudioClient.cpp b/libraries/audio-client/src/AudioClient.cpp index 0e2991ae30..0e06cf8661 100644 --- a/libraries/audio-client/src/AudioClient.cpp +++ b/libraries/audio-client/src/AudioClient.cpp @@ -148,17 +148,17 @@ static inline float convertToFloat(int16_t sample) { AudioClient::AudioClient() : AbstractAudioInterface(), _gate(this), - _audioInput(nullptr), + _audioInput(NULL), _desiredInputFormat(), _inputFormat(), _numInputCallbackBytes(0), - _audioOutput(nullptr), + _audioOutput(NULL), _desiredOutputFormat(), _outputFormat(), _outputFrameSize(0), _numOutputCallbackBytes(0), - _loopbackAudioOutput(nullptr), - _loopbackOutputDevice(nullptr), + _loopbackAudioOutput(NULL), + _loopbackOutputDevice(NULL), _inputRingBuffer(0), _localInjectorsStream(0), _receivedAudioStream(RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES), @@ -176,9 +176,9 @@ AudioClient::AudioClient() : _isNoiseGateEnabled(true), _reverb(false), _reverbOptions(&_scriptReverbOptions), - _inputToNetworkResampler(nullptr), - _networkToOutputResampler(nullptr), - _localToOutputResampler(nullptr), + _inputToNetworkResampler(NULL), + _networkToOutputResampler(NULL), + _localToOutputResampler(NULL), _localAudioThread(this), _audioLimiter(AudioConstants::SAMPLE_RATE, OUTPUT_CHANNEL_COUNT), _outgoingAvatarAudioSequenceNumber(0), @@ -393,9 +393,9 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { } } else { HRESULT hr = S_OK; - CoInitialize(nullptr); - IMMDeviceEnumerator* pMMDeviceEnumerator = nullptr; - CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); + CoInitialize(NULL); + IMMDeviceEnumerator* pMMDeviceEnumerator = NULL; + CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator); IMMDevice* pEndpoint; hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(mode == QAudio::AudioOutput ? eRender : eCapture, eMultimedia, &pEndpoint); if (hr == E_NOTFOUND) { @@ -405,7 +405,7 @@ QAudioDeviceInfo defaultAudioDeviceForMode(QAudio::Mode mode) { deviceName = friendlyNameForAudioDevice(pEndpoint); } pMMDeviceEnumerator->Release(); - pMMDeviceEnumerator = nullptr; + pMMDeviceEnumerator = NULL; CoUninitialize(); } @@ -965,7 +965,7 @@ void AudioClient::handleLocalEchoAndReverb(QByteArray& inputByteArray) { } void AudioClient::handleAudioInput() { - if (!_inputDevice || _playingBackRecording) { + if (!_inputDevice || _isPlayingBackRecording) { return; } @@ -1354,10 +1354,10 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn // That in turn causes it to be disconnected (see for example // http://stackoverflow.com/questions/9264750/qt-signals-and-slots-object-disconnect). _audioInput->stop(); - _inputDevice = nullptr; + _inputDevice = NULL; delete _audioInput; - _audioInput = nullptr; + _audioInput = NULL; _numInputCallbackBytes = 0; _inputAudioDeviceName = ""; @@ -1366,7 +1366,7 @@ bool AudioClient::switchInputToAudioDevice(const QAudioDeviceInfo& inputDeviceIn if (_inputToNetworkResampler) { // if we were using an input to network resampler, delete it here delete _inputToNetworkResampler; - _inputToNetworkResampler = nullptr; + _inputToNetworkResampler = NULL; } if (!inputDeviceInfo.isNull()) { @@ -1461,29 +1461,29 @@ bool AudioClient::switchOutputToAudioDevice(const QAudioDeviceInfo& outputDevice _audioOutput->stop(); delete _audioOutput; - _audioOutput = nullptr; + _audioOutput = NULL; - _loopbackOutputDevice = nullptr; + _loopbackOutputDevice = NULL; delete _loopbackAudioOutput; - _loopbackAudioOutput = nullptr; + _loopbackAudioOutput = NULL; delete[] _outputMixBuffer; - _outputMixBuffer = nullptr; + _outputMixBuffer = NULL; delete[] _outputScratchBuffer; - _outputScratchBuffer = nullptr; + _outputScratchBuffer = NULL; delete[] _localOutputMixBuffer; - _localOutputMixBuffer = nullptr; + _localOutputMixBuffer = NULL; } if (_networkToOutputResampler) { // if we were using an input to network resampler, delete it here delete _networkToOutputResampler; - _networkToOutputResampler = nullptr; + _networkToOutputResampler = NULL; delete _localToOutputResampler; - _localToOutputResampler = nullptr; + _localToOutputResampler = NULL; } if (!outputDeviceInfo.isNull()) { diff --git a/libraries/audio-client/src/AudioClient.h b/libraries/audio-client/src/AudioClient.h index 739c4b459c..072990e0c5 100644 --- a/libraries/audio-client/src/AudioClient.h +++ b/libraries/audio-client/src/AudioClient.h @@ -145,7 +145,7 @@ public: void setPositionGetter(AudioPositionGetter positionGetter) { _positionGetter = positionGetter; } void setOrientationGetter(AudioOrientationGetter orientationGetter) { _orientationGetter = orientationGetter; } - void setPlayingBackRecording(bool playingBackRecording) { _playingBackRecording = playingBackRecording; } + void setIsPlayingBackRecording(bool isPlayingBackRecording) { _isPlayingBackRecording = isPlayingBackRecording; } Q_INVOKABLE void setAvatarBoundingBoxParameters(glm::vec3 corner, glm::vec3 scale); @@ -328,14 +328,14 @@ private: // for local audio (used by audio injectors thread) float _localMixBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_STEREO]; int16_t _localScratchBuffer[AudioConstants::NETWORK_FRAME_SAMPLES_AMBISONIC]; - float* _localOutputMixBuffer { nullptr }; + float* _localOutputMixBuffer { NULL }; AudioInjectorsThread _localAudioThread; Mutex _localAudioMutex; // for output audio (used by this thread) int _outputPeriod { 0 }; - float* _outputMixBuffer { nullptr }; - int16_t* _outputScratchBuffer { nullptr }; + float* _outputMixBuffer { NULL }; + int16_t* _outputScratchBuffer { NULL }; AudioLimiter _audioLimiter; @@ -373,7 +373,7 @@ private: QVector _activeLocalAudioInjectors; - bool _playingBackRecording { false }; + bool _isPlayingBackRecording { false }; CodecPluginPointer _codec; QString _selectedCodecName; From dde9640c66a9be73e8c65c8d87a3b12d22f6a787 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 10:29:17 -0800 Subject: [PATCH 43/48] remove old full rate distance code --- assignment-client/src/avatars/AvatarMixer.cpp | 10 +- .../src/avatars/AvatarMixerClientData.cpp | 2 - .../src/avatars/AvatarMixerClientData.h | 9 -- .../src/avatars/AvatarMixerSlave.cpp | 131 ++++++------------ .../src/avatars/AvatarMixerSlave.h | 5 +- libraries/avatars/src/AvatarData.cpp | 7 + libraries/avatars/src/AvatarData.h | 2 + 7 files changed, 53 insertions(+), 113 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 6db9d05234..0f6863f9ae 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -506,11 +506,8 @@ void AvatarMixer::sendStatsPacket() { float averageOthersIncluded = averageNodes ? stats.numOthersIncluded / averageNodes : 0.0f; slaveObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); - float averageRandomDrops = averageNodes ? stats.randomDrops / averageNodes : 0.0f; - slaveObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); - float averageOverBudgetAvatars = averageNodes ? stats.overBudgetAvatars / averageNodes : 0.0f; - slaveObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); + slaveObject["sent_7_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slaveObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(stats.processIncomingPacketsElapsedTime); slaveObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(stats.ignoreCalculationElapsedTime); @@ -542,11 +539,8 @@ void AvatarMixer::sendStatsPacket() { float averageOthersIncluded = averageNodes ? aggregateStats.numOthersIncluded / averageNodes : 0.0f; slavesAggregatObject["sent_6_averageOthersIncluded"] = TIGHT_LOOP_STAT(averageOthersIncluded); - float averageRandomDrops = averageNodes ? aggregateStats.randomDrops / averageNodes : 0.0f; - slavesAggregatObject["sent_7_averageRandomDrops"] = TIGHT_LOOP_STAT(averageRandomDrops); - float averageOverBudgetAvatars = averageNodes ? aggregateStats.overBudgetAvatars / averageNodes : 0.0f; - slavesAggregatObject["sent_8_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); + slavesAggregatObject["sent_7_averageOverBudgetAvatars"] = TIGHT_LOOP_STAT(averageOverBudgetAvatars); slavesAggregatObject["timing_1_processIncomingPackets"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.processIncomingPacketsElapsedTime); slavesAggregatObject["timing_2_ignoreCalculation"] = TIGHT_LOOP_STAT_UINT64(aggregateStats.ignoreCalculationElapsedTime); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 1df6c24a60..717e14535f 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -126,8 +126,6 @@ bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) { void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["display_name"] = _avatar->getDisplayName(); - jsonObject["full_rate_distance"] = _fullRateDistance; - jsonObject["max_av_distance"] = _maxAvatarDistance; jsonObject["num_avs_sent_last_frame"] = _numAvatarsSentLastFrame; jsonObject["avg_other_av_starves_per_second"] = getAvgNumOtherAvatarStarvesPerSecond(); jsonObject["avg_other_av_skips_per_second"] = getAvgNumOtherAvatarSkipsPerSecond(); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 9cf73a94f4..51b8d692e2 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -68,12 +68,6 @@ public: bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } - void setFullRateDistance(float fullRateDistance) { _fullRateDistance = fullRateDistance; } - float getFullRateDistance() const { return _fullRateDistance; } - - void setMaxAvatarDistance(float maxAvatarDistance) { _maxAvatarDistance = maxAvatarDistance; } - float getMaxAvatarDistance() const { return _maxAvatarDistance; } - void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } int getNumAvatarsSentLastFrame() const { return _numAvatarsSentLastFrame; } @@ -156,9 +150,6 @@ private: HRCTime _identityChangeTimestamp; bool _avatarSessionDisplayNameMustChange{ false }; - float _fullRateDistance = FLT_MAX; - float _maxAvatarDistance = FLT_MAX; - int _numAvatarsSentLastFrame = 0; int _numFramesSinceAdjustment = 0; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 21eaebc176..1f9fbde89a 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -66,13 +66,16 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { } -void AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { +int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode) { + int bytesSent = 0; QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(); auto identityPacket = NLPacket::create(PacketType::AvatarIdentity, individualData.size()); - individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); + individualData.replace(0, NUM_BYTES_RFC4122_UUID, nodeData->getNodeID().toRfc4122()); // FIXME, this looks suspicious + bytesSent += individualData.size(); identityPacket->write(individualData); DependencyManager::get()->sendPacket(std::move(identityPacket), *destinationNode); _stats.numIdentityPackets++; + return bytesSent; } static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; @@ -117,9 +120,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // reset the internal state for correct random number distribution distribution.reset(); - // reset the max distance for this frame - float maxAvatarDistanceThisFrame = 0.0f; - // reset the number of sent avatars nodeData->resetNumAvatarsSentLastFrame(); @@ -128,10 +128,14 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; + int identityBytesSent = 0; // max number of avatarBytes per frame auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; + // FIXME - find a way to not send the sessionID for every avatar + int minimumBytesPerAvatar = AvatarDataPacket::AVATAR_HAS_FLAGS_SIZE + NUM_BYTES_RFC4122_UUID; + int overBudgetAvatars = 0; // keep track of the number of other avatars held back in this frame @@ -140,9 +144,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // keep track of the number of other avatar frames skipped int numAvatarsWithSkippedFrames = 0; - // use the data rate specifically for avatar data for FRD adjustment checks - float avatarDataRateLastSecond = nodeData->getOutboundAvatarDataKbps(); - // When this is true, the AvatarMixer will send Avatar data to a client about avatars that are not in the view frustrum bool getsOutOfView = nodeData->getRequestsDomainListData(); @@ -152,43 +153,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // When this is true, the AvatarMixer will send Avatar data to a client about avatars that have ignored them bool getsAnyIgnored = getsIgnoredByMe && node->getCanKick(); - // Check if it is time to adjust what we send this client based on the observed - // bandwidth to this node. We do this once a second, which is also the window for - // the bandwidth reported by node->getOutboundBandwidth(); - if (nodeData->getNumFramesSinceFRDAdjustment() > AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND) { - - const float FRD_ADJUSTMENT_ACCEPTABLE_RATIO = 0.8f; - const float HYSTERISIS_GAP = (1 - FRD_ADJUSTMENT_ACCEPTABLE_RATIO); - const float HYSTERISIS_MIDDLE_PERCENTAGE = (1 - (HYSTERISIS_GAP * 0.5f)); - - // get the current full rate distance so we can work with it - float currentFullRateDistance = nodeData->getFullRateDistance(); - - if (avatarDataRateLastSecond > _maxKbpsPerNode) { - - // is the FRD greater than the farthest avatar? - // if so, before we calculate anything, set it to that distance - currentFullRateDistance = std::min(currentFullRateDistance, nodeData->getMaxAvatarDistance()); - - // we're adjusting the full rate distance to target a bandwidth in the middle - // of the hysterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - - nodeData->setFullRateDistance(currentFullRateDistance); - nodeData->resetNumFramesSinceFRDAdjustment(); - } else if (currentFullRateDistance < nodeData->getMaxAvatarDistance() - && avatarDataRateLastSecond < _maxKbpsPerNode * FRD_ADJUSTMENT_ACCEPTABLE_RATIO) { - // we are constrained AND we've recovered to below the acceptable ratio - // lets adjust the full rate distance to target a bandwidth in the middle of the hyterisis gap - currentFullRateDistance *= (_maxKbpsPerNode * HYSTERISIS_MIDDLE_PERCENTAGE) / avatarDataRateLastSecond; - - nodeData->setFullRateDistance(currentFullRateDistance); - nodeData->resetNumFramesSinceFRDAdjustment(); - } - } else { - nodeData->incrementNumFramesSinceFRDAdjustment(); - } - // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); @@ -233,18 +197,39 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // loop through our sorted avatars and allocate our bandwidth to them accordingly int avatarRank = 0; + + // this is overly conservative, because it includes some avatars we might not consider + // FIXME - move the ignore logic up into the sorting list so we get a better estimate + int remainingAvatars = (int)sortedAvatars.size(); + while (!sortedAvatars.empty()) { AvatarPriority sortData = sortedAvatars.top(); sortedAvatars.pop(); const auto& avatarData = sortData.avatar; avatarRank++; + remainingAvatars--; auto otherNode = avatarDataToNodes[avatarData]; + // FIXME - should't this be an assert? if (!otherNode) { continue; } + // NOTE: Here's where we determine if we are over budget and drop to bare minimum data + int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; + bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; + /** + qDebug() << "Budget debugging:" + << "numAvatarDataBytes:" << numAvatarDataBytes << "\n" + << "identityBytesSent:" << identityBytesSent << "\n" + << "remainingAvatars:" << remainingAvatars << "\n" + << "minimumBytesPerAvatar:" << minimumBytesPerAvatar << "\n" + << "minimRemainingAvatarBytes:" << minimRemainingAvatarBytes << "\n" + << "overBudget:" << overBudget << "\n" + << "overBudgetAvatars:" << overBudgetAvatars; + **/ + bool shouldConsider = false; quint64 startIgnoreCalculation = usecTimestampNow(); @@ -313,45 +298,21 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID()); // FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..." - if (otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 + if (!overBudget + && otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 && (forceSend || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - sendIdentityPacket(otherNodeData, node); + identityBytesSent += sendIdentityPacket(otherNodeData, node); } const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); // Decide whether to send this avatar's data based on it's distance from us - // The full rate distance is the distance at which EVERY update will be sent for this avatar - // at twice the full rate distance, there will be a 50% chance of sending this avatar's update glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); float distanceToAvatar = glm::length(myPosition - otherPosition); - // potentially update the max full rate distance for this frame - maxAvatarDistanceThisFrame = std::max(maxAvatarDistanceThisFrame, distanceToAvatar); - - // This code handles the random dropping of avatar data based on the ratio of - // "getFullRateDistance" to actual distance. - // - // NOTE: If the recieving node is in "PAL mode" then it's asked to get things even that - // are out of view, this also appears to disable this random distribution. - // - // FIXME - This is the old approach for managing the outbound bandwidth. I've left it - // in for now, even though we're also directly managing budget by calculating the - // number of bytes to send per frame and simply exiting the sorted avatar list - // once that budget is surpassed. I need to remove this old logic next. [BHG 2/22/17] - if (distanceToAvatar != 0.0f - && !getsOutOfView - && distribution(generator) > (nodeData->getFullRateDistance() / distanceToAvatar) - ) { - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - shouldConsider = false; - _stats.randomDrops++; - } - if (shouldConsider) { AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); @@ -377,16 +338,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { ++numAvatarsWithSkippedFrames; } - // NOTE: Here's where we determine if we are over budget and stop considering avatars after this. - // - // FIXME - revisit this code, remove the random drop logic, and move this code higher up into - // the loop so we don't bother with all these other calculations once over budget. [BHG 2/22/17] - if (numAvatarDataBytes > maxAvatarBytesPerFrame) { - overBudgetAvatars++; - _stats.overBudgetAvatars++; - shouldConsider = false; - } - // we're going to send this avatar if (shouldConsider) { @@ -396,19 +347,26 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { bool isInView = nodeData->otherAvatarInView(otherNodeBox); // this throttles the extra data to only be sent every Nth message + /** if (!isInView && !getsOutOfView && (lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0)) { quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); shouldConsider = false; } + ***/ if (shouldConsider) { // start a new segment in the PacketList for this avatar avatarPacketList->startSegment(); AvatarData::AvatarDataDetail detail; - if (!isInView && !getsOutOfView) { + + if (overBudget) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + detail = AvatarData::NoData; + } else if (!isInView && !getsOutOfView) { detail = AvatarData::MinimumData; nodeData->incrementAvatarOutOfView(); } else { @@ -498,13 +456,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); - if (numOtherAvatars == 0) { - // update the full rate distance to FLOAT_MAX since we didn't have any other avatars to send - nodeData->setMaxAvatarDistance(FLT_MAX); - } else { - nodeData->setMaxAvatarDistance(maxAvatarDistanceThisFrame); - } - quint64 endPacketSending = usecTimestampNow(); _stats.packetSendingElapsedTime += (endPacketSending - startPacketSending); } diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 1bbd367b2b..04141d9d72 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -25,7 +25,6 @@ public: int numBytesSent { 0 }; int numIdentityPackets { 0 }; int numOthersIncluded { 0 }; - int randomDrops { 0 }; int overBudgetAvatars { 0 }; quint64 ignoreCalculationElapsedTime { 0 }; @@ -46,7 +45,6 @@ public: numBytesSent = 0; numIdentityPackets = 0; numOthersIncluded = 0; - randomDrops = 0; overBudgetAvatars = 0; ignoreCalculationElapsedTime = 0; @@ -66,7 +64,6 @@ public: numBytesSent += rhs.numBytesSent; numIdentityPackets += rhs.numIdentityPackets; numOthersIncluded += rhs.numOthersIncluded; - randomDrops += rhs.randomDrops; overBudgetAvatars += rhs.overBudgetAvatars; ignoreCalculationElapsedTime += rhs.ignoreCalculationElapsedTime; @@ -94,7 +91,7 @@ public: void harvestStats(AvatarMixerSlaveStats& stats); private: - void sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); + int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); // frame state ConstIter _begin; diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4993c0ac5d..6a01c2930c 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -192,6 +192,13 @@ QByteArray AvatarData::toByteArray(AvatarDataDetail dataDetail, quint64 lastSent unsigned char* destinationBuffer = reinterpret_cast(avatarDataByteArray.data()); unsigned char* startPosition = destinationBuffer; + // special case, if we were asked for no data, then just include the flags all set to nothing + if (dataDetail == NoData) { + AvatarDataPacket::HasFlags packetStateFlags = 0; + memcpy(destinationBuffer, &packetStateFlags, sizeof(packetStateFlags)); + return avatarDataByteArray.left(sizeof(packetStateFlags)); + } + // FIXME - // // BUG -- if you enter a space bubble, and then back away, the avatar has wrong orientation until "send all" happens... diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0eddc29cda..3797132274 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -136,6 +136,7 @@ namespace AvatarDataPacket { const HasFlags PACKET_HAS_AVATAR_LOCAL_POSITION = 1U << 9; const HasFlags PACKET_HAS_FACE_TRACKER_INFO = 1U << 10; const HasFlags PACKET_HAS_JOINT_DATA = 1U << 11; + const size_t AVATAR_HAS_FLAGS_SIZE = 2; // NOTE: AvatarDataPackets start with a uint16_t sequence number that is not reflected in the Header structure. @@ -373,6 +374,7 @@ public: void setHandPosition(const glm::vec3& handPosition); typedef enum { + NoData, MinimumData, CullSmallData, IncludeSmallData, From 99e51e88ae3e8265611428efb9de1fba4ce26aed Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Fri, 3 Feb 2017 15:13:11 -0800 Subject: [PATCH 44/48] Add QMetaObject for AvatarEntityMap --- libraries/avatars/src/AvatarData.cpp | 19 +++++++++++++++++++ libraries/avatars/src/AvatarData.h | 6 ++++++ libraries/script-engine/src/ScriptEngine.cpp | 1 + 3 files changed, 26 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 2263964e8f..bbd3e05994 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2309,3 +2309,22 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra vec3FromScriptValue(intersection, value.intersection); } } + +QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { + QScriptValue obj = engine->newObject(); + for (auto entityID : value.keys()) { + QScriptValue EntityProperties = engine->newVariant(QVariant::fromValue(value.value(entityID))); + QString key = entityID.toString(); + obj.setProperty(key, EntityProperties); + } + return obj; +} + +void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value) { + QScriptValueIterator itr(object); + while (itr.hasNext()) { + itr.next(); + QUuid EntityID = QUuid(itr.name()); + value[EntityID] = qvariant_cast(itr.value().data().toVariant()); + } +} diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 264da75de2..502ae06425 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -44,6 +44,7 @@ typedef unsigned long long quint64; #include #include #include +#include #include #include @@ -816,6 +817,11 @@ Q_DECLARE_METATYPE(RayToAvatarIntersectionResult) QScriptValue RayToAvatarIntersectionResultToScriptValue(QScriptEngine* engine, const RayToAvatarIntersectionResult& results); void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, RayToAvatarIntersectionResult& results); +Q_DECLARE_METATYPE(AvatarEntityMap) + +QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value); +void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& value); + // faux joint indexes (-1 means invalid) const int SENSOR_TO_WORLD_MATRIX_INDEX = 65534; // -2 const int CONTROLLER_RIGHTHAND_INDEX = 65533; // -3 diff --git a/libraries/script-engine/src/ScriptEngine.cpp b/libraries/script-engine/src/ScriptEngine.cpp index f1ff4c4686..83f2f5ccc0 100644 --- a/libraries/script-engine/src/ScriptEngine.cpp +++ b/libraries/script-engine/src/ScriptEngine.cpp @@ -576,6 +576,7 @@ void ScriptEngine::init() { qScriptRegisterMetaType(this, EntityItemIDtoScriptValue, EntityItemIDfromScriptValue); qScriptRegisterMetaType(this, RayToEntityIntersectionResultToScriptValue, RayToEntityIntersectionResultFromScriptValue); qScriptRegisterMetaType(this, RayToAvatarIntersectionResultToScriptValue, RayToAvatarIntersectionResultFromScriptValue); + qScriptRegisterMetaType(this, AvatarEntityMapToScriptValue, AvatarEntityMapFromScriptValue); qScriptRegisterSequenceMetaType>(this); qScriptRegisterSequenceMetaType>(this); From 3af05ece7de2b59e5f4540f33606957a1940cf80 Mon Sep 17 00:00:00 2001 From: kunalgosar Date: Tue, 7 Feb 2017 13:28:09 -0800 Subject: [PATCH 45/48] Made QScript Object more JS friendly --- libraries/avatars/src/AvatarData.cpp | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index bbd3e05994..4ea3d8bb3e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include "AvatarLogging.h" @@ -2313,9 +2314,18 @@ void RayToAvatarIntersectionResultFromScriptValue(const QScriptValue& object, Ra QScriptValue AvatarEntityMapToScriptValue(QScriptEngine* engine, const AvatarEntityMap& value) { QScriptValue obj = engine->newObject(); for (auto entityID : value.keys()) { - QScriptValue EntityProperties = engine->newVariant(QVariant::fromValue(value.value(entityID))); + QByteArray entityProperties = value.value(entityID); + QJsonDocument jsonEntityProperties = QJsonDocument::fromBinaryData(entityProperties); + if (!jsonEntityProperties.isObject()) { + qCDebug(avatars) << "bad AvatarEntityData in AvatarEntityMap" << QString(entityProperties.toHex()); + } + + QVariant variantEntityProperties = jsonEntityProperties.toVariant(); + QVariantMap entityPropertiesMap = variantEntityProperties.toMap(); + QScriptValue scriptEntityProperties = variantMapToScriptValue(entityPropertiesMap, *engine); + QString key = entityID.toString(); - obj.setProperty(key, EntityProperties); + obj.setProperty(key, scriptEntityProperties); } return obj; } @@ -2325,6 +2335,12 @@ void AvatarEntityMapFromScriptValue(const QScriptValue& object, AvatarEntityMap& while (itr.hasNext()) { itr.next(); QUuid EntityID = QUuid(itr.name()); - value[EntityID] = qvariant_cast(itr.value().data().toVariant()); + + QScriptValue scriptEntityProperties = itr.value(); + QVariant variantEntityProperties = scriptEntityProperties.toVariant(); + QJsonDocument jsonEntityProperties = QJsonDocument::fromVariant(variantEntityProperties); + QByteArray binaryEntityProperties = jsonEntityProperties.toBinaryData(); + + value[EntityID] = binaryEntityProperties; } } From f61e16fccfe901116ffd75e56608c0be4ac7a6b5 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 11:01:55 -0800 Subject: [PATCH 46/48] more aggressive out of view data culling --- assignment-client/src/avatars/AvatarMixerSlave.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 1f9fbde89a..dfc91b90c1 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -367,7 +367,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { _stats.overBudgetAvatars++; detail = AvatarData::NoData; } else if (!isInView && !getsOutOfView) { - detail = AvatarData::MinimumData; + detail = AvatarData::NoData; nodeData->incrementAvatarOutOfView(); } else { detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO From 8cb8d686ecf33798645243e11b7c7e1c9cc68993 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 13:09:39 -0800 Subject: [PATCH 47/48] more cleanup and some CR feedback fixes --- .../src/avatars/AvatarMixerSlave.cpp | 395 ++++++++---------- 1 file changed, 182 insertions(+), 213 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index dfc91b90c1..a839784cd1 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -80,13 +80,6 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, static const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; -// only send extra avatar data (avatars out of view, ignored) every Nth AvatarData frame -// Extra avatar data will be sent (AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND/EXTRA_AVATAR_DATA_FRAME_RATIO) times -// per second. -// This value should be a power of two for performance purposes, as the mixer performs a modulo operation every frame -// to determine whether the extra data should be sent. -static const int EXTRA_AVATAR_DATA_FRAME_RATIO = 16; - // FIXME - There is some old logic (unchanged as of 2/17/17) that randomly decides to send an identity // packet. That logic had the following comment about the constants it uses... // @@ -156,6 +149,20 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // setup a PacketList for the avatarPackets auto avatarPacketList = NLPacketList::create(PacketType::BulkAvatarData); + // Define the minimum bubble size + static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f); + // Define the scale of the box for the current node + glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f; + // Set up the bounding box for the current node + AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) { + nodeBox.setScaleStayCentered(minBubbleSize); + } + // Quadruple the scale of both bounding boxes + nodeBox.embiggen(4.0f); + + // setup list of AvatarData as well as maps to map betweeen the AvatarData and the original nodes // for calling the AvatarData::sortAvatars() function and getting our sorted list of client nodes QList avatarList; @@ -170,7 +177,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { avatarList << otherAvatar; avatarDataToNodes[otherAvatar] = otherNode; } - }); AvatarSharedPointer thisAvatar = nodeData->getAvatarSharedPointer(); @@ -191,8 +197,87 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { return glm::max(nodeBoxHalfScale.x, glm::max(nodeBoxHalfScale.y, nodeBoxHalfScale.z)); }, - [thisAvatar](AvatarSharedPointer avatar)->bool{ - return (avatar == thisAvatar); // ignore ourselves... + [&](AvatarSharedPointer avatar)->bool{ + if (avatar == thisAvatar) { + return true; // ignore ourselves... + } + + bool shouldIgnore = false; + + // We will also ignore other nodes for a couple of different reasons: + // 1) ignore bubbles and ignore specific node + // 2) the node hasn't really updated it's frame data recently, this can + // happen if for example the avatar is connected on a desktop and sending + // updates at ~30hz. So every 3 frames we skip a frame. + auto avatarNode = avatarDataToNodes[avatar]; + if (avatarNode) { + const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); + if (avatarNodeData) { + quint64 startIgnoreCalculation = usecTimestampNow(); + + // make sure we have data for this avatar, that it isn't the same node, + // and isn't an avatar that the viewing node has ignored + // or that has ignored the viewing node + if (!avatarNode->getLinkedData() + || avatarNode->getUUID() == node->getUUID() + || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !getsIgnoredByMe) + || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + shouldIgnore = true; + } else { + + // Check to see if the space bubble is enabled + if (node->isIgnoreRadiusEnabled() || avatarNode->isIgnoreRadiusEnabled()) { + + // Define the scale of the box for the current other node + glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + // Set up the bounding box for the current other node + AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { + otherNodeBox.setScaleStayCentered(minBubbleSize); + } + // Quadruple the scale of both bounding boxes + otherNodeBox.embiggen(4.0f); + + // Perform the collision check between the two bounding boxes + if (nodeBox.touches(otherNodeBox)) { + nodeData->ignoreOther(node, avatarNode); + shouldIgnore = !getsAnyIgnored; + } + } + // Not close enough to ignore + if (!shouldIgnore) { + nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); + } + } + quint64 endIgnoreCalculation = usecTimestampNow(); + _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + + if (!shouldIgnore) { + AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); + AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); + + // FIXME - This code does appear to be working. But it seems brittle. + // It supports determining if the frame of data for this "other" + // avatar has already been sent to the reciever. This has been + // verified to work on a desktop display that renders at 60hz and + // therefore sends to mixer at 30hz. Each second you'd expect to + // have 15 (45hz-30hz) duplicate frames. In this case, the stat + // avg_other_av_skips_per_second does report 15. + // + // make sure we haven't already sent this data from this sender to this receiver + // or that somehow we haven't sent + if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { + ++numAvatarsHeldBack; + shouldIgnore = true; + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + // this is a skip - we still send the packet but capture the presence of the skip so we see it happening + ++numAvatarsWithSkippedFrames; + } + } + } + } + return shouldIgnore; }); // loop through our sorted avatars and allocate our bandwidth to them accordingly @@ -219,223 +304,107 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // NOTE: Here's where we determine if we are over budget and drop to bare minimum data int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars; bool overBudget = (identityBytesSent + numAvatarDataBytes + minimRemainingAvatarBytes) > maxAvatarBytesPerFrame; - /** - qDebug() << "Budget debugging:" - << "numAvatarDataBytes:" << numAvatarDataBytes << "\n" - << "identityBytesSent:" << identityBytesSent << "\n" - << "remainingAvatars:" << remainingAvatars << "\n" - << "minimumBytesPerAvatar:" << minimumBytesPerAvatar << "\n" - << "minimRemainingAvatarBytes:" << minimRemainingAvatarBytes << "\n" - << "overBudget:" << overBudget << "\n" - << "overBudgetAvatars:" << overBudgetAvatars; - **/ - bool shouldConsider = false; - quint64 startIgnoreCalculation = usecTimestampNow(); + quint64 startAvatarDataPacking = usecTimestampNow(); - // make sure we have data for this avatar, that it isn't the same node, - // and isn't an avatar that the viewing node has ignored - // or that has ignored the viewing node - if (!otherNode->getLinkedData() - || otherNode->getUUID() == node->getUUID() - || (node->isIgnoringNodeWithID(otherNode->getUUID()) && !getsIgnoredByMe) - || (otherNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + ++numOtherAvatars; - shouldConsider = false; + const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + + // make sure we send out identity packets to and from new arrivals. + bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID()); + + // FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..." + if (!overBudget + && otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 + && (forceSend + || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp + || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { + + identityBytesSent += sendIdentityPacket(otherNodeData, node); + } + + const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); + glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); + + // determine if avatar is in view, to determine how much data to include... + glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + bool isInView = nodeData->otherAvatarInView(otherNodeBox); + + // start a new segment in the PacketList for this avatar + avatarPacketList->startSegment(); + + AvatarData::AvatarDataDetail detail; + + if (overBudget) { + overBudgetAvatars++; + _stats.overBudgetAvatars++; + detail = AvatarData::NoData; + } else if (!isInView && !getsOutOfView) { + detail = AvatarData::NoData; + nodeData->incrementAvatarOutOfView(); } else { - const AvatarMixerClientData* otherData = reinterpret_cast(otherNode->getLinkedData()); - - shouldConsider = true; // assume we will consider... - - // Check to see if the space bubble is enabled - if (node->isIgnoreRadiusEnabled() || otherNode->isIgnoreRadiusEnabled()) { - // Define the minimum bubble size - static const glm::vec3 minBubbleSize = glm::vec3(0.3f, 1.3f, 0.3f); - // Define the scale of the box for the current node - glm::vec3 nodeBoxScale = (nodeData->getPosition() - nodeData->getGlobalBoundingBoxCorner()) * 2.0f; - // Define the scale of the box for the current other node - glm::vec3 otherNodeBoxScale = (otherData->getPosition() - otherData->getGlobalBoundingBoxCorner()) * 2.0f; - - // Set up the bounding box for the current node - AABox nodeBox(nodeData->getGlobalBoundingBoxCorner(), nodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(nodeBoxScale, minBubbleSize))) { - nodeBox.setScaleStayCentered(minBubbleSize); - } - // Set up the bounding box for the current other node - AABox otherNodeBox(otherData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { - otherNodeBox.setScaleStayCentered(minBubbleSize); - } - // Quadruple the scale of both bounding boxes - nodeBox.embiggen(4.0f); - otherNodeBox.embiggen(4.0f); - - // Perform the collision check between the two bounding boxes - if (nodeBox.touches(otherNodeBox)) { - nodeData->ignoreOther(node, otherNode); - shouldConsider = getsAnyIgnored; - } - } - // Not close enough to ignore - if (shouldConsider) { - nodeData->removeFromRadiusIgnoringSet(node, otherNode->getUUID()); - } - - quint64 endIgnoreCalculation = usecTimestampNow(); - _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO + ? AvatarData::SendAllData : AvatarData::CullSmallData; + nodeData->incrementAvatarInView(); } - if (shouldConsider) { - quint64 startAvatarDataPacking = usecTimestampNow(); + bool includeThisAvatar = true; + auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); + QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); + bool distanceAdjust = true; + glm::vec3 viewerPosition = myPosition; + AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray + bool dropFaceTracking = false; - ++numOtherAvatars; - - const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); - - // make sure we send out identity packets to and from new arrivals. - bool forceSend = !nodeData->checkAndSetHasReceivedFirstPacketsFrom(otherNode->getUUID()); - - // FIXME - this clause seems suspicious "... || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp ..." - if (!overBudget - && otherNodeData->getIdentityChangeTimestamp().time_since_epoch().count() > 0 - && (forceSend - || otherNodeData->getIdentityChangeTimestamp() > _lastFrameTimestamp - || distribution(generator) < IDENTITY_SEND_PROBABILITY)) { - - identityBytesSent += sendIdentityPacket(otherNodeData, node); - } - - const AvatarData* otherAvatar = otherNodeData->getConstAvatarData(); - // Decide whether to send this avatar's data based on it's distance from us - - glm::vec3 otherPosition = otherAvatar->getClientGlobalPosition(); - float distanceToAvatar = glm::length(myPosition - otherPosition); - - if (shouldConsider) { - AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(otherNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = otherNodeData->getLastReceivedSequenceNumber(); - - // FIXME - This code does appear to be working. But it seems brittle. - // It supports determining if the frame of data for this "other" - // avatar has already been sent to the reciever. This has been - // verified to work on a desktop display that renders at 60hz and - // therefore sends to mixer at 30hz. Each second you'd expect to - // have 15 (45hz-30hz) duplicate frames. In this case, the stat - // avg_other_av_skips_per_second does report 15. - // - // make sure we haven't already sent this data from this sender to this receiver - // or that somehow we haven't sent - if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { - ++numAvatarsHeldBack; - - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - shouldConsider = false; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { - // this is a skip - we still send the packet but capture the presence of the skip so we see it happening - ++numAvatarsWithSkippedFrames; - } - - // we're going to send this avatar - if (shouldConsider) { - - // determine if avatar is in view, to determine how much data to include... - glm::vec3 otherNodeBoxScale = (otherPosition - otherNodeData->getGlobalBoundingBoxCorner()) * 2.0f; - AABox otherNodeBox(otherNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - bool isInView = nodeData->otherAvatarInView(otherNodeBox); - - // this throttles the extra data to only be sent every Nth message - /** - if (!isInView && !getsOutOfView && (lastSeqToReceiver % EXTRA_AVATAR_DATA_FRAME_RATIO > 0)) { - quint64 endAvatarDataPacking = usecTimestampNow(); - - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - shouldConsider = false; - } - ***/ - - if (shouldConsider) { - // start a new segment in the PacketList for this avatar - avatarPacketList->startSegment(); - - AvatarData::AvatarDataDetail detail; - - if (overBudget) { - overBudgetAvatars++; - _stats.overBudgetAvatars++; - detail = AvatarData::NoData; - } else if (!isInView && !getsOutOfView) { - detail = AvatarData::NoData; - nodeData->incrementAvatarOutOfView(); - } else { - detail = distribution(generator) < AVATAR_SEND_FULL_UPDATE_RATIO - ? AvatarData::SendAllData : AvatarData::CullSmallData; - nodeData->incrementAvatarInView(); - } - - { - bool includeThisAvatar = true; - auto lastEncodeForOther = nodeData->getLastOtherAvatarEncodeTime(otherNode->getUUID()); - QVector& lastSentJointsForOther = nodeData->getLastOtherAvatarSentJoints(otherNode->getUUID()); - bool distanceAdjust = true; - glm::vec3 viewerPosition = myPosition; - AvatarDataPacket::HasFlags hasFlagsOut; // the result of the toByteArray - bool dropFaceTracking = false; - - quint64 start = usecTimestampNow(); - QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - quint64 end = usecTimestampNow(); - _stats.toByteArrayElapsedTime += (end - start); - - static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data"; - - dropFaceTracking = true; // first try dropping the facial data - bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData"; - bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther, + quint64 start = usecTimestampNow(); + QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - } + quint64 end = usecTimestampNow(); + _stats.toByteArrayElapsedTime += (end - start); - if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { - qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!"; - includeThisAvatar = false; - } - } + static const int MAX_ALLOWED_AVATAR_DATA = (1400 - NUM_BYTES_RFC4122_UUID); + if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { + qCWarning(avatars) << "otherAvatar.toByteArray() resulted in very large buffer:" << bytes.size() << "... attempt to drop facial data"; - if (includeThisAvatar) { - numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); - numAvatarDataBytes += avatarPacketList->write(bytes); - _stats.numOthersIncluded++; + dropFaceTracking = true; // first try dropping the facial data + bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, + hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); - // increment the number of avatars sent to this reciever - nodeData->incrementNumAvatarsSentLastFrame(); + if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { + qCWarning(avatars) << "otherAvatar.toByteArray() without facial data resulted in very large buffer:" << bytes.size() << "... reduce to MinimumData"; + bytes = otherAvatar->toByteArray(AvatarData::MinimumData, lastEncodeForOther, lastSentJointsForOther, + hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); + } - // set the last sent sequence number for this sender on the receiver - nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), - otherNodeData->getLastReceivedSequenceNumber()); - - // remember the last time we sent details about this other node to the receiver - nodeData->setLastBroadcastTime(otherNode->getUUID(), start); - - } - } - - avatarPacketList->endSegment(); - - quint64 endAvatarDataPacking = usecTimestampNow(); - _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); - } - } + if (bytes.size() > MAX_ALLOWED_AVATAR_DATA) { + qCWarning(avatars) << "otherAvatar.toByteArray() MinimumData resulted in very large buffer:" << bytes.size() << "... FAIL!!"; + includeThisAvatar = false; } } + + if (includeThisAvatar) { + numAvatarDataBytes += avatarPacketList->write(otherNode->getUUID().toRfc4122()); + numAvatarDataBytes += avatarPacketList->write(bytes); + _stats.numOthersIncluded++; + + // increment the number of avatars sent to this reciever + nodeData->incrementNumAvatarsSentLastFrame(); + + // set the last sent sequence number for this sender on the receiver + nodeData->setLastBroadcastSequenceNumber(otherNode->getUUID(), + otherNodeData->getLastReceivedSequenceNumber()); + + // remember the last time we sent details about this other node to the receiver + nodeData->setLastBroadcastTime(otherNode->getUUID(), start); + + } + + avatarPacketList->endSegment(); + + quint64 endAvatarDataPacking = usecTimestampNow(); + _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); }; quint64 startPacketSending = usecTimestampNow(); From 1e91f74ce745929d6a502f7f7677640e64b617d6 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Thu, 23 Feb 2017 13:32:35 -0800 Subject: [PATCH 48/48] clean up some uneedded nullptr checks, make them asserts --- .../src/avatars/AvatarMixerSlave.cpp | 133 +++++++++--------- 1 file changed, 65 insertions(+), 68 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index a839784cd1..8d9a5e6951 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -171,6 +171,10 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { int listItem = 0; std::for_each(_begin, _end, [&](const SharedNodePointer& otherNode) { const AvatarMixerClientData* otherNodeData = reinterpret_cast(otherNode->getLinkedData()); + + // theoretically it's possible for a Node to be in the NodeList (and therefore end up here), + // but not have yet sent data that's linked to the node. Check for that case and don't + // consider those nodes. if (otherNodeData) { listItem++; AvatarSharedPointer otherAvatar = otherNodeData->getAvatarSharedPointer(); @@ -186,10 +190,8 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { [&](AvatarSharedPointer avatar)->uint64_t{ auto avatarNode = avatarDataToNodes[avatar]; - if (avatarNode) { - return nodeData->getLastBroadcastTime(avatarNode->getUUID()); - } - return 0; + assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map + return nodeData->getLastBroadcastTime(avatarNode->getUUID()); }, [&](AvatarSharedPointer avatar)->float{ @@ -210,72 +212,72 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { // happen if for example the avatar is connected on a desktop and sending // updates at ~30hz. So every 3 frames we skip a frame. auto avatarNode = avatarDataToNodes[avatar]; - if (avatarNode) { - const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); - if (avatarNodeData) { - quint64 startIgnoreCalculation = usecTimestampNow(); - // make sure we have data for this avatar, that it isn't the same node, - // and isn't an avatar that the viewing node has ignored - // or that has ignored the viewing node - if (!avatarNode->getLinkedData() - || avatarNode->getUUID() == node->getUUID() - || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !getsIgnoredByMe) - || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { - shouldIgnore = true; - } else { + assert(avatarNode); // we can't have gotten here without the avatarData being a valid key in the map - // Check to see if the space bubble is enabled - if (node->isIgnoreRadiusEnabled() || avatarNode->isIgnoreRadiusEnabled()) { + const AvatarMixerClientData* avatarNodeData = reinterpret_cast(avatarNode->getLinkedData()); + assert(avatarNodeData); // we can't have gotten here without avatarNode having valid data + quint64 startIgnoreCalculation = usecTimestampNow(); - // Define the scale of the box for the current other node - glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; - // Set up the bounding box for the current other node - AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); - // Clamp the size of the bounding box to a minimum scale - if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { - otherNodeBox.setScaleStayCentered(minBubbleSize); - } - // Quadruple the scale of both bounding boxes - otherNodeBox.embiggen(4.0f); + // make sure we have data for this avatar, that it isn't the same node, + // and isn't an avatar that the viewing node has ignored + // or that has ignored the viewing node + if (!avatarNode->getLinkedData() + || avatarNode->getUUID() == node->getUUID() + || (node->isIgnoringNodeWithID(avatarNode->getUUID()) && !getsIgnoredByMe) + || (avatarNode->isIgnoringNodeWithID(node->getUUID()) && !getsAnyIgnored)) { + shouldIgnore = true; + } else { - // Perform the collision check between the two bounding boxes - if (nodeBox.touches(otherNodeBox)) { - nodeData->ignoreOther(node, avatarNode); - shouldIgnore = !getsAnyIgnored; - } - } - // Not close enough to ignore - if (!shouldIgnore) { - nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); - } + // Check to see if the space bubble is enabled + if (node->isIgnoreRadiusEnabled() || avatarNode->isIgnoreRadiusEnabled()) { + + // Define the scale of the box for the current other node + glm::vec3 otherNodeBoxScale = (avatarNodeData->getPosition() - avatarNodeData->getGlobalBoundingBoxCorner()) * 2.0f; + // Set up the bounding box for the current other node + AABox otherNodeBox(avatarNodeData->getGlobalBoundingBoxCorner(), otherNodeBoxScale); + // Clamp the size of the bounding box to a minimum scale + if (glm::any(glm::lessThan(otherNodeBoxScale, minBubbleSize))) { + otherNodeBox.setScaleStayCentered(minBubbleSize); } - quint64 endIgnoreCalculation = usecTimestampNow(); - _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + // Quadruple the scale of both bounding boxes + otherNodeBox.embiggen(4.0f); - if (!shouldIgnore) { - AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); - AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); - - // FIXME - This code does appear to be working. But it seems brittle. - // It supports determining if the frame of data for this "other" - // avatar has already been sent to the reciever. This has been - // verified to work on a desktop display that renders at 60hz and - // therefore sends to mixer at 30hz. Each second you'd expect to - // have 15 (45hz-30hz) duplicate frames. In this case, the stat - // avg_other_av_skips_per_second does report 15. - // - // make sure we haven't already sent this data from this sender to this receiver - // or that somehow we haven't sent - if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { - ++numAvatarsHeldBack; - shouldIgnore = true; - } else if (lastSeqFromSender - lastSeqToReceiver > 1) { - // this is a skip - we still send the packet but capture the presence of the skip so we see it happening - ++numAvatarsWithSkippedFrames; - } + // Perform the collision check between the two bounding boxes + if (nodeBox.touches(otherNodeBox)) { + nodeData->ignoreOther(node, avatarNode); + shouldIgnore = !getsAnyIgnored; } } + // Not close enough to ignore + if (!shouldIgnore) { + nodeData->removeFromRadiusIgnoringSet(node, avatarNode->getUUID()); + } + } + quint64 endIgnoreCalculation = usecTimestampNow(); + _stats.ignoreCalculationElapsedTime += (endIgnoreCalculation - startIgnoreCalculation); + + if (!shouldIgnore) { + AvatarDataSequenceNumber lastSeqToReceiver = nodeData->getLastBroadcastSequenceNumber(avatarNode->getUUID()); + AvatarDataSequenceNumber lastSeqFromSender = avatarNodeData->getLastReceivedSequenceNumber(); + + // FIXME - This code does appear to be working. But it seems brittle. + // It supports determining if the frame of data for this "other" + // avatar has already been sent to the reciever. This has been + // verified to work on a desktop display that renders at 60hz and + // therefore sends to mixer at 30hz. Each second you'd expect to + // have 15 (45hz-30hz) duplicate frames. In this case, the stat + // avg_other_av_skips_per_second does report 15. + // + // make sure we haven't already sent this data from this sender to this receiver + // or that somehow we haven't sent + if (lastSeqToReceiver == lastSeqFromSender && lastSeqToReceiver != 0) { + ++numAvatarsHeldBack; + shouldIgnore = true; + } else if (lastSeqFromSender - lastSeqToReceiver > 1) { + // this is a skip - we still send the packet but capture the presence of the skip so we see it happening + ++numAvatarsWithSkippedFrames; + } } return shouldIgnore; }); @@ -284,7 +286,6 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { int avatarRank = 0; // this is overly conservative, because it includes some avatars we might not consider - // FIXME - move the ignore logic up into the sorting list so we get a better estimate int remainingAvatars = (int)sortedAvatars.size(); while (!sortedAvatars.empty()) { @@ -295,11 +296,7 @@ void AvatarMixerSlave::broadcastAvatarData(const SharedNodePointer& node) { remainingAvatars--; auto otherNode = avatarDataToNodes[avatarData]; - - // FIXME - should't this be an assert? - if (!otherNode) { - continue; - } + assert(otherNode); // we can't have gotten here without the avatarData being a valid key in the map // NOTE: Here's where we determine if we are over budget and drop to bare minimum data int minimRemainingAvatarBytes = minimumBytesPerAvatar * remainingAvatars;