From 09d33177fcfadabae4a85b5b778d65d412ec3925 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Mon, 6 Aug 2018 15:36:25 -0700 Subject: [PATCH 01/64] Adjust Workload and better representation of the workload debuging features --- .../render-utils/src/WorkloadResource.slh | 82 +++++++++++++ .../render-utils/src/drawWorkloadProxy.slf | 4 +- .../render-utils/src/drawWorkloadProxy.slv | 36 +----- .../render-utils/src/drawWorkloadView.slf | 4 +- .../render-utils/src/drawWorkloadView.slv | 44 +------ libraries/workload/src/workload/ViewTask.cpp | 19 ++- libraries/workload/src/workload/ViewTask.h | 6 +- .../developer/utilities/workload/workload.js | 109 +++++++++++++----- .../utilities/workload/workloadInspector.qml | 71 ++++++++++-- 9 files changed, 256 insertions(+), 119 deletions(-) create mode 100644 libraries/render-utils/src/WorkloadResource.slh diff --git a/libraries/render-utils/src/WorkloadResource.slh b/libraries/render-utils/src/WorkloadResource.slh new file mode 100644 index 0000000000..c1d0d21fa1 --- /dev/null +++ b/libraries/render-utils/src/WorkloadResource.slh @@ -0,0 +1,82 @@ +// +// Created by Sam Gateau on 7/31/2018. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// +<@if not WORKLOAD_RESOURCE_SLH@> +<@def WORKLOAD_RESOURCE_SLH@> + +<@include gpu/Color.slh@> +<$declareColorWheel()$> + +const vec4 REGION_COLOR[4] = vec4[4]( + vec4(0.0, 1.0, 0.0, 1.0), + vec4(1.0, 0.6, 0.0, 1.0), + vec4(1.0, 0.0, 0.0, 1.0), + vec4(0.3, 0.0, 0.8, 1.0) +); + +<@func declareWorkloadProxies() @> + +struct WorkloadProxy { + vec4 sphere; + vec4 region; +}; + +#if defined(GPU_GL410) +uniform samplerBuffer workloadProxiesBuffer; +WorkloadProxy getWorkloadProxy(int i) { + int offset = 2 * i; + WorkloadProxy proxy; + proxy.sphere = texelFetch(workloadProxiesBuffer, offset); + proxy.region = texelFetch(workloadProxiesBuffer, offset + 1); + return proxy; +} +#else +layout(std140) buffer workloadProxiesBuffer { + WorkloadProxy _proxies[]; +}; +WorkloadProxy getWorkloadProxy(int i) { + WorkloadProxy proxy = _proxies[i]; + return proxy; +} +#endif + +<@endfunc@> + + +<@func declareWorkloadViews() @> + +struct WorkloadView { + vec4 direction_far; + vec4 fov; + vec4 origin; + vec4 backFront[2]; + vec4 regions[3]; +}; + +#if defined(GPU_GL410) +uniform samplerBuffer workloadViewsBuffer; +WorkloadView getWorkloadView(int i) { + int offset = 2 * i; + WorkloadView view; + view.origin = texelFetch(workloadViewsBuffer, offset); + view.radiuses = texelFetch(workloadViewsBuffer, offset + 1); + return view; +} +#else +layout(std140) buffer workloadViewsBuffer { + WorkloadView _views[]; +}; +WorkloadView getWorkloadView(int i) { + WorkloadView view = _views[i]; + return view; +} +#endif + +<@endfunc@> + +<@endif@> + diff --git a/libraries/render-utils/src/drawWorkloadProxy.slf b/libraries/render-utils/src/drawWorkloadProxy.slf index 1304e68c7f..32dceab00a 100644 --- a/libraries/render-utils/src/drawWorkloadProxy.slf +++ b/libraries/render-utils/src/drawWorkloadProxy.slf @@ -15,11 +15,13 @@ in vec4 varColor; in vec3 varTexcoord; +in vec3 varEyePos; void main(void) { if (varColor.w > 0.0) { float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz)); - float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w); + float d = varColor.w / abs(varEyePos.z); + float a = paintStripe(r * d, 0.0, 1.0 / d, 0.002 / d); if (a <= 0.1 || r > 1.1) { discard; } diff --git a/libraries/render-utils/src/drawWorkloadProxy.slv b/libraries/render-utils/src/drawWorkloadProxy.slv index 64fb335fd6..1b91e349ab 100644 --- a/libraries/render-utils/src/drawWorkloadProxy.slv +++ b/libraries/render-utils/src/drawWorkloadProxy.slv @@ -15,40 +15,13 @@ <@include gpu/Transform.slh@> <$declareStandardTransform()$> -<@include gpu/Color.slh@> -<$declareColorWheel()$> - -uniform vec4 inColor; - - -struct WorkloadProxy { - vec4 sphere; - vec4 region; -}; - -#if defined(GPU_GL410) -uniform samplerBuffer workloadProxiesBuffer; -WorkloadProxy getWorkloadProxy(int i) { - int offset = 2 * i; - WorkloadProxy proxy; - proxy.sphere = texelFetch(workloadProxiesBuffer, offset); - proxy.region = texelFetch(workloadProxiesBuffer, offset + 1); - return proxy; -} -#else -layout(std140) buffer workloadProxiesBuffer { - WorkloadProxy _proxies[]; -}; -WorkloadProxy getWorkloadProxy(int i) { - WorkloadProxy proxy = _proxies[i]; - return proxy; -} -#endif - +<@include WorkloadResource.slh@> +<$declareWorkloadProxies()$> out vec4 varColor; out vec3 varTexcoord; +out vec3 varEyePos; void main(void) { const vec4 UNIT_SPRITE[3] = vec4[3]( @@ -79,6 +52,7 @@ void main(void) { vec3 dirY = vec3(0.0, 1.0, 0.0); vec4 pos = vec4(proxyPosEye.xyz + proxy.sphere.w * ( dirX * spriteVert.x + dirY * spriteVert.y /* + dirZ * spriteVert.z*/), 1.0); + varEyePos = pos.xyz; varTexcoord = spriteVert.xyz; <$transformEyeToClipPos(cam, pos, gl_Position)$> @@ -86,7 +60,7 @@ void main(void) { int region = floatBitsToInt(proxy.region.x); region = (0x000000FF & region); - varColor = vec4(colorWheel(float(region) / 4.0), proxy.sphere.w); + varColor = vec4(REGION_COLOR[region].xyz, proxy.sphere.w); if (region == 4) { gl_Position = vec4(0.0); diff --git a/libraries/render-utils/src/drawWorkloadView.slf b/libraries/render-utils/src/drawWorkloadView.slf index 1304e68c7f..a3779ad6f4 100644 --- a/libraries/render-utils/src/drawWorkloadView.slf +++ b/libraries/render-utils/src/drawWorkloadView.slf @@ -15,11 +15,13 @@ in vec4 varColor; in vec3 varTexcoord; +in vec3 varEyePos; void main(void) { if (varColor.w > 0.0) { float r = sqrt(dot(varTexcoord.xyz,varTexcoord.xyz)); - float a = paintStripe(r * varColor.w, 0.0, 1.0 / varColor.w, 0.05 / varColor.w); + float d = varColor.w / abs(varEyePos.z); + float a = paintStripe(r * d, 0.0, 1.0 / d, 0.005 / d); if (a <= 0.1 || r > 1.1) { discard; } diff --git a/libraries/render-utils/src/drawWorkloadView.slv b/libraries/render-utils/src/drawWorkloadView.slv index f5497d250c..3212bc055c 100644 --- a/libraries/render-utils/src/drawWorkloadView.slv +++ b/libraries/render-utils/src/drawWorkloadView.slv @@ -15,45 +15,12 @@ <@include gpu/Transform.slh@> <$declareStandardTransform()$> -<@include gpu/Color.slh@> -<$declareColorWheel()$> - -uniform vec4 inColor; - - -struct WorkloadView { - vec4 direction_far; - vec4 fov; - vec4 origin; - vec4 backFront[2]; - vec4 regions[3]; -}; - -#if defined(GPU_GL410) -uniform samplerBuffer workloadViewsBuffer; -WorkloadView getWorkloadView(int i) { - int offset = 2 * i; - WorkloadView view; - view.origin = texelFetch(workloadViewsBuffer, offset); - view.radiuses = texelFetch(workloadViewsBuffer, offset + 1); - return view; -} -#else -layout(std140) buffer workloadViewsBuffer { - WorkloadView _views[]; -}; -WorkloadView getWorkloadView(int i) { - WorkloadView view = _views[i]; - return view; -} -#endif - - - - +<@include WorkloadResource.slh@> +<$declareWorkloadViews()$> out vec4 varColor; out vec3 varTexcoord; +out vec3 varEyePos; const int NUM_VERTICES_PER_SEGMENT = 2; const int NUM_SEGMENT_PER_VIEW_REGION = 65; @@ -109,12 +76,13 @@ void main(void) { <$transformModelToEyeDir(cam, obj, originSpaceTan, tanEye)$> lateralDir = normalize(cross(vec3(0.0, 0.0, 1.0), normalize(tanEye))); - posEye.xyz += (0.05 * (regionID + 1)) * (-1.0 + 2.0 * float(segmentVertexID)) * lateralDir; + posEye.xyz += (0.005 * abs(posEye.z) * (regionID + 1)) * (-1.0 + 2.0 * float(segmentVertexID)) * lateralDir; + varEyePos = posEye.xyz; <$transformEyeToClipPos(cam, posEye, gl_Position)$> varTexcoord = spriteVert.xyz; // Convert region to color - varColor = vec4(colorWheel(float(regionID) / 4.0), -1.0); + varColor = vec4(REGION_COLOR[regionID].xyz, -1.0); } diff --git a/libraries/workload/src/workload/ViewTask.cpp b/libraries/workload/src/workload/ViewTask.cpp index 4b77733c52..5d9953cdf4 100644 --- a/libraries/workload/src/workload/ViewTask.cpp +++ b/libraries/workload/src/workload/ViewTask.cpp @@ -91,12 +91,24 @@ void ControlViews::run(const workload::WorkloadContextPointer& runContext, const regulateViews(outViews, inTimings); } - // Export the timings for debuging + // Export the ranges for debuging + bool doExport = false; + if (outViews.size()) { + _dataExport.ranges[workload::Region::R1] = outViews[0].regionBackFronts[workload::Region::R1]; + _dataExport.ranges[workload::Region::R2] = outViews[0].regionBackFronts[workload::Region::R2]; + _dataExport.ranges[workload::Region::R3] = outViews[0].regionBackFronts[workload::Region::R3]; + doExport = true; + } + + // Export the ranges and timings for debuging if (inTimings.size()) { _dataExport.timings[workload::Region::R1] = std::chrono::duration(inTimings[0]).count(); _dataExport.timings[workload::Region::R2] = _dataExport.timings[workload::Region::R1]; _dataExport.timings[workload::Region::R3] = std::chrono::duration(inTimings[1]).count(); + doExport = true; + } + if (doExport) { auto config = std::static_pointer_cast(runContext->jobConfig); config->dataExport = _dataExport; config->emitDirty(); @@ -129,11 +141,6 @@ void ControlViews::regulateViews(workload::Views& outViews, const workload::Timi regionBackFronts[workload::Region::R3] = regionRegulators[workload::Region::R3].run(loopDuration, timings[1], regionBackFronts[workload::Region::R3]); enforceRegionContainment(); - - _dataExport.ranges[workload::Region::R1] = regionBackFronts[workload::Region::R1]; - _dataExport.ranges[workload::Region::R2] = regionBackFronts[workload::Region::R2]; - _dataExport.ranges[workload::Region::R3] = regionBackFronts[workload::Region::R3]; - for (auto& outView : outViews) { outView.regionBackFronts[workload::Region::R1] = regionBackFronts[workload::Region::R1]; outView.regionBackFronts[workload::Region::R2] = regionBackFronts[workload::Region::R2]; diff --git a/libraries/workload/src/workload/ViewTask.h b/libraries/workload/src/workload/ViewTask.h index 867f22d534..c20314d514 100644 --- a/libraries/workload/src/workload/ViewTask.h +++ b/libraries/workload/src/workload/ViewTask.h @@ -32,9 +32,9 @@ namespace workload { }; const std::vector MAX_VIEW_BACK_FRONTS = { - { 100.0f, 200.0f }, - { 150.0f, 300.0f }, - { 250.0f, 500.0f } + { 100.0f, 1600.0f }, + { 150.0f, 10000.0f }, + { 250.0f, 16000.0f } }; const float RELATIVE_STEP_DOWN = 0.05f; diff --git a/scripts/developer/utilities/workload/workload.js b/scripts/developer/utilities/workload/workload.js index d74eb4e6d5..ce41e0ecb8 100644 --- a/scripts/developer/utilities/workload/workload.js +++ b/scripts/developer/utilities/workload/workload.js @@ -16,16 +16,9 @@ var ICON_URL = Script.resolvePath("../../../system/assets/images/luci-i.svg"); var ACTIVE_ICON_URL = Script.resolvePath("../../../system/assets/images/luci-a.svg"); - - var onAppScreen = false; + var onTablet = false; // set this to true to use the tablet, false use a floating window - function onClicked() { - if (onAppScreen) { - tablet.gotoHomeScreen(); - } else { - tablet.loadQMLSource(QMLAPP_URL); - } - } + var onAppScreen = false; var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system"); var button = tablet.addButton({ @@ -36,33 +29,82 @@ var hasEventBridge = false; - function wireEventBridge(on) { - if (!tablet) { - print("Warning in wireEventBridge(): 'tablet' undefined!"); - return; - } - if (on) { - if (!hasEventBridge) { - tablet.fromQml.connect(fromQml); - hasEventBridge = true; + var onScreen = false; + var window; + + function onClicked() { + if (onTablet) { + if (onAppScreen) { + tablet.gotoHomeScreen(); + } else { + tablet.loadQMLSource(QMLAPP_URL); } } else { - if (hasEventBridge) { - tablet.fromQml.disconnect(fromQml); - hasEventBridge = false; + if (onScreen) { + killWindow() + } else { + createWindow() + } + } + } + + function createWindow() { + var qml = Script.resolvePath(QMLAPP_URL); + window = Desktop.createWindow(Script.resolvePath(QMLAPP_URL), { + title: 'Workload Inspector', + flags: Desktop.ALWAYS_ON_TOP, + presentationMode: Desktop.PresentationMode.NATIVE, + size: {x: 400, y: 600} + }); + window.setPosition(200, 50); + window.closed.connect(killWindow); + window.fromQml.connect(fromQml); + onScreen = true + button.editProperties({isActive: true}); + } + + function killWindow() { + if (window !== undefined) { + window.closed.disconnect(killWindow); + window.fromQml.disconnect(fromQml); + window.close() + window = undefined + } + onScreen = false + button.editProperties({isActive: false}) + } + + function wireEventBridge(on) { + if (onTablet) { + if (!tablet) { + print("Warning in wireEventBridge(): 'tablet' undefined!"); + return; + } + if (on) { + if (!hasEventBridge) { + tablet.fromQml.connect(fromQml); + hasEventBridge = true; + } + } else { + if (hasEventBridge) { + tablet.fromQml.disconnect(fromQml); + hasEventBridge = false; + } } } } function onScreenChanged(type, url) { - if (url === QMLAPP_URL) { - onAppScreen = true; - } else { - onAppScreen = false; + if (onTablet) { + if (url === QMLAPP_URL) { + onAppScreen = true; + } else { + onAppScreen = false; + } + + button.editProperties({isActive: onAppScreen}); + wireEventBridge(onAppScreen); } - - button.editProperties({isActive: onAppScreen}); - wireEventBridge(onAppScreen); } function fromQml(message) { @@ -72,6 +114,7 @@ tablet.screenChanged.connect(onScreenChanged); Script.scriptEnding.connect(function () { + killWindow() if (onAppScreen) { tablet.gotoHomeScreen(); } @@ -113,8 +156,14 @@ } function sendToQml(message) { - tablet.sendToQml(message); + if (onTablet) { + tablet.sendToQml(message); + } else { + if (window) { + window.sendToQml(message); + } + } } updateGridInQML() -}()); \ No newline at end of file +}()); diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index 8076f5c1c2..c55fdee040 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -259,18 +259,71 @@ Rectangle { } ] } + /* PlotPerf { + title: "Ranges" + height: 100 + object: stats.controlViews + valueScale: 1.0 + valueUnit: "m" + plots: [ + { + prop: "r3RangeFront", + label: "R3 F", + color: "#FF0000" + }, + { + prop: "r3RangeBack", + label: "R3 B", + color: "#EF0000" + }, + { + prop: "r2RangeFront", + label: "R2 F", + color: "orange" + }, + { + prop: "r2RangeBack", + label: "R2 B", + color: "magenta" + }, + { + prop: "r1RangeFront", + label: "R1 F", + color: "#00FF00" + }, + { + prop: "r1RangeBack", + label: "R1 B", + color: "#00EF00" + }, + ] + }*/ Separator {} HifiControls.Label { - text: "Numbers:"; + text: "Ranges & Numbers:"; } - HifiControls.Label { - text: "R1= " + Workload.getConfig("regionState")["numR1"]; - } - HifiControls.Label { - text: "R2= " + Workload.getConfig("regionState")["numR2"]; - } - HifiControls.Label { - text: "R3= " + Workload.getConfig("regionState")["numR3"]; + Repeater { + model: [ + "green:R1:numR1:r1RangeBack:r1RangeFront", + "orange:R2:numR2:r2RangeBack:r2RangeFront", + "red:R3:numR3:r3RangeBack:r3RangeFront" + ] + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + HifiControls.Label { + text: modelData.split(":")[1] + " : " + Workload.getConfig("regionState")[modelData.split(":")[2]] ; + color: modelData.split(":")[0] + } + HifiControls.Label { + text: Workload.getConfig("controlViews")[modelData.split(":")[3]].toFixed(0) ; + color: modelData.split(":")[0] + } + HifiControls.Label { + text: Workload.getConfig("controlViews")[modelData.split(":")[4]].toFixed(0) ; + color: modelData.split(":")[0] + } + } } Separator {} From 274bf33318d06924fcdd6bd537f7723254c6792b Mon Sep 17 00:00:00 2001 From: amantley Date: Tue, 7 Aug 2018 18:29:51 -0700 Subject: [PATCH 02/64] fix for the avatar scale reset when we re-enter hmd mode --- interface/src/Application.cpp | 1 - interface/src/avatar/MyAvatar.cpp | 6 ++++++ interface/src/avatar/MySkeletonModel.cpp | 2 ++ libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 1 + libraries/render-utils/src/Model.cpp | 1 + 5 files changed, 10 insertions(+), 1 deletion(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0bbd2b1c17..d1871c7692 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8133,7 +8133,6 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { // reset the avatar, to set head and hand palms back to a reasonable default pose. getMyAvatar()->reset(false); - // switch to first person if entering hmd and setting is checked if (menu) { QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3f738ea4cb..0fdd7edb7a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -806,6 +806,7 @@ void MyAvatar::updateSensorToWorldMatrix() { // update the sensor mat so that the body position will end up in the desired // position when driven from the head. float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); + qCDebug(interfaceapp) << sensorToWorldScale; glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); @@ -1742,6 +1743,10 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); + glm::mat4 printout = getSensorToWorldMatrix(); + if (action == controller::Action::HEAD) { + //qCDebug(interfaceapp) << "matrix 4 sensor to world" << printout; + } if (pose.valid) { return pose.transform(getSensorToWorldMatrix()); } else { @@ -1751,6 +1756,7 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); + // qCDebug(interfaceapp) << " the head position in world frame is " << pose.getTranslation(); if (pose.valid) { glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 0fc5e7521e..6bc0c8fefa 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -116,7 +116,9 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); + if (avatarHeadPose.isValid()) { + //qCDebug(interfaceapp) << " the head position in avatar frame is " << avatarHeadPose.getTranslation(); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 69356cdfaa..0fda5be77b 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1761,6 +1761,7 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& // thread-safe float Avatar::getEyeHeight() const { + qCDebug(animation) << "modelscale "< Date: Wed, 8 Aug 2018 09:18:28 -0700 Subject: [PATCH 03/64] removed print statements --- interface/src/Application.cpp | 1 + interface/src/avatar/MyAvatar.cpp | 6 ------ interface/src/avatar/MySkeletonModel.cpp | 2 -- libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp | 1 - 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index d1871c7692..0bbd2b1c17 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -8133,6 +8133,7 @@ void Application::setDisplayPlugin(DisplayPluginPointer newDisplayPlugin) { // reset the avatar, to set head and hand palms back to a reasonable default pose. getMyAvatar()->reset(false); + // switch to first person if entering hmd and setting is checked if (menu) { QAction* action = menu->getActionForOption(newDisplayPlugin->getName()); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 0fdd7edb7a..3f738ea4cb 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -806,7 +806,6 @@ void MyAvatar::updateSensorToWorldMatrix() { // update the sensor mat so that the body position will end up in the desired // position when driven from the head. float sensorToWorldScale = getEyeHeight() / getUserEyeHeight(); - qCDebug(interfaceapp) << sensorToWorldScale; glm::mat4 desiredMat = createMatFromScaleQuatAndPos(glm::vec3(sensorToWorldScale), getWorldOrientation(), getWorldPosition()); _sensorToWorldMatrix = desiredMat * glm::inverse(_bodySensorMatrix); @@ -1743,10 +1742,6 @@ controller::Pose MyAvatar::getControllerPoseInSensorFrame(controller::Action act controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action action) const { auto pose = getControllerPoseInSensorFrame(action); - glm::mat4 printout = getSensorToWorldMatrix(); - if (action == controller::Action::HEAD) { - //qCDebug(interfaceapp) << "matrix 4 sensor to world" << printout; - } if (pose.valid) { return pose.transform(getSensorToWorldMatrix()); } else { @@ -1756,7 +1751,6 @@ controller::Pose MyAvatar::getControllerPoseInWorldFrame(controller::Action acti controller::Pose MyAvatar::getControllerPoseInAvatarFrame(controller::Action action) const { auto pose = getControllerPoseInWorldFrame(action); - // qCDebug(interfaceapp) << " the head position in world frame is " << pose.getTranslation(); if (pose.valid) { glm::mat4 invAvatarMatrix = glm::inverse(createMatFromQuatAndPos(getWorldOrientation(), getWorldPosition())); return pose.transform(invAvatarMatrix); diff --git a/interface/src/avatar/MySkeletonModel.cpp b/interface/src/avatar/MySkeletonModel.cpp index 6bc0c8fefa..0fc5e7521e 100644 --- a/interface/src/avatar/MySkeletonModel.cpp +++ b/interface/src/avatar/MySkeletonModel.cpp @@ -116,9 +116,7 @@ void MySkeletonModel::updateRig(float deltaTime, glm::mat4 parentTransform) { // input action is the highest priority source for head orientation. auto avatarHeadPose = myAvatar->getControllerPoseInAvatarFrame(controller::Action::HEAD); - if (avatarHeadPose.isValid()) { - //qCDebug(interfaceapp) << " the head position in avatar frame is " << avatarHeadPose.getTranslation(); AnimPose pose(avatarHeadPose.getRotation(), avatarHeadPose.getTranslation()); params.primaryControllerPoses[Rig::PrimaryControllerType_Head] = avatarToRigPose * pose; params.primaryControllerFlags[Rig::PrimaryControllerType_Head] = (uint8_t)Rig::ControllerFlags::Enabled; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0fda5be77b..69356cdfaa 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -1761,7 +1761,6 @@ void Avatar::ensureInScene(AvatarSharedPointer self, const render::ScenePointer& // thread-safe float Avatar::getEyeHeight() const { - qCDebug(animation) << "modelscale "< Date: Wed, 8 Aug 2018 09:22:31 -0700 Subject: [PATCH 04/64] fixing the ui script and trying to get shadow --- interface/src/workload/GameWorkloadRenderer.cpp | 2 +- libraries/render-utils/src/WorkloadResource.slh | 8 ++++---- scripts/developer/utilities/workload/workload.js | 7 +++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/interface/src/workload/GameWorkloadRenderer.cpp b/interface/src/workload/GameWorkloadRenderer.cpp index ae0e0abbeb..bbbc24c2c1 100644 --- a/interface/src/workload/GameWorkloadRenderer.cpp +++ b/interface/src/workload/GameWorkloadRenderer.cpp @@ -97,7 +97,7 @@ namespace render { } } -GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) { +GameWorkloadRenderItem::GameWorkloadRenderItem() : _key(render::ItemKey::Builder::opaqueShape().withShadowCaster().withTagBits(render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1)) { } render::ItemKey GameWorkloadRenderItem::getKey() const { diff --git a/libraries/render-utils/src/WorkloadResource.slh b/libraries/render-utils/src/WorkloadResource.slh index c1d0d21fa1..81b6ed78ce 100644 --- a/libraries/render-utils/src/WorkloadResource.slh +++ b/libraries/render-utils/src/WorkloadResource.slh @@ -26,7 +26,7 @@ struct WorkloadProxy { }; #if defined(GPU_GL410) -uniform samplerBuffer workloadProxiesBuffer; +layout(binding=0) uniform samplerBuffer workloadProxiesBuffer; WorkloadProxy getWorkloadProxy(int i) { int offset = 2 * i; WorkloadProxy proxy; @@ -35,7 +35,7 @@ WorkloadProxy getWorkloadProxy(int i) { return proxy; } #else -layout(std140) buffer workloadProxiesBuffer { +layout(std140, binding=0) buffer workloadProxiesBuffer { WorkloadProxy _proxies[]; }; WorkloadProxy getWorkloadProxy(int i) { @@ -58,7 +58,7 @@ struct WorkloadView { }; #if defined(GPU_GL410) -uniform samplerBuffer workloadViewsBuffer; +layout(binding=1) uniform samplerBuffer workloadViewsBuffer; WorkloadView getWorkloadView(int i) { int offset = 2 * i; WorkloadView view; @@ -67,7 +67,7 @@ WorkloadView getWorkloadView(int i) { return view; } #else -layout(std140) buffer workloadViewsBuffer { +layout(std140, binding=1) buffer workloadViewsBuffer { WorkloadView _views[]; }; WorkloadView getWorkloadView(int i) { diff --git a/scripts/developer/utilities/workload/workload.js b/scripts/developer/utilities/workload/workload.js index ce41e0ecb8..bdd33dcc5a 100644 --- a/scripts/developer/utilities/workload/workload.js +++ b/scripts/developer/utilities/workload/workload.js @@ -56,7 +56,7 @@ presentationMode: Desktop.PresentationMode.NATIVE, size: {x: 400, y: 600} }); - window.setPosition(200, 50); + // window.setPosition(200, 50); window.closed.connect(killWindow); window.fromQml.connect(fromQml); onScreen = true @@ -107,9 +107,7 @@ } } - function fromQml(message) { - } - + button.clicked.connect(onClicked); tablet.screenChanged.connect(onScreenChanged); @@ -127,6 +125,7 @@ Script.include("./test_physics_scene.js") function fromQml(message) { + print("fromQml: " + JSON.stringify(message)) switch (message.method) { case "createScene": createScene(); From a85ed2a2f55e1edcaa352f1d91f196261291dcd8 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Mon, 6 Aug 2018 22:35:20 +0300 Subject: [PATCH 05/64] show 'edit wearables' button even if avatar doesn't have any wearables --- interface/resources/qml/hifi/AvatarApp.qml | 23 --------------- .../qml/hifi/avatarapp/MessageBoxes.qml | 29 ------------------- 2 files changed, 52 deletions(-) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index b7e1adda70..bd3f8fd1cf 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -491,33 +491,10 @@ Rectangle { anchors.verticalCenter: wearablesLabel.verticalCenter glyphText: "\ue02e" - visible: avatarWearablesCount !== 0 onClicked: { adjustWearables.open(currentAvatar); } } - - // TextStyle3 - RalewayRegular { - size: 22; - anchors.right: parent.right - anchors.verticalCenter: wearablesLabel.verticalCenter - font.underline: true - text: "Add" - color: 'black' - visible: avatarWearablesCount === 0 - - MouseArea { - anchors.fill: parent - onClicked: { - popup.showGetWearables(function() { - emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'}) - }, function(link) { - emitSendToScript({'method' : 'navigate', 'url' : link}) - }); - } - } - } } Rectangle { diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 90f55fd8bb..c9b57abe66 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -33,35 +33,6 @@ MessageBox { popup.inputText.forceActiveFocus(); } - property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' - - function showGetWearables(callback, linkCallback) { - popup.button2text = 'AvatarIsland' - popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; - popup.button1text = 'CANCEL' - popup.titleText = 'Get Wearables' - popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearables from My Purchases.' + '
' + '
' + - 'Visit “AvatarIsland” to get wearables' - - popup.imageSource = getWearablesUrl; - popup.onButton2Clicked = function() { - popup.close(); - - if(callback) - callback(); - } - - popup.onLinkClicked = function(link) { - popup.close(); - - if(linkCallback) - linkCallback(link); - } - - popup.open(); - } - function showDeleteFavorite(favoriteName, callback) { popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' From f8c1ba48ad19d4938a128e281e1e8dbe28adc806 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Mon, 6 Aug 2018 23:55:06 +0300 Subject: [PATCH 06/64] update UI accordingly to the updated mockup --- .../qml/hifi/avatarapp/AdjustWearables.qml | 176 ++++++++++++------ 1 file changed, 119 insertions(+), 57 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 39344b04bc..909f7c886f 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -115,64 +115,111 @@ Rectangle { Column { anchors.top: parent.top - anchors.topMargin: 15 + anchors.topMargin: 12 anchors.horizontalCenter: parent.horizontalCenter spacing: 20 - width: parent.width - 30 * 2 + width: parent.width - 22 * 2 - HifiControlsUit.ComboBox { - id: wearablesCombobox - anchors.left: parent.left - anchors.right: parent.right - comboBox.textRole: "text" + Column { + width: parent.width - model: ListModel { - function findIndexById(id) { + Row { + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Wearable" + anchors.verticalCenter: parent.verticalCenter + } - for(var i = 0; i < count; ++i) { - var wearable = get(i); - if(wearable.id === id) { - return i; - } - } + spacing: 10 - return -1; + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Add custom" + linkColor: hifi.colors.blueHighlight + anchors.verticalCenter: parent.verticalCenter } } - comboBox.onCurrentIndexChanged: { - var currentWearable = getCurrentWearable(); + HifiControlsUit.ComboBox { + id: wearablesCombobox + anchors.left: parent.left + anchors.right: parent.right + comboBox.textRole: "text" - if(currentWearable) { - position.set(currentWearable.localPosition); - rotation.set(currentWearable.localRotationAngles); - scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) + model: ListModel { + function findIndexById(id) { - wearableSelected(currentWearable.id); + for(var i = 0; i < count; ++i) { + var wearable = get(i); + if(wearable.id === id) { + return i; + } + } + + return -1; + } + } + + comboBox.onCurrentIndexChanged: { + var currentWearable = getCurrentWearable(); + + if(currentWearable) { + position.set(currentWearable.localPosition); + rotation.set(currentWearable.localRotationAngles); + scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) + + wearableSelected(currentWearable.id); + } + } + } + } + + Column { + width: parent.width + + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Joint" + } + + HifiControlsUit.ComboBox { + id: jointsCombobox + anchors.left: parent.left + anchors.right: parent.right + comboBox.textRole: "text" + + model: ListModel { } } } Column { width: parent.width - spacing: 5 Row { spacing: 20 // TextStyle5 - FiraSansSemiBold { + RalewayBold { id: positionLabel - size: 22; + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; text: "Position" } // TextStyle7 - FiraSansRegular { - size: 18; + RalewayBold { + size: 15; lineHeightMode: Text.FixedHeight - lineHeight: 16.9; + lineHeight: 18; text: "m" anchors.verticalCenter: positionLabel.verticalCenter } @@ -214,23 +261,24 @@ Rectangle { Column { width: parent.width - spacing: 5 Row { spacing: 20 // TextStyle5 - FiraSansSemiBold { + RalewayBold { id: rotationLabel - size: 22; + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; text: "Rotation" } // TextStyle7 - FiraSansRegular { - size: 18; + RalewayBold { + size: 15; lineHeightMode: Text.FixedHeight - lineHeight: 16.9; + lineHeight: 18; text: "deg" anchors.verticalCenter: rotationLabel.verticalCenter } @@ -270,19 +318,30 @@ Rectangle { } } - Column { + Item { width: parent.width - spacing: 5 + height: childrenRect.height - // TextStyle5 - FiraSansSemiBold { - size: 22; - text: "Scale" + HifiControlsUit.CheckBox { + text: "Is soft" + labelFontSize: 15 + labelFontWeight: Font.Bold + color: Qt.black + y: scalespinner.y } - Item { - width: parent.width - height: childrenRect.height + Column { + id: scalesColumn + anchors.right: parent.right + + // TextStyle5 + RalewayBold { + id: scaleLabel + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Scale" + } HifiControlsUit.SpinBox { id: scalespinner @@ -320,26 +379,29 @@ Rectangle { wearableUpdated(currentWearable.id, wearablesCombobox.currentIndex, properties); } } - - HifiControlsUit.Button { - fontSize: 18 - height: 40 - anchors.right: parent.right - color: hifi.buttons.red; - colorScheme: hifi.colorSchemes.light; - text: "TAKE IT OFF" - onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id); - enabled: wearablesCombobox.model.count !== 0 - anchors.verticalCenter: scalespinner.verticalCenter - } } + } + Column { + width: parent.width + + HifiControlsUit.Button { + fontSize: 18 + height: 40 + width: scalespinner.width + anchors.right: parent.right + color: hifi.buttons.red; + colorScheme: hifi.colorSchemes.light; + text: "TAKE IT OFF" + onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id); + enabled: wearablesCombobox.model.count !== 0 + } } } DialogButtons { anchors.bottom: parent.bottom - anchors.bottomMargin: 30 + anchors.bottomMargin: 57 anchors.left: parent.left anchors.leftMargin: 30 anchors.right: parent.right From 550e39ac8498a79a6d6cf9f49ece488bddb179e0 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 8 Aug 2018 21:41:10 +0300 Subject: [PATCH 07/64] add possibility to change joints using combobox --- .../qml/hifi/avatarapp/AdjustWearables.qml | 33 +++++++++++++++++-- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 909f7c886f..8ea3827f9c 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -42,7 +42,7 @@ Rectangle { var wearable = avatar.wearables.get(i).properties; for(var j = (wearable.modelURL.length - 1); j >= 0; --j) { if(wearable.modelURL[j] === '/') { - wearable.text = wearable.modelURL.substring(j + 1) + ' [%jointIndex%]'.replace('%jointIndex%', jointNames[wearable.parentJointIndex]); + wearable.text = wearable.modelURL.substring(j + 1); break; } } @@ -50,6 +50,9 @@ Rectangle { } wearablesCombobox.currentIndex = 0; + if(wearablesCombobox.model.count !== 0) { + jointsCombobox.set(getCurrentWearable().parentJointIndex) + } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { @@ -172,6 +175,7 @@ Rectangle { position.set(currentWearable.localPosition); rotation.set(currentWearable.localRotationAngles); scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) + jointsCombobox.set(currentWearable.parentJointIndex) wearableSelected(currentWearable.id); } @@ -193,9 +197,32 @@ Rectangle { id: jointsCombobox anchors.left: parent.left anchors.right: parent.right - comboBox.textRole: "text" - model: ListModel { + model: jointNames + property bool notify: false + + function set(jointIndex) { + notify = false; + currentIndex = jointIndex; + notify = true; + } + + function notifyJointChanged() { + modified = true; + var properties = { + parentJointIndex: currentIndex, + localPosition: { + x: position.xvalue, + y: position.yvalue, + z: position.zvalue + } + }; + + wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties); + } + + onCurrentIndexChanged: { + if(notify) notifyJointChanged(); } } } From da68a8906eb9bde610058015439cf0ef46a0a809 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 8 Aug 2018 23:56:01 +0300 Subject: [PATCH 08/64] show displayText if specified (to allow override combobox current value) --- interface/resources/qml/controls-uit/ComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/ComboBox.qml b/interface/resources/qml/controls-uit/ComboBox.qml index be8c9f6740..245b565a62 100644 --- a/interface/resources/qml/controls-uit/ComboBox.qml +++ b/interface/resources/qml/controls-uit/ComboBox.qml @@ -103,7 +103,7 @@ FocusScope { verticalCenter: parent.verticalCenter } size: hifi.fontSizes.textFieldInput - text: comboBox.currentText + text: comboBox.displayText ? comboBox.displayText : comboBox.currentText elide: Text.ElideRight color: comboBox.hovered || comboBox.popup.visible ? hifi.colors.baseGray : (isLightColorScheme ? hifi.colors.lightGray : hifi.colors.lightGrayText ) } From 56194bba8e69d8946543f2fb42af52fc303928f6 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 8 Aug 2018 23:57:08 +0300 Subject: [PATCH 09/64] bind 'is soft' to backend API, always show 'Hips' for soften entity --- .../qml/hifi/avatarapp/AdjustWearables.qml | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 8ea3827f9c..158757770c 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -48,11 +48,7 @@ Rectangle { } wearablesCombobox.model.append(wearable); } - wearablesCombobox.currentIndex = 0; - if(wearablesCombobox.model.count !== 0) { - jointsCombobox.set(getCurrentWearable().parentJointIndex) - } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { @@ -176,6 +172,7 @@ Rectangle { rotation.set(currentWearable.localRotationAngles); scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) jointsCombobox.set(currentWearable.parentJointIndex) + isSoft.set(currentWearable.relayParentJoints) wearableSelected(currentWearable.id); } @@ -197,6 +194,8 @@ Rectangle { id: jointsCombobox anchors.left: parent.left anchors.right: parent.right + enabled: !isSoft.checked + comboBox.displayText: isSoft.checked ? 'Hips' : comboBox.currentText model: jointNames property bool notify: false @@ -350,11 +349,31 @@ Rectangle { height: childrenRect.height HifiControlsUit.CheckBox { + id: isSoft text: "Is soft" labelFontSize: 15 labelFontWeight: Font.Bold color: Qt.black y: scalespinner.y + + function set(value) { + notify = false; + checked = value + notify = true; + } + + function notifyIsSoftChanged() { + modified = true; + var properties = { + relayParentJoints: checked + }; + + wearableUpdated(getCurrentWearable().id, wearablesCombobox.currentIndex, properties); + } + + property bool notify: false; + + onCheckedChanged: if(notify) notifyIsSoftChanged(); } Column { From 0a28361dc8cb866fbdd2dfd0f5acef4240a85f93 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 9 Aug 2018 01:22:11 +0300 Subject: [PATCH 10/64] treat ModelEntityItem-s with relayParentJoints as wearables --- interface/src/AvatarBookmarks.cpp | 7 ++++--- scripts/system/avatarapp.js | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 3e0e643bd8..0400beb39c 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -35,7 +36,7 @@ #include "QVariantGLM.h" #include - +#include void addAvatarEntities(const QVariantList& avatarEntities) { auto nodeList = DependencyManager::get(); @@ -145,8 +146,8 @@ void AvatarBookmarks::removeBookmark(const QString& bookmarkName) { } bool isWearableEntity(const EntityItemPointer& entity) { - return entity->isVisible() && entity->getParentJointIndex() != INVALID_JOINT_INDEX && - (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); + return entity->isVisible() && (entity->getParentJointIndex() != INVALID_JOINT_INDEX || (entity->getType() == EntityTypes::Model && (std::static_pointer_cast(entity))->getRelayParentJoints())) + && (entity->getParentID() == DependencyManager::get()->getSessionUUID() || entity->getParentID() == DependencyManager::get()->getMyAvatar()->getSelfID()); } void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 03b7b3969d..4bc4d1a762 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -31,7 +31,7 @@ function executeLater(callback) { var INVALID_JOINT_INDEX = -1 function isWearable(avatarEntity) { - return avatarEntity.properties.visible === true && avatarEntity.properties.parentJointIndex !== INVALID_JOINT_INDEX && + return avatarEntity.properties.visible === true && (avatarEntity.properties.parentJointIndex !== INVALID_JOINT_INDEX || avatarEntity.properties.relayParentJoints === true) && (avatarEntity.properties.parentID === MyAvatar.sessionUUID || avatarEntity.properties.parentID === MyAvatar.SELF_ID); } From e900e034a36fd1d52b1db6827dc92be00c41cd6b Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 9 Aug 2018 02:21:27 +0300 Subject: [PATCH 11/64] couple Spinbox fixes: - stop resetting value in 'valueFromText'. The issue with such approach is that is is not possible to understand whether value was really changed or, for example, focus did change - instead, propagate validator to TextInput which makes not possible to type bad text (which eliminate the reason for original fix) - specify correct inputMethodHints to guide onscreen keyboards --- interface/resources/qml/controls-uit/SpinBox.qml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/interface/resources/qml/controls-uit/SpinBox.qml b/interface/resources/qml/controls-uit/SpinBox.qml index f3ae3c5d6e..3d3ea7a75e 100644 --- a/interface/resources/qml/controls-uit/SpinBox.qml +++ b/interface/resources/qml/controls-uit/SpinBox.qml @@ -21,6 +21,7 @@ SpinBox { id: hifi } + inputMethodHints: Qt.ImhFormattedNumbersOnly property int colorScheme: hifi.colorSchemes.light readonly property bool isLightColorScheme: colorScheme === hifi.colorSchemes.light property string label: "" @@ -91,7 +92,6 @@ SpinBox { } valueFromText: function(text, locale) { - spinBox.value = 0; // Force valueChanged signal to be emitted so that validator fires. return Number.fromLocaleString(locale, text)*factor; } @@ -104,6 +104,8 @@ SpinBox { selectedTextColor: hifi.colors.black selectionColor: hifi.colors.primaryHighlight text: spinBox.textFromValue(spinBox.value, spinBox.locale) + suffix + inputMethodHints: spinBox.inputMethodHints + validator: spinBox.validator verticalAlignment: Qt.AlignVCenter leftPadding: spinBoxLabelInside.visible ? 30 : hifi.dimensions.textPadding //rightPadding: hifi.dimensions.spinnerSize From 1771e4c74232f3ca3b93f45c42c95cf398e6f188 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Thu, 9 Aug 2018 02:22:35 +0300 Subject: [PATCH 12/64] better handling of 'modified' inside AdjustWearables dialog --- .../resources/qml/hifi/avatarapp/AdjustWearables.qml | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 158757770c..5f42bb4556 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -30,6 +30,7 @@ Rectangle { function open(avatar) { adjustWearablesOpened(avatar.name); + modified = false; visible = true; avatarName = avatar.name; wearablesModel = avatar.wearables; @@ -401,7 +402,7 @@ Rectangle { colorScheme: hifi.colorSchemes.light property bool notify: false; - onValueChanged: if(notify) notifyScaleChanged(); + onRealValueChanged: if(notify) notifyScaleChanged(); function set(value) { notify = false; @@ -439,13 +440,18 @@ Rectangle { color: hifi.buttons.red; colorScheme: hifi.colorSchemes.light; text: "TAKE IT OFF" - onClicked: wearableDeleted(root.avatarName, getCurrentWearable().id); + onClicked: { + modified = true; + wearableDeleted(root.avatarName, getCurrentWearable().id); + } enabled: wearablesCombobox.model.count !== 0 } } } DialogButtons { + yesButton.enabled: modified + anchors.bottom: parent.bottom anchors.bottomMargin: 57 anchors.left: parent.left From 9234813e0835c04dd47f153205a742193d4318a7 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 14:54:15 +0300 Subject: [PATCH 13/64] bring back 'get more' wearables button --- .../qml/hifi/avatarapp/AdjustWearables.qml | 30 +++++++++++++++++-- .../qml/hifi/avatarapp/MessageBoxes.qml | 29 ++++++++++++++++++ 2 files changed, 57 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 5f42bb4556..e99fdfd243 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -1,5 +1,6 @@ import Hifi 1.0 as Hifi import QtQuick 2.5 +import QtQuick.Layouts 1.3 import "../../styles-uit" import "../../controls-uit" as HifiControlsUit import "../../controls" as HifiControls @@ -124,7 +125,10 @@ Rectangle { Column { width: parent.width - Row { + RowLayout { + anchors.left: parent.left + anchors.right: parent.right + RalewayBold { size: 15; lineHeightMode: Text.FixedHeight @@ -139,9 +143,31 @@ Rectangle { size: 15; lineHeightMode: Text.FixedHeight lineHeight: 18; - text: "Add custom" + text: "Get more" linkColor: hifi.colors.blueHighlight anchors.verticalCenter: parent.verticalCenter + onLinkActivated: { + popup.showGetWearables(function() { + emitSendToScript({'method' : 'navigate', 'url' : 'hifi://AvatarIsland/11.5848,-8.10862,-2.80195'}) + }, function(link) { + emitSendToScript({'method' : 'navigate', 'url' : link}) + }); + } + } + + Rectangle { + Layout.fillWidth: true + Layout.fillHeight: true + + RalewayBold { + size: 15; + lineHeightMode: Text.FixedHeight + lineHeight: 18; + text: "Add custom" + linkColor: hifi.colors.blueHighlight + anchors.verticalCenter: parent.verticalCenter + anchors.right: parent.right + } } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index c9b57abe66..a381da7d0b 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -33,6 +33,35 @@ MessageBox { popup.inputText.forceActiveFocus(); } + property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' + + function showGetWearables(callback, linkCallback) { + popup.button2text = 'AvatarIsland' + popup.dialogButtons.yesButton.fontCapitalization = Font.MixedCase; + popup.button1text = 'CANCEL' + popup.titleText = 'Get Wearables' + popup.bodyText = 'Buy wearables from Marketplace.' + '
' + + 'Wear wearables from My Purchases.' + '
' + '
' + + 'Visit “AvatarIsland” to get wearables' + + popup.imageSource = getWearablesUrl; + popup.onButton2Clicked = function() { + popup.close(); + + if(callback) + callback(); + } + + popup.onLinkClicked = function(link) { + popup.close(); + + if(linkCallback) + linkCallback(link); + } + + popup.open(); + } + function showDeleteFavorite(favoriteName, callback) { popup.titleText = 'Delete Favorite: {AvatarName}'.replace('{AvatarName}', favoriteName) popup.bodyText = 'This will delete your favorite. You will retain access to the wearables and avatar that made up the favorite from My Purchases.' From 90fb0d7be6a40ef3fe36f5c2f62cc56f62c68150 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 17:59:11 +0300 Subject: [PATCH 14/64] add possibility to add wearable from external url --- interface/resources/qml/hifi/AvatarApp.qml | 3 +++ .../qml/hifi/avatarapp/AdjustWearables.qml | 9 +++++++ .../qml/hifi/avatarapp/MessageBoxes.qml | 20 ++++++++++++++ scripts/system/avatarapp.js | 27 +++++++++++++++++++ 4 files changed, 59 insertions(+) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index bd3f8fd1cf..217525498d 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -285,6 +285,9 @@ Rectangle { onWearableSelected: { emitSendToScript({'method' : 'selectWearable', 'entityID' : id}); } + onAddWearable: { + emitSendToScript({'method' : 'addWearable', 'avatarName' : avatarName, 'url' : url}); + } z: 3 } diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index e99fdfd243..27ae590fc5 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -18,6 +18,7 @@ Rectangle { signal adjustWearablesOpened(var avatarName); signal adjustWearablesClosed(bool status, var avatarName); + signal addWearable(string avatarName, string url); property bool modified: false; Component.onCompleted: { @@ -40,6 +41,8 @@ Rectangle { function refresh(avatar) { wearablesCombobox.model.clear(); + wearablesCombobox.currentIndex = -1; + for(var i = 0; i < avatar.wearables.count; ++i) { var wearable = avatar.wearables.get(i).properties; for(var j = (wearable.modelURL.length - 1); j >= 0; --j) { @@ -167,6 +170,12 @@ Rectangle { linkColor: hifi.colors.blueHighlight anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right + onLinkActivated: { + popup.showSpecifyWearableUrl(function(url) { + console.debug('popup.showSpecifyWearableUrl: ', url); + addWearable(root.avatarName, url); + }); + } } } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index a381da7d0b..64c2d478c0 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -33,6 +33,26 @@ MessageBox { popup.inputText.forceActiveFocus(); } + function showSpecifyWearableUrl(callback) { + popup.button2text = 'CONFIRM' + popup.button1text = 'CANCEL' + popup.titleText = 'Specify Wearable URL' + popup.bodyText = 'If you want to add a custom wearable, you can specify the URL of the wearable file here.' + + popup.inputText.visible = true; + popup.inputText.placeholderText = 'Enter Wearable URL'; + + popup.onButton2Clicked = function() { + if(callback) + callback(popup.inputText.text); + + popup.close(); + } + + popup.open(); + popup.inputText.forceActiveFocus(); + } + property url getWearablesUrl: '../../../images/avatarapp/AvatarIsland.jpg' function showGetWearables(callback, linkCallback) { diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 4bc4d1a762..38b6d2447c 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -235,6 +235,33 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See Messages.messageReceived.disconnect(handleWearableMessages); Messages.unsubscribe('Hifi-Object-Manipulation'); break; + case 'addWearable': + + var joints = MyAvatar.getJointNames(); + var hipsIndex = -1; + + for(var i = 0; i < joints.length; ++i) { + if(joints[i] === 'Hips') { + hipsIndex = i; + break; + } + } + + var properties = { + name: "Custom wearable", + type: "Model", + modelURL: message.url, + parentID: MyAvatar.sessionUUID, + relayParentJoints: false, + parentJointIndex: hipsIndex + }; + + var entityID = Entities.addEntity(properties, true); + updateAvatarWearables(currentAvatar, message.avatarName); + executeLater(function() { + onSelectedEntity(entityID); + }); + break; case 'selectWearable': ensureWearableSelected(message.entityID); break; From 9304d6c5c4f85185653c93b275427d1c8bf006a6 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 18:42:08 +0300 Subject: [PATCH 15/64] reset UI to default state & disable controls on removing last wearable --- .../qml/hifi/avatarapp/AdjustWearables.qml | 47 ++++++++++++------- .../resources/qml/hifi/avatarapp/Vector3.qml | 4 ++ 2 files changed, 34 insertions(+), 17 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 27ae590fc5..bf3c62410e 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -53,7 +53,10 @@ Rectangle { } wearablesCombobox.model.append(wearable); } - wearablesCombobox.currentIndex = 0; + + if(wearablesCombobox.model.count !== 0) { + wearablesCombobox.currentIndex = 0; + } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { @@ -75,9 +78,9 @@ Rectangle { if(updateUI) { if(prop === 'localPosition') { - position.set(wearable[prop]); + positionVector.set(wearable[prop]); } else if(prop === 'localRotationAngles') { - rotation.set(wearable[prop]); + rotationVector.set(wearable[prop]); } else if(prop === 'dimensions') { scalespinner.set(wearable[prop].x / wearable.naturalDimensions.x); } @@ -88,7 +91,7 @@ Rectangle { } function getCurrentWearable() { - return wearablesCombobox.model.get(wearablesCombobox.currentIndex) + return wearablesCombobox.currentIndex !== -1 ? wearablesCombobox.model.get(wearablesCombobox.currentIndex) : null; } function selectWearableByID(entityID) { @@ -184,6 +187,7 @@ Rectangle { id: wearablesCombobox anchors.left: parent.left anchors.right: parent.right + enabled: getCurrentWearable() !== null comboBox.textRole: "text" model: ListModel { @@ -202,14 +206,19 @@ Rectangle { comboBox.onCurrentIndexChanged: { var currentWearable = getCurrentWearable(); + var position = currentWearable ? currentWearable.localPosition : { x : 0, y : 0, z : 0 }; + var rotation = currentWearable ? currentWearable.localRotationAngles : { x : 0, y : 0, z : 0 }; + var scale = currentWearable ? currentWearable.dimensions.x / currentWearable.naturalDimensions.x : 1.0; + var joint = currentWearable ? currentWearable.parentJointIndex : -1; + var soft = currentWearable ? currentWearable.relayParentJoints : false; + + positionVector.set(position); + rotationVector.set(rotation); + scalespinner.set(scale); + jointsCombobox.set(joint); + isSoft.set(soft); if(currentWearable) { - position.set(currentWearable.localPosition); - rotation.set(currentWearable.localRotationAngles); - scalespinner.set(currentWearable.dimensions.x / currentWearable.naturalDimensions.x) - jointsCombobox.set(currentWearable.parentJointIndex) - isSoft.set(currentWearable.relayParentJoints) - wearableSelected(currentWearable.id); } } @@ -230,7 +239,7 @@ Rectangle { id: jointsCombobox anchors.left: parent.left anchors.right: parent.right - enabled: !isSoft.checked + enabled: getCurrentWearable() !== null && !isSoft.checked comboBox.displayText: isSoft.checked ? 'Hips' : comboBox.currentText model: jointNames @@ -247,9 +256,9 @@ Rectangle { var properties = { parentJointIndex: currentIndex, localPosition: { - x: position.xvalue, - y: position.yvalue, - z: position.zvalue + x: positionVector.xvalue, + y: positionVector.yvalue, + z: positionVector.zvalue } }; @@ -288,8 +297,9 @@ Rectangle { } Vector3 { - id: position + id: positionVector backgroundColor: "lightgray" + enabled: getCurrentWearable() !== null function set(localPosition) { notify = false; @@ -347,8 +357,9 @@ Rectangle { } Vector3 { - id: rotation + id: rotationVector backgroundColor: "lightgray" + enabled: getCurrentWearable() !== null function set(localRotationAngles) { notify = false; @@ -386,6 +397,7 @@ Rectangle { HifiControlsUit.CheckBox { id: isSoft + enabled: getCurrentWearable() !== null text: "Is soft" labelFontSize: 15 labelFontWeight: Font.Bold @@ -427,13 +439,14 @@ Rectangle { HifiControlsUit.SpinBox { id: scalespinner + enabled: getCurrentWearable() !== null decimals: 2 realStepSize: 0.1 realFrom: 0.1 realTo: 3.0 realValue: 1.0 backgroundColor: "lightgray" - width: position.spinboxWidth + width: positionVector.spinboxWidth colorScheme: hifi.colorSchemes.light property bool notify: false; diff --git a/interface/resources/qml/hifi/avatarapp/Vector3.qml b/interface/resources/qml/hifi/avatarapp/Vector3.qml index 33e82fe0ee..d77665f992 100644 --- a/interface/resources/qml/hifi/avatarapp/Vector3.qml +++ b/interface/resources/qml/hifi/avatarapp/Vector3.qml @@ -20,6 +20,7 @@ Row { spacing: spinboxSpace + property bool enabled: false; property alias xvalue: xspinner.realValue property alias yvalue: yspinner.realValue property alias zvalue: zspinner.realValue @@ -35,6 +36,7 @@ Row { realFrom: root.realFrom realTo: root.realTo realStepSize: root.realStepSize + enabled: root.enabled } HifiControlsUit.SpinBox { @@ -48,6 +50,7 @@ Row { realFrom: root.realFrom realTo: root.realTo realStepSize: root.realStepSize + enabled: root.enabled } HifiControlsUit.SpinBox { @@ -61,5 +64,6 @@ Row { realFrom: root.realFrom realTo: root.realTo realStepSize: root.realStepSize + enabled: root.enabled } } From ce6c8c72f528e24cf73254f5708d74dab5f6ade9 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 19:09:47 +0300 Subject: [PATCH 16/64] ensure selection of newly-added wearable always happens after pushing updated wearables list to UI --- scripts/system/avatarapp.js | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 38b6d2447c..85946c644a 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -72,12 +72,15 @@ function getMyAvatarSettings() { } } -function updateAvatarWearables(avatar, bookmarkAvatarName) { +function updateAvatarWearables(avatar, bookmarkAvatarName, callback) { executeLater(function() { var wearables = getMyAvatarWearables(); avatar[ENTRY_AVATAR_ENTITIES] = wearables; sendToQml({'method' : 'wearablesUpdated', 'wearables' : wearables, 'avatarName' : bookmarkAvatarName}) + + if(callback) + callback(); }); } @@ -257,8 +260,7 @@ function fromQml(message) { // messages are {method, params}, like json-rpc. See }; var entityID = Entities.addEntity(properties, true); - updateAvatarWearables(currentAvatar, message.avatarName); - executeLater(function() { + updateAvatarWearables(currentAvatar, message.avatarName, function() { onSelectedEntity(entityID); }); break; From 3bfe173371c73446377e065ff692c5e2b1901b0b Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 19:13:17 +0300 Subject: [PATCH 17/64] allow saving wearables right after adding new one --- interface/resources/qml/hifi/avatarapp/AdjustWearables.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index bf3c62410e..5e88836070 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -177,6 +177,7 @@ Rectangle { popup.showSpecifyWearableUrl(function(url) { console.debug('popup.showSpecifyWearableUrl: ', url); addWearable(root.avatarName, url); + modified = true; }); } } From 9b9e30e829325e47b4408fb537415f9ee93f2282 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Fri, 10 Aug 2018 20:14:15 +0300 Subject: [PATCH 18/64] add 'Tip: You can use hand controllers to grab and adjust your wearables' if in HMD --- .../qml/hifi/avatarapp/AdjustWearables.qml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 5e88836070..2b50fbdf34 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -131,6 +131,32 @@ Rectangle { Column { width: parent.width + Rectangle { + color: hifi.colors.orangeHighlight + anchors.left: parent.left + anchors.right: parent.right + height: 70 + visible: HMD.active + + RalewayRegular { + anchors.fill: parent + anchors.leftMargin: 18 + anchors.rightMargin: 18 + size: 20; + lineHeightMode: Text.FixedHeight + lineHeight: 23; + text: "Tip: You can use hand controllers to grab and adjust your wearables" + wrapMode: Text.WordWrap + } + } + + Rectangle { + anchors.left: parent.left + anchors.right: parent.right + height: 12 // spacing + visible: HMD.active + } + RowLayout { anchors.left: parent.left anchors.right: parent.right From e6915e63f2fb777d31951b3c7f91c3b1c83f3c72 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 11 Aug 2018 00:39:43 +0300 Subject: [PATCH 19/64] put 'Fedora' on HEAD --- interface/resources/avatar/bookmarks/avatarbookmarks.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/interface/resources/avatar/bookmarks/avatarbookmarks.json b/interface/resources/avatar/bookmarks/avatarbookmarks.json index 2ef59d53a3..065e97c842 100644 --- a/interface/resources/avatar/bookmarks/avatarbookmarks.json +++ b/interface/resources/avatar/bookmarks/avatarbookmarks.json @@ -507,7 +507,7 @@ "originalTextures": "{\n \"file5\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Base_Color.png\",\n \"file7\": \"http://mpassets.highfidelity.com/11c4208d-15d7-4449-9758-a08da6dbd3dc-v1/Fedora.fbx/Texture/Fedora_Hat1_Roughness.png\"\n}\n", "owningAvatarID": "{4c770def-4abb-40c6-91a1-88da5247b2db}", "parentID": "{4c770def-4abb-40c6-91a1-88da5247b2db}", - "parentJointIndex": 64, + "parentJointIndex": 63, "position": { "x": -0.030000008642673492, "y": 0.12999820709228516, From c1ebadfdd808359aa614c36926663a206c2faeba Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 11 Aug 2018 00:43:39 +0300 Subject: [PATCH 20/64] adjust text in 'get wearables' popup --- interface/resources/qml/hifi/avatarapp/MessageBoxes.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index 64c2d478c0..62203960e8 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -61,8 +61,8 @@ MessageBox { popup.button1text = 'CANCEL' popup.titleText = 'Get Wearables' popup.bodyText = 'Buy wearables from Marketplace.' + '
' + - 'Wear wearables from My Purchases.' + '
' + '
' + - 'Visit “AvatarIsland” to get wearables' + 'Wear wearable from My Purchases.' + '
' + '
' + + 'Visit “AvatarIsland” to get wearables' popup.imageSource = getWearablesUrl; popup.onButton2Clicked = function() { From 5ba68cbfac3d51c2c4dade2af391ddbee6b8b242 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 11 Aug 2018 00:50:32 +0300 Subject: [PATCH 21/64] remove attachments menu item / dialogs / related components --- .../qml/hifi/dialogs/AttachmentsDialog.qml | 48 --- .../hifi/dialogs/AvatarAttachmentsDialog.qml | 130 -------- .../hifi/dialogs/attachments/Attachment.qml | 230 -------------- .../qml/hifi/dialogs/attachments/Rotation.qml | 9 - .../hifi/dialogs/attachments/Translation.qml | 9 - .../qml/hifi/dialogs/attachments/Vector3.qml | 112 ------- .../dialogs/content/AttachmentsContent.qml | 282 ------------------ .../hifi/tablet/TabletAttachmentsDialog.qml | 103 ------- interface/src/Menu.cpp | 7 - interface/src/Menu.h | 1 - 10 files changed, 931 deletions(-) delete mode 100644 interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml delete mode 100644 interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Attachment.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Rotation.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Translation.qml delete mode 100644 interface/resources/qml/hifi/dialogs/attachments/Vector3.qml delete mode 100644 interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml delete mode 100644 interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml diff --git a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml deleted file mode 100644 index 006a4b7158..0000000000 --- a/interface/resources/qml/hifi/dialogs/AttachmentsDialog.qml +++ /dev/null @@ -1,48 +0,0 @@ -// -// AttachmentsDialog.qml -// -// Created by David Rowe on 9 Mar 2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 -import Qt.labs.settings 1.0 - -import "../../styles-uit" -import "../../windows" -import "content" - -ScrollingWindow { - id: root - title: "Attachments" - objectName: "AttachmentsDialog" - width: 600 - height: 600 - resizable: true - destroyOnHidden: true - minSize: Qt.vector2d(400, 500) - - HifiConstants { id: hifi } - - // This is for JS/QML communication, which is unused in the AttachmentsDialog, - // but not having this here results in spurious warnings about a - // missing signal - signal sendToScript(var message); - - property var settings: Settings { - category: "AttachmentsDialog" - property alias x: root.x - property alias y: root.y - property alias width: root.width - property alias height: root.height - } - - function closeDialog() { - root.destroy(); - } - - AttachmentsContent { } -} diff --git a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml b/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml deleted file mode 100644 index b4357a0077..0000000000 --- a/interface/resources/qml/hifi/dialogs/AvatarAttachmentsDialog.qml +++ /dev/null @@ -1,130 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 1.5 -import QtQuick.XmlListModel 2.0 -import QtQuick.Controls.Styles 1.4 - -import "../../windows" -import "../../js/Utils.js" as Utils -import "../models" - -ModalWindow { - id: root - resizable: true - width: 640 - height: 480 - - property var result; - - signal selected(var modelUrl); - signal canceled(); - - Rectangle { - anchors.fill: parent - color: "white" - - Item { - anchors { fill: parent; margins: 8 } - - TextField { - id: filterEdit - anchors { left: parent.left; right: parent.right; top: parent.top } - style: TextFieldStyle { renderType: Text.QtRendering } - placeholderText: "filter" - onTextChanged: tableView.model.filter = text - } - - TableView { - id: tableView - anchors { left: parent.left; right: parent.right; top: filterEdit.bottom; topMargin: 8; bottom: buttonRow.top; bottomMargin: 8 } - model: S3Model{} - onCurrentRowChanged: { - if (currentRow == -1) { - root.result = null; - return; - } - result = model.baseUrl + "/" + model.get(tableView.currentRow).key; - } - itemDelegate: Component { - Item { - clip: true - Text { - x: 3 - anchors.verticalCenter: parent.verticalCenter - color: tableView.activeFocus && styleData.row === tableView.currentRow ? "yellow" : styleData.textColor - elide: styleData.elideMode - text: getText() - - function getText() { - switch(styleData.column) { - case 1: - return Utils.formatSize(styleData.value) - default: - return styleData.value; - } - } - - } - } - } - TableViewColumn { - role: "name" - title: "Name" - width: 200 - } - TableViewColumn { - role: "size" - title: "Size" - width: 100 - } - TableViewColumn { - role: "modified" - title: "Last Modified" - width: 200 - } - Rectangle { - anchors.fill: parent - visible: tableView.model.status !== XmlListModel.Ready - color: "#7fffffff" - BusyIndicator { - anchors.centerIn: parent - width: 48; height: 48 - running: true - } - } - } - - Row { - id: buttonRow - anchors { right: parent.right; bottom: parent.bottom } - Button { action: acceptAction } - Button { action: cancelAction } - } - - Action { - id: acceptAction - text: qsTr("OK") - enabled: root.result ? true : false - shortcut: "Return" - onTriggered: { - root.selected(root.result); - root.destroy(); - } - } - - Action { - id: cancelAction - text: qsTr("Cancel") - shortcut: "Esc" - onTriggered: { - root.canceled(); - root.destroy(); - } - } - } - - } -} - - - - diff --git a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml b/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml deleted file mode 100644 index 54270c2d06..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Attachment.qml +++ /dev/null @@ -1,230 +0,0 @@ -import QtQuick 2.5 - -import "." -import ".." -import "../../tablet" -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls -import "../../../windows" - -Item { - height: column.height + 2 * 8 - - property var attachment; - - HifiConstants { id: hifi } - - signal selectAttachment(); - signal deleteAttachment(var attachment); - signal updateAttachment(); - property bool completed: false; - - function doSelectAttachment(control, focus) { - if (focus) { - selectAttachment(); - - // Refocus control after possibly changing focus to attachment. - if (control.setControlFocus !== undefined) { - control.setControlFocus(); - } else { - control.focus = true; - } - } - } - - Rectangle { color: hifi.colors.baseGray; anchors.fill: parent; radius: 4 } - - Component.onCompleted: { - jointChooser.model = MyAvatar.jointNames; - completed = true; - } - - Column { - y: 8 - id: column - anchors { left: parent.left; right: parent.right; margins: 20 } - spacing: 8 - - Item { - height: modelChooserButton.height + urlLabel.height + 4 - anchors { left: parent.left; right: parent.right;} - HifiControls.Label { id: urlLabel; color: hifi.colors.lightGrayText; text: "Model URL"; anchors.top: parent.top;} - HifiControls.TextField { - id: modelUrl; - height: jointChooser.height; - colorScheme: hifi.colorSchemes.dark - anchors { bottom: parent.bottom; left: parent.left; rightMargin: 8; right: modelChooserButton.left } - text: attachment ? attachment.modelUrl : "" - onTextChanged: { - if (completed && attachment && attachment.modelUrl !== text) { - attachment.modelUrl = text; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - HifiControls.Button { - id: modelChooserButton; - text: "Choose"; - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors { right: parent.right; verticalCenter: modelUrl.verticalCenter } - Component { - id: modelBrowserBuilder; - ModelBrowserDialog {} - } - Component { - id: tabletModelBrowserBuilder; - TabletModelBrowserDialog {} - } - - onClicked: { - var browser; - if (typeof desktop !== "undefined") { - browser = modelBrowserBuilder.createObject(desktop); - browser.selected.connect(function(newModelUrl){ - modelUrl.text = newModelUrl; - }); - } else { - browser = tabletModelBrowserBuilder.createObject(tabletRoot); - browser.selected.connect(function(newModelUrl){ - modelUrl.text = newModelUrl; - tabletRoot.openModal = null; - }); - browser.canceled.connect(function() { - tabletRoot.openModal = null; - }); - - // Make dialog modal. - tabletRoot.openModal = browser; - } - } - } - } - - Item { - z: 1000 - height: jointChooser.height + jointLabel.height + 4 - anchors { left: parent.left; right: parent.right; } - HifiControls.Label { - id: jointLabel; - text: "Joint"; - color: hifi.colors.lightGrayText; - anchors.top: parent.top - } - HifiControls.ComboBox { - id: jointChooser; - dropdownHeight: (typeof desktop !== "undefined") ? 480 : 206 - anchors { bottom: parent.bottom; left: parent.left; right: parent.right } - colorScheme: hifi.colorSchemes.dark - currentIndex: attachment ? model.indexOf(attachment.jointName) : -1 - onCurrentIndexChanged: { - if (completed && attachment && currentIndex != -1 && attachment.jointName !== model[currentIndex]) { - attachment.jointName = model[currentIndex]; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - } - - Item { - height: translation.height + translationLabel.height + 4 - anchors { left: parent.left; right: parent.right; } - HifiControls.Label { id: translationLabel; color: hifi.colors.lightGrayText; text: "Translation"; anchors.top: parent.top; } - Translation { - id: translation; - anchors { left: parent.left; right: parent.right; bottom: parent.bottom} - vector: attachment ? attachment.translation : {x: 0, y: 0, z: 0}; - onValueChanged: { - if (completed && attachment) { - attachment.translation = vector; - updateAttachment(); - } - } - onControlFocusChanged: doSelectAttachment(this, controlFocus); - } - } - - Item { - height: rotation.height + rotationLabel.height + 4 - anchors { left: parent.left; right: parent.right; } - HifiControls.Label { id: rotationLabel; color: hifi.colors.lightGrayText; text: "Rotation"; anchors.top: parent.top; } - Rotation { - id: rotation; - anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } - vector: attachment ? attachment.rotation : {x: 0, y: 0, z: 0}; - onValueChanged: { - if (completed && attachment) { - attachment.rotation = vector; - updateAttachment(); - } - } - onControlFocusChanged: doSelectAttachment(this, controlFocus); - } - } - - Item { - height: scaleItem.height - anchors { left: parent.left; right: parent.right; } - - Item { - id: scaleItem - height: scaleSpinner.height + scaleLabel.height + 4 - width: parent.width / 3 - 8 - anchors { right: parent.right; } - HifiControls.Label { id: scaleLabel; color: hifi.colors.lightGrayText; text: "Scale"; anchors.top: parent.top; } - HifiControls.SpinBox { - id: scaleSpinner; - anchors { left: parent.left; right: parent.right; bottom: parent.bottom; } - decimals: 2; - minimumValue: 0.01 - maximumValue: 10 - realStepSize: 0.05; - realValue: attachment ? attachment.scale : 1.0 - colorScheme: hifi.colorSchemes.dark - onRealValueChanged: { - if (completed && attachment && attachment.scale !== realValue) { - attachment.scale = realValue; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - } - - Item { - id: isSoftItem - height: scaleSpinner.height - anchors { - left: parent.left - bottom: parent.bottom - } - HifiControls.CheckBox { - id: soft - text: "Is soft" - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - checked: attachment ? attachment.soft : false - colorScheme: hifi.colorSchemes.dark - onCheckedChanged: { - if (completed && attachment && attachment.soft !== checked) { - attachment.soft = checked; - updateAttachment(); - } - } - onFocusChanged: doSelectAttachment(this, focus); - } - } - } - HifiControls.Button { - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - anchors { left: parent.left; right: parent.right; } - text: "Delete" - onClicked: deleteAttachment(root.attachment); - } - } -} diff --git a/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml b/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml deleted file mode 100644 index 6061efc4c8..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Rotation.qml +++ /dev/null @@ -1,9 +0,0 @@ -import "." - -Vector3 { - decimals: 1; - stepSize: 1; - maximumValue: 180 - minimumValue: -180 -} - diff --git a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml b/interface/resources/qml/hifi/dialogs/attachments/Translation.qml deleted file mode 100644 index 39ac6da55a..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Translation.qml +++ /dev/null @@ -1,9 +0,0 @@ -import "." - -Vector3 { - decimals: 3; - stepSize: 0.01; - maximumValue: 10 - minimumValue: -10 -} - diff --git a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml b/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml deleted file mode 100644 index 311492858b..0000000000 --- a/interface/resources/qml/hifi/dialogs/attachments/Vector3.qml +++ /dev/null @@ -1,112 +0,0 @@ -import QtQuick 2.5 - -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls -import "../../../windows" - -Item { - id: root - implicitHeight: xspinner.height - readonly property real spacing: 8 - property real spinboxWidth: (width / 3) - spacing - property var vector; - property real decimals: 0 - property real stepSize: 1 - property real maximumValue: 99 - property real minimumValue: 0 - property bool controlFocus: false; // True if one of the ordinate controls has focus. - property var controlFocusControl: undefined - - signal valueChanged(); - - function setControlFocus() { - if (controlFocusControl) { - controlFocusControl.focus = true; - // The controlFocus value is updated via onFocusChanged. - } - } - - function setFocus(control, focus) { - if (focus) { - controlFocusControl = control; - setControlFocusTrue.start(); // After any subsequent false from previous control. - } else { - controlFocus = false; - } - } - - Timer { - id: setControlFocusTrue - interval: 50 - repeat: false - running: false - onTriggered: { - controlFocus = true; - } - } - - HifiConstants { id: hifi } - - HifiControls.SpinBox { - id: xspinner - width: root.spinboxWidth - anchors { left: parent.left } - realValue: root.vector.x - labelInside: "X:" - colorScheme: hifi.colorSchemes.dark - colorLabelInside: hifi.colors.redHighlight - decimals: root.decimals - realStepSize: root.stepSize - maximumValue: root.maximumValue - minimumValue: root.minimumValue - onRealValueChanged: { - if (realValue !== vector.x) { - vector.x = realValue - root.valueChanged(); - } - } - onFocusChanged: setFocus(this, focus); - } - - HifiControls.SpinBox { - id: yspinner - width: root.spinboxWidth - anchors { horizontalCenter: parent.horizontalCenter } - realValue: root.vector.y - labelInside: "Y:" - colorLabelInside: hifi.colors.greenHighlight - colorScheme: hifi.colorSchemes.dark - decimals: root.decimals - realStepSize: root.stepSize - maximumValue: root.maximumValue - minimumValue: root.minimumValue - onRealValueChanged: { - if (realValue !== vector.y) { - vector.y = realValue - root.valueChanged(); - } - } - onFocusChanged: setFocus(this, focus); - } - - HifiControls.SpinBox { - id: zspinner - width: root.spinboxWidth - anchors { right: parent.right; } - realValue: root.vector.z - labelInside: "Z:" - colorLabelInside: hifi.colors.primaryHighlight - colorScheme: hifi.colorSchemes.dark - decimals: root.decimals - realStepSize: root.stepSize - maximumValue: root.maximumValue - minimumValue: root.minimumValue - onRealValueChanged: { - if (realValue !== vector.z) { - vector.z = realValue - root.valueChanged(); - } - } - onFocusChanged: setFocus(this, focus); - } -} diff --git a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml b/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml deleted file mode 100644 index fa71116574..0000000000 --- a/interface/resources/qml/hifi/dialogs/content/AttachmentsContent.qml +++ /dev/null @@ -1,282 +0,0 @@ -import QtQuick 2.7 -import QtQuick.Controls 2.3 -import QtQuick.Dialogs 1.2 as OriginalDialogs - -import "../../../styles-uit" -import "../../../controls-uit" as HifiControls -import "../../../windows" -import "../attachments" - -Item { - id: content - - readonly property var originalAttachments: MyAvatar.getAttachmentsVariant(); - property var attachments: []; - - function reload(){ - content.attachments = []; - var currentAttachments = MyAvatar.getAttachmentsVariant(); - listView.model.clear(); - for (var i = 0; i < currentAttachments.length; ++i) { - var attachment = currentAttachments[i]; - content.attachments.push(attachment); - listView.model.append({}); - } - } - - Connections { - id: onAttachmentsChangedConnection - target: MyAvatar - onAttachmentsChanged: reload() - } - - Component.onCompleted: { - reload() - } - - function setAttachmentsVariant(attachments) { - onAttachmentsChangedConnection.enabled = false; - MyAvatar.setAttachmentsVariant(attachments); - onAttachmentsChangedConnection.enabled = true; - } - - Column { - width: pane.width - - Rectangle { - width: parent.width - height: root.height - (keyboardEnabled && keyboardRaised ? 200 : 0) - color: hifi.colors.baseGray - - Rectangle { - id: attachmentsBackground - anchors { - left: parent.left; right: parent.right; top: parent.top; bottom: newAttachmentButton.top; - margins: hifi.dimensions.contentMargin.x - bottomMargin: hifi.dimensions.contentSpacing.y - } - color: hifi.colors.baseGrayShadow - radius: 4 - - ListView { - id: listView - anchors { - top: parent.top - left: parent.left - right: scrollBar.left - bottom: parent.bottom - margins: 4 - } - clip: true - cacheBuffer: 4000 - - model: ListModel {} - delegate: Item { - id: attachmentDelegate - implicitHeight: attachmentView.height + 8; - implicitWidth: attachmentView.width - - MouseArea { - // User can click on whitespace to select item. - anchors.fill: parent - propagateComposedEvents: true - onClicked: { - listView.currentIndex = index; - attachmentsBackground.forceActiveFocus(); // Unfocus from any control. - mouse.accepted = false; - } - } - - Attachment { - id: attachmentView - width: listView.width - attachment: content.attachments[index] - onSelectAttachment: { - listView.currentIndex = index; - } - onDeleteAttachment: { - attachments.splice(index, 1); - listView.model.remove(index, 1); - } - onUpdateAttachment: { - setAttachmentsVariant(attachments); - } - } - } - - onCountChanged: { - setAttachmentsVariant(attachments); - } - - /* - // DEBUG - highlight: Rectangle { color: "#40ffff00" } - highlightFollowsCurrentItem: true - */ - - onHeightChanged: { - // Keyboard has been raised / lowered. - positionViewAtIndex(listView.currentIndex, ListView.SnapPosition); - } - - onCurrentIndexChanged: { - if (!yScrollTimer.running) { - scrollSlider.y = currentIndex * (scrollBar.height - scrollSlider.height) / (listView.count - 1); - } - } - - onContentYChanged: { - // User may have dragged content up/down. - yScrollTimer.restart(); - } - - Timer { - id: yScrollTimer - interval: 200 - repeat: false - running: false - onTriggered: { - var index = (listView.count - 1) * listView.contentY / (listView.contentHeight - scrollBar.height); - index = Math.round(index); - listView.currentIndex = index; - scrollSlider.y = index * (scrollBar.height - scrollSlider.height) / (listView.count - 1); - } - } - } - - Rectangle { - id: scrollBar - - property bool scrolling: listView.contentHeight > listView.height - - anchors { - top: parent.top - right: parent.right - bottom: parent.bottom - topMargin: 4 - bottomMargin: 4 - } - width: scrolling ? 18 : 0 - radius: attachmentsBackground.radius - color: hifi.colors.baseGrayShadow - - MouseArea { - anchors.fill: parent - - onClicked: { - var index = listView.currentIndex; - index = index + (mouse.y <= scrollSlider.y ? -1 : 1); - if (index < 0) { - index = 0; - } - if (index > listView.count - 1) { - index = listView.count - 1; - } - listView.currentIndex = index; - } - } - - Rectangle { - id: scrollSlider - anchors { - right: parent.right - rightMargin: 3 - } - width: 16 - height: (listView.height / listView.contentHeight) * listView.height - radius: width / 2 - color: hifi.colors.lightGray - - visible: scrollBar.scrolling; - - onYChanged: { - var index = y * (listView.count - 1) / (scrollBar.height - scrollSlider.height); - index = Math.round(index); - listView.currentIndex = index; - } - - MouseArea { - anchors.fill: parent - drag.target: scrollSlider - drag.axis: Drag.YAxis - drag.minimumY: 0 - drag.maximumY: scrollBar.height - scrollSlider.height - } - } - } - } - - HifiControls.Button { - id: newAttachmentButton - anchors { - left: parent.left - right: parent.right - bottom: buttonRow.top - margins: hifi.dimensions.contentMargin.x; - topMargin: hifi.dimensions.contentSpacing.y - bottomMargin: hifi.dimensions.contentSpacing.y - } - text: "New Attachment" - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - onClicked: { - var template = { - modelUrl: "", - translation: { x: 0, y: 0, z: 0 }, - rotation: { x: 0, y: 0, z: 0 }, - scale: 1, - jointName: MyAvatar.jointNames[0], - soft: false - }; - attachments.push(template); - listView.model.append({}); - setAttachmentsVariant(attachments); - } - } - - Row { - id: buttonRow - spacing: 8 - anchors { - right: parent.right - bottom: parent.bottom - margins: hifi.dimensions.contentMargin.x - topMargin: hifi.dimensions.contentSpacing.y - bottomMargin: hifi.dimensions.contentSpacing.y - } - HifiControls.Button { - action: okAction - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - } - HifiControls.Button { - action: cancelAction - color: hifi.buttons.black - colorScheme: hifi.colorSchemes.dark - } - } - - Action { - id: cancelAction - text: "Cancel" - onTriggered: { - setAttachmentsVariant(originalAttachments); - closeDialog(); - } - } - - Action { - id: okAction - text: "OK" - onTriggered: { - for (var i = 0; i < attachments.length; ++i) { - console.log("Attachment " + i + ": " + attachments[i]); - } - - setAttachmentsVariant(attachments); - closeDialog(); - } - } - } - } -} diff --git a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml b/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml deleted file mode 100644 index 7e1cdea0db..0000000000 --- a/interface/resources/qml/hifi/tablet/TabletAttachmentsDialog.qml +++ /dev/null @@ -1,103 +0,0 @@ -// -// TabletAttachmentsDialog.qml -// -// Created by David Rowe on 9 Mar 2017. -// Copyright 2017 High Fidelity, Inc. -// -// Distributed under the Apache License, Version 2.0. -// See the accompanying file LICENSE or https://www.apache.org/licenses/LICENSE-2.0.html -// - -import QtQuick 2.5 - -import "../../controls-uit" as HifiControls -import "../../styles-uit" -import "../dialogs/content" - -Item { - id: root - objectName: "AttachmentsDialog" - - property string title: "Avatar Attachments" - - property bool keyboardEnabled: false - property bool keyboardRaised: false - property bool punctuationMode: false - - signal sendToScript(var message); - - anchors.fill: parent - - HifiConstants { id: hifi } - - Rectangle { - id: pane // Surrogate for ScrollingWindow's pane. - anchors.fill: parent - } - - function closeDialog() { - Tablet.getTablet("com.highfidelity.interface.tablet.system").gotoHomeScreen(); - } - - anchors.topMargin: hifi.dimensions.tabletMenuHeader // Space for header. - - HifiControls.TabletHeader { - id: header - title: root.title - - anchors { - left: parent.left - right: parent.right - bottom: parent.top - } - } - - AttachmentsContent { - id: attachments - - anchors { - top: header.bottom - left: parent.left - right: parent.right - bottom: keyboard.top - } - - MouseArea { - // Defocuses any current control so that the keyboard gets hidden. - id: defocuser - anchors.fill: parent - propagateComposedEvents: true - acceptedButtons: Qt.AllButtons - onPressed: { - parent.forceActiveFocus(); - mouse.accepted = false; - } - } - } - - HifiControls.Keyboard { - id: keyboard - raised: parent.keyboardEnabled && parent.keyboardRaised - numeric: parent.punctuationMode - anchors { - left: parent.left - right: parent.right - bottom: parent.bottom - } - } - - MouseArea { - id: activator - anchors.fill: parent - propagateComposedEvents: true - enabled: true - acceptedButtons: Qt.AllButtons - onPressed: { - mouse.accepted = false; - } - } - - Component.onCompleted: { - keyboardEnabled = HMD.active; - } -} diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 130c2c0b89..8524c40262 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -275,13 +275,6 @@ Menu::Menu() { QString("hifi/tablet/TabletGraphicsPreferences.qml"), "GraphicsPreferencesDialog"); }); - // Settings > Attachments... - action = addActionToQMenuAndActionHash(settingsMenu, MenuOption::Attachments); - connect(action, &QAction::triggered, [] { - qApp->showDialog(QString("hifi/dialogs/AttachmentsDialog.qml"), - QString("hifi/tablet/TabletAttachmentsDialog.qml"), "AttachmentsDialog"); - }); - // Settings > Developer Menu addCheckableActionToQMenuAndActionHash(settingsMenu, "Developer Menu", 0, false, this, SLOT(toggleDeveloperMenus())); diff --git a/interface/src/Menu.h b/interface/src/Menu.h index 1ab7faa82b..be8957cefd 100644 --- a/interface/src/Menu.h +++ b/interface/src/Menu.h @@ -36,7 +36,6 @@ namespace MenuOption { const QString AskToResetSettings = "Ask To Reset Settings on Start"; const QString AssetMigration = "ATP Asset Migration"; const QString AssetServer = "Asset Browser"; - const QString Attachments = "Attachments..."; const QString AudioScope = "Show Scope"; const QString AudioScopeFiftyFrames = "Fifty"; const QString AudioScopeFiveFrames = "Five"; From 877bce23d2936a2dbbd73ab9423e583ff6d47eb8 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 18 Aug 2018 16:32:31 +0300 Subject: [PATCH 22/64] explicitely specify localRotations on joint change, otherwise it will be updated by engine --- interface/resources/qml/hifi/avatarapp/AdjustWearables.qml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index 2b50fbdf34..bec037fdf2 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -286,6 +286,11 @@ Rectangle { x: positionVector.xvalue, y: positionVector.yvalue, z: positionVector.zvalue + }, + localRotationAngles: { + x: rotationVector.xvalue, + y: rotationVector.yvalue, + z: rotationVector.zvalue, } }; From 3bcd6f4a25fb46df70a4e49697a348f6bd942972 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 18 Aug 2018 16:33:30 +0300 Subject: [PATCH 23/64] take 'relayParentJoints' into account during wearables comparison --- interface/resources/qml/hifi/avatarapp/AvatarsModel.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml index 931a041747..9b78dee5bc 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml @@ -164,6 +164,7 @@ ListModel { {'propertyName' : 'marketplaceID'}, {'propertyName' : 'itemName'}, {'propertyName' : 'script'}, + {'propertyName' : 'relayParentJoints'}, {'propertyName' : 'localPosition', 'comparer' : compareNumericObjects}, {'propertyName' : 'localRotationAngles', 'comparer' : compareNumericObjects}, {'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties') From 9f92ff5de9b49582466cc1c3a31958590baa891e Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Sat, 18 Aug 2018 16:38:35 +0300 Subject: [PATCH 24/64] fix for duplicating wearables on saving note: the issue was caused by newly-introduced code converting between attachments & entities fix removes attachments-related code because attachments are deprecated and converted into entities on the fly --- .../resources/qml/hifi/avatarapp/AvatarsModel.qml | 12 ------------ interface/src/AvatarBookmarks.cpp | 1 - scripts/system/avatarapp.js | 2 -- 3 files changed, 15 deletions(-) diff --git a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml index 9b78dee5bc..f9a231ec48 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml @@ -42,7 +42,6 @@ ListModel { 'thumbnailUrl' : avatarThumbnailUrl, 'avatarUrl' : avatar.avatarUrl, 'wearables' : avatar.avatarEntites ? avatar.avatarEntites : [], - 'attachments' : avatar.attachments ? avatar.attachments : [], 'entry' : avatar, 'getMoreAvatars' : false }; @@ -170,13 +169,6 @@ ListModel { {'propertyName' : 'dimensions', 'comparer' : compareNumericObjects}], 'properties') } - function compareAttachments(a1, a2) { - return compareObjects(a1, a2, [{'propertyName' : 'position', 'comparer' : compareNumericObjects}, - {'propertyName' : 'orientation'}, - {'propertyName' : 'parentJointIndex'}, - {'propertyName' : 'modelurl'}]) - } - function findAvatarIndexByValue(avatar) { var index = -1; @@ -192,10 +184,6 @@ ListModel { if(bookmarkedAvatar.avatarScale !== avatar.avatarScale) continue; - if(!modelsAreEqual(bookmarkedAvatar.attachments, avatar.attachments, compareAttachments)) { - continue; - } - if(!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) { continue; } diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index d8fc30963b..6ebd7b0d9e 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -255,7 +255,6 @@ QVariantMap AvatarBookmarks::getAvatarDataToBookmark() { bookmark.insert(ENTRY_VERSION, AVATAR_BOOKMARK_VERSION); bookmark.insert(ENTRY_AVATAR_URL, avatarUrl); bookmark.insert(ENTRY_AVATAR_SCALE, avatarScale); - bookmark.insert(ENTRY_AVATAR_ATTACHMENTS, myAvatar->getAttachmentsVariant()); QScriptEngine scriptEngine; QVariantList wearableEntities; diff --git a/scripts/system/avatarapp.js b/scripts/system/avatarapp.js index 85946c644a..10ccb66d96 100644 --- a/scripts/system/avatarapp.js +++ b/scripts/system/avatarapp.js @@ -20,7 +20,6 @@ Script.include("/~/system/libraries/controllers.js"); // constants from AvatarBookmarks.h var ENTRY_AVATAR_URL = "avatarUrl"; -var ENTRY_AVATAR_ATTACHMENTS = "attachments"; var ENTRY_AVATAR_ENTITIES = "avatarEntites"; var ENTRY_AVATAR_SCALE = "avatarScale"; var ENTRY_VERSION = "version"; @@ -57,7 +56,6 @@ function getMyAvatar() { var avatar = {} avatar[ENTRY_AVATAR_URL] = MyAvatar.skeletonModelURL; avatar[ENTRY_AVATAR_SCALE] = MyAvatar.getAvatarScale(); - avatar[ENTRY_AVATAR_ATTACHMENTS] = MyAvatar.getAttachmentsVariant(); avatar[ENTRY_AVATAR_ENTITIES] = getMyAvatarWearables(); return avatar; } From 67eb03ba7d049e0e08fdca580770e8f75ceec1e2 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Tue, 21 Aug 2018 11:18:00 +1200 Subject: [PATCH 25/64] Reduce strength of teleport parabola --- scripts/system/controllers/controllerModules/teleport.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/system/controllers/controllerModules/teleport.js b/scripts/system/controllers/controllerModules/teleport.js index 92e3168147..2253432f48 100644 --- a/scripts/system/controllers/controllerModules/teleport.js +++ b/scripts/system/controllers/controllerModules/teleport.js @@ -110,7 +110,7 @@ Script.include("/~/system/libraries/controllers.js"); SEAT: 'seat' // The current target is a seat }; - var speed = 12.0; + var speed = 9.3; var accelerationAxis = {x: 0.0, y: -5.0, z: 0.0}; function Teleporter(hand) { From 1914f1f1031ebe47670bf59a00f3127680455aa6 Mon Sep 17 00:00:00 2001 From: Sam Gateau Date: Tue, 21 Aug 2018 10:10:39 -0700 Subject: [PATCH 26/64] Extending the default size of the regions to 16km --- libraries/workload/src/workload/ViewTask.h | 2 +- scripts/developer/utilities/workload/workloadInspector.qml | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/workload/src/workload/ViewTask.h b/libraries/workload/src/workload/ViewTask.h index c20314d514..4fdd7e0f4c 100644 --- a/libraries/workload/src/workload/ViewTask.h +++ b/libraries/workload/src/workload/ViewTask.h @@ -192,7 +192,7 @@ namespace workload { struct Data { - bool regulateViewRanges{ false }; + bool regulateViewRanges{ false }; // regulation is OFF by default } data; struct DataExport { diff --git a/scripts/developer/utilities/workload/workloadInspector.qml b/scripts/developer/utilities/workload/workloadInspector.qml index c55fdee040..2eaa9d8133 100644 --- a/scripts/developer/utilities/workload/workloadInspector.qml +++ b/scripts/developer/utilities/workload/workloadInspector.qml @@ -160,9 +160,9 @@ Rectangle { } Repeater { model: [ - "r1Front:500:1.0", - "r2Front:500:1.0", - "r3Front:500:1.0" + "r1Front:16000:1.0", + "r2Front:16000:1.0", + "r3Front:16000:1.0" ] ConfigSlider { showLabel: false From 486557e28a7d35e635224e7849700b287e1c705c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 30 Jul 2018 16:29:28 -0700 Subject: [PATCH 27/64] initial trait packet sending on skeleton URL change --- assignment-client/src/avatars/AvatarMixer.cpp | 5 ++ assignment-client/src/avatars/AvatarMixer.h | 1 + interface/src/avatar/MyAvatar.cpp | 7 +- interface/src/avatar/MyAvatar.h | 20 +++-- libraries/avatars/src/AvatarTraits.h | 29 +++++++ libraries/avatars/src/ClientTraitsHandler.cpp | 84 +++++++++++++++++++ libraries/avatars/src/ClientTraitsHandler.h | 42 ++++++++++ libraries/networking/src/udt/PacketHeaders.h | 2 +- 8 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 libraries/avatars/src/AvatarTraits.h create mode 100644 libraries/avatars/src/ClientTraitsHandler.cpp create mode 100644 libraries/avatars/src/ClientTraitsHandler.h diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 8f1df9c321..a5eba6ad11 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -54,6 +54,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket"); + packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "handleSetAvatarTraitsMessage"); packetReceiver.registerListenerForTypes({ PacketType::ReplicatedAvatarIdentity, @@ -606,6 +607,10 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes _handleAvatarIdentityPacketElapsedTime += (end - start); } +void AvatarMixer::handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode) { + qDebug() << "Got a traits packet of" << message->getSize() << "bytes from" << senderNode; +} + void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointer message, SharedNodePointer senderNode) { if (message->getSize() < NUM_BYTES_RFC4122_UUID) { qCDebug(avatars) << "Malformed AvatarIdentityRequest received from" << message->getSenderSockAddr().toString(); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index c27ca33729..b0405a0b6d 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -48,6 +48,7 @@ private slots: void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); + void handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message, SharedNodePointer senderNode); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 41855e7973..3ba2cbfc1e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -119,7 +119,8 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()) + _hmdAtRestDetector(glm::vec3(0), glm::quat()), + _clientTraitsHandler(this) { // give the pointer to our head to inherited _headData variable from AvatarData @@ -513,6 +514,8 @@ void MyAvatar::update(float deltaTime) { sendIdentityPacket(); } + _clientTraitsHandler.sendChangedTraitsToMixer(); + simulate(deltaTime); currentEnergy += energyChargeRate; @@ -1696,6 +1699,8 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { saveAvatarUrl(); emit skeletonChanged(); emit skeletonModelURLChanged(); + + _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); } void MyAvatar::removeAvatarEntities(const std::function& condition) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index f2c52624d9..8fdc4ba4e3 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -18,22 +18,22 @@ #include -#include -#include -#include -#include - -#include -#include #include #include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include "AtRestDetector.h" #include "MyCharacterController.h" #include "RingBufferHistory.h" -#include -#include class AvatarActionHold; class ModelItemID; @@ -1776,6 +1776,8 @@ private: bool _haveReceivedHeightLimitsFromDomain { false }; int _disableHandTouchCount { 0 }; + + ClientTraitsHandler _clientTraitsHandler; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h new file mode 100644 index 0000000000..ebeaeaea20 --- /dev/null +++ b/libraries/avatars/src/AvatarTraits.h @@ -0,0 +1,29 @@ +// +// AvatarTraits.h +// libraries/avatars/src +// +// Created by Stephen Birarda on 7/30/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AvatarTraits_h +#define hifi_AvatarTraits_h + +#include + +namespace AvatarTraits { + enum Trait : uint8_t { + SkeletonModelURL, + TotalTraits + }; + + using TraitVersion = uint32_t; + const TraitVersion DEFAULT_TRAIT_VERSION = 0; + + using TraitVersions = std::vector; +} + +#endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp new file mode 100644 index 0000000000..c4685461a0 --- /dev/null +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -0,0 +1,84 @@ +// +// ClientTraitsHandler.cpp +// libraries/avatars/src +// +// Created by Stephen Birarda on 7/30/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#include "ClientTraitsHandler.h" + +#include + +#include +#include + +#include "AvatarData.h" + +ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) : + _owningAvatar(owningAvatar) +{ + auto nodeList = DependencyManager::get(); + QObject::connect(nodeList.data(), &NodeList::nodeAdded, [this](SharedNodePointer addedNode){ + if (addedNode->getType() == NodeType::AvatarMixer) { + resetForNewMixer(); + } + }); +} + +void ClientTraitsHandler::resetForNewMixer() { + // re-set the current version to 0 + _currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION; + + // mark that all traits should be sent next time + _performInitialSend = true; +} + +void ClientTraitsHandler::sendChangedTraitsToMixer() { + if (hasChangedTraits() || _performInitialSend) { + // we have at least one changed trait to send + + auto nodeList = DependencyManager::get(); + auto avatarMixer = nodeList->soloNodeOfType(NodeType::AvatarMixer); + if (!avatarMixer || !avatarMixer->getActiveSocket()) { + // we don't have an avatar mixer with an active socket, we can't send changed traits at this time + return; + } + + // we have a mixer to send to, setup our set traits packet + + // bump and write the current trait version to an extended header + // the trait version is the same for all traits in this packet list + ++_currentTraitVersion; + QByteArray extendedHeader(reinterpret_cast(&_currentTraitVersion), sizeof(_currentTraitVersion)); + + auto traitsPacketList = NLPacketList::create(PacketType::SetAvatarTraits, extendedHeader, true); + + // take a copy of the set of changed traits and clear the stored set + auto changedTraitsCopy { _changedTraits }; + _changedTraits.clear(); + + if (_performInitialSend || changedTraitsCopy.count(AvatarTraits::SkeletonModelURL)) { + traitsPacketList->startSegment(); + + traitsPacketList->writePrimitive(AvatarTraits::SkeletonModelURL); + + auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded(); + + uint16_t encodedURLSize = encodedSkeletonURL.size(); + traitsPacketList->writePrimitive(encodedURLSize); + + traitsPacketList->write(encodedSkeletonURL); + + traitsPacketList->endSegment(); + } + + nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); + + // if this was an initial send of all traits, consider it completed + _performInitialSend = false; + } +} diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h new file mode 100644 index 0000000000..9fd3104e7e --- /dev/null +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -0,0 +1,42 @@ +// +// ClientTraitsHandler.h +// libraries/avatars/src +// +// Created by Stephen Birarda on 7/30/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ClientTraitsHandler_h +#define hifi_ClientTraitsHandler_h + +#include + +#include "AvatarTraits.h" + +class AvatarData; + +class ClientTraitsHandler { +public: + ClientTraitsHandler(AvatarData* owningAvatar); + + void sendChangedTraitsToMixer(); + + bool hasChangedTraits() { return _changedTraits.size(); } + void markTraitChanged(AvatarTraits::Trait changedTrait) { _changedTraits.insert(changedTrait); } + + bool hasTraitChanged(AvatarTraits::Trait checkTrait) { return _changedTraits.count(checkTrait) > 0; } + + void resetForNewMixer(); + +private: + AvatarData* _owningAvatar; + + std::set _changedTraits; + AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; + bool _performInitialSend { false }; +}; + +#endif // hifi_ClientTraitsHandler_h diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 126dac7c8f..3990afa79a 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -56,7 +56,7 @@ public: ICEServerPeerInformation, ICEServerQuery, OctreeStats, - UNUSED_PACKET_TYPE_1, + SetAvatarTraits, AvatarIdentityRequest, AssignmentClientStatus, NoisyMute, From ac835650b74421be618aaef7bd362f6dd17c0e95 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 30 Jul 2018 17:33:01 -0700 Subject: [PATCH 28/64] add skeleton trait sending to Agent, queuing in AvatarMixer --- assignment-client/src/Agent.h | 1 + assignment-client/src/avatars/AvatarMixer.cpp | 6 +----- assignment-client/src/avatars/AvatarMixer.h | 3 --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 8 ++++++++ assignment-client/src/avatars/AvatarMixerClientData.h | 2 ++ assignment-client/src/avatars/ScriptableAvatar.cpp | 5 +++++ assignment-client/src/avatars/ScriptableAvatar.h | 5 ++++- interface/src/avatar/MyAvatar.cpp | 9 ++++++++- libraries/avatars/src/AvatarTraits.h | 2 -- 9 files changed, 29 insertions(+), 12 deletions(-) diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h index 7d47c8e713..2b5ff51b49 100644 --- a/assignment-client/src/Agent.h +++ b/assignment-client/src/Agent.h @@ -21,6 +21,7 @@ #include #include +#include #include #include #include diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index a5eba6ad11..56fb15c9f0 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -54,7 +54,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) : packetReceiver.registerListener(PacketType::RadiusIgnoreRequest, this, "handleRadiusIgnoreRequestPacket"); packetReceiver.registerListener(PacketType::RequestsDomainListData, this, "handleRequestsDomainListDataPacket"); packetReceiver.registerListener(PacketType::AvatarIdentityRequest, this, "handleAvatarIdentityRequestPacket"); - packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "handleSetAvatarTraitsMessage"); + packetReceiver.registerListener(PacketType::SetAvatarTraits, this, "queueIncomingPacket"); packetReceiver.registerListenerForTypes({ PacketType::ReplicatedAvatarIdentity, @@ -607,10 +607,6 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes _handleAvatarIdentityPacketElapsedTime += (end - start); } -void AvatarMixer::handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode) { - qDebug() << "Got a traits packet of" << message->getSize() << "bytes from" << senderNode; -} - void AvatarMixer::handleAvatarIdentityRequestPacket(QSharedPointer message, SharedNodePointer senderNode) { if (message->getSize() < NUM_BYTES_RFC4122_UUID) { qCDebug(avatars) << "Malformed AvatarIdentityRequest received from" << message->getSenderSockAddr().toString(); diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index b0405a0b6d..14f84c9e7b 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -48,7 +48,6 @@ private slots: void handleAdjustAvatarSorting(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarQueryPacket(QSharedPointer message, SharedNodePointer senderNode); void handleAvatarIdentityPacket(QSharedPointer message, SharedNodePointer senderNode); - void handleSetAvatarTraitsMessage(QSharedPointer message, SharedNodePointer senderNode); void handleKillAvatarPacket(QSharedPointer message, SharedNodePointer senderNode); void handleNodeIgnoreRequestPacket(QSharedPointer message, SharedNodePointer senderNode); void handleRadiusIgnoreRequestPacket(QSharedPointer packet, SharedNodePointer sendingNode); @@ -127,9 +126,7 @@ private: RateCounter<> _loopRate; // this is the rate that the main thread tight loop runs - AvatarMixerSlavePool _slavePool; - }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 9aa3e88b52..f6718b839a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -62,6 +62,9 @@ int AvatarMixerClientData::processPackets() { case PacketType::AvatarData: parseData(*packet); break; + case PacketType::SetAvatarTraits: + processSetTraitsMessage(*packet); + break; default: Q_UNREACHABLE(); } @@ -87,6 +90,11 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { // compute the offset to the data payload return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead())); } + +void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { + qDebug() << "Pulling a traits message of" << message.getSize(); +} + uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastTimes.find(nodeUUID); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index e038e81505..2e30b2c8a1 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -120,6 +120,8 @@ public: void queuePacket(QSharedPointer message, SharedNodePointer node); int processPackets(); // returns number of packets processed + void processSetTraitsMessage(ReceivedMessage& message); + private: struct PacketQueue : public std::queue> { QWeakPointer node; diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index e7210db83a..4e47cf96f1 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -61,7 +61,10 @@ AnimationDetails ScriptableAvatar::getAnimationDetails() { void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _bind.reset(); _animSkeleton.reset(); + AvatarData::setSkeletonModelURL(skeletonModelURL); + + _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); } static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { @@ -137,4 +140,6 @@ void ScriptableAvatar::update(float deltatime) { _animation.clear(); } } + + _clientTraitsHandler.sendChangedTraitsToMixer(); } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index d34ad2d21e..8e3d779dda 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -15,6 +15,7 @@ #include #include #include +#include #include /**jsdoc @@ -123,7 +124,7 @@ class ScriptableAvatar : public AvatarData, public Dependency { Q_OBJECT public: - + /**jsdoc * @function Avatar.startAnimation * @param {string} url @@ -164,6 +165,8 @@ private: QStringList _maskedJoints; AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies std::shared_ptr _animSkeleton; + + ClientTraitsHandler _clientTraitsHandler { this }; }; #endif // hifi_ScriptableAvatar_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 3ba2cbfc1e..53ad8c0a0f 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1669,7 +1669,10 @@ void MyAvatar::clearJointsData() { void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _skeletonModelChangeCount++; int skeletonModelChangeCount = _skeletonModelChangeCount; + + auto previousSkeletonModelURL = _skeletonModelURL; Avatar::setSkeletonModelURL(skeletonModelURL); + _skeletonModel->setTagMask(render::hifi::TAG_NONE); _skeletonModel->setGroupCulled(true); _skeletonModel->setVisibleInScene(true, qApp->getMain3DScene()); @@ -1700,7 +1703,11 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); emit skeletonModelURLChanged(); - _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); + if (previousSkeletonModelURL != _skeletonModelURL) { + _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); + } else { + qDebug() << "Not marking skeleton model URL trait changed since the new value matches the previous"; + } } void MyAvatar::removeAvatarEntities(const std::function& condition) { diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index ebeaeaea20..c35bfae95c 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -22,8 +22,6 @@ namespace AvatarTraits { using TraitVersion = uint32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; - - using TraitVersions = std::vector; } #endif // hifi_AvatarTraits_h From f23a036f4aab08ab49a2cd49803e9dc0353076b6 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 11:29:03 -0700 Subject: [PATCH 29/64] add node local ID retreivable from NodeData --- assignment-client/src/audio/AudioMixer.cpp | 2 +- assignment-client/src/audio/AudioMixerClientData.cpp | 4 ++-- assignment-client/src/audio/AudioMixerClientData.h | 2 +- assignment-client/src/avatars/AvatarMixer.cpp | 2 +- assignment-client/src/avatars/AvatarMixerClientData.cpp | 2 +- assignment-client/src/avatars/AvatarMixerClientData.h | 2 +- libraries/networking/src/NodeData.cpp | 9 ++++----- libraries/networking/src/NodeData.h | 5 ++++- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/assignment-client/src/audio/AudioMixer.cpp b/assignment-client/src/audio/AudioMixer.cpp index d56b22466e..0d42cc83be 100644 --- a/assignment-client/src/audio/AudioMixer.cpp +++ b/assignment-client/src/audio/AudioMixer.cpp @@ -366,7 +366,7 @@ AudioMixerClientData* AudioMixer::getOrCreateClientData(Node* node) { auto clientData = dynamic_cast(node->getLinkedData()); if (!clientData) { - node->setLinkedData(std::unique_ptr { new AudioMixerClientData(node->getUUID()) }); + node->setLinkedData(std::unique_ptr { new AudioMixerClientData(node->getUUID(), node->getLocalID()) }); clientData = dynamic_cast(node->getLinkedData()); connect(clientData, &AudioMixerClientData::injectorStreamFinished, this, &AudioMixer::removeHRTFsForFinishedInjector); } diff --git a/assignment-client/src/audio/AudioMixerClientData.cpp b/assignment-client/src/audio/AudioMixerClientData.cpp index bc08c6f24a..07cc5493b0 100644 --- a/assignment-client/src/audio/AudioMixerClientData.cpp +++ b/assignment-client/src/audio/AudioMixerClientData.cpp @@ -25,8 +25,8 @@ #include "AudioHelpers.h" #include "AudioMixer.h" -AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID) : - NodeData(nodeID), +AudioMixerClientData::AudioMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : + NodeData(nodeID, nodeLocalID), audioLimiter(AudioConstants::SAMPLE_RATE, AudioConstants::STEREO), _ignoreZone(*this), _outgoingMixedAudioSequenceNumber(0), diff --git a/assignment-client/src/audio/AudioMixerClientData.h b/assignment-client/src/audio/AudioMixerClientData.h index 514e1c9756..82bdc0e5c5 100644 --- a/assignment-client/src/audio/AudioMixerClientData.h +++ b/assignment-client/src/audio/AudioMixerClientData.h @@ -30,7 +30,7 @@ class AudioMixerClientData : public NodeData { Q_OBJECT public: - AudioMixerClientData(const QUuid& nodeID); + AudioMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID); ~AudioMixerClientData(); using SharedStreamPointer = std::shared_ptr; diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 56fb15c9f0..b139870e6e 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -904,7 +904,7 @@ AvatarMixerClientData* AvatarMixer::getOrCreateClientData(SharedNodePointer node auto clientData = dynamic_cast(node->getLinkedData()); if (!clientData) { - node->setLinkedData(std::unique_ptr { new AvatarMixerClientData(node->getUUID()) }); + node->setLinkedData(std::unique_ptr { new AvatarMixerClientData(node->getUUID(), node->getLocalID()) }); clientData = dynamic_cast(node->getLinkedData()); auto& avatar = clientData->getAvatar(); avatar.setDomainMinimumHeight(_domainMinimumHeight); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index f6718b839a..19016ae80e 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -16,7 +16,7 @@ #include #include -AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) : +AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 2e30b2c8a1..ceb66a9d22 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -36,7 +36,7 @@ const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; class AvatarMixerClientData : public NodeData { Q_OBJECT public: - AvatarMixerClientData(const QUuid& nodeID = QUuid()); + AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID); virtual ~AvatarMixerClientData() {} using HRCTime = p_high_resolution_clock::time_point; diff --git a/libraries/networking/src/NodeData.cpp b/libraries/networking/src/NodeData.cpp index 300c76ca28..dd926852cd 100644 --- a/libraries/networking/src/NodeData.cpp +++ b/libraries/networking/src/NodeData.cpp @@ -11,13 +11,12 @@ #include "NodeData.h" -NodeData::NodeData(const QUuid& nodeID) : +NodeData::NodeData(const QUuid& nodeID, NetworkPeer::LocalID nodeLocalID) : _mutex(), - _nodeID(nodeID) + _nodeID(nodeID), + _nodeLocalID(nodeLocalID) { } -NodeData::~NodeData() { - -} +NodeData::~NodeData() {} diff --git a/libraries/networking/src/NodeData.h b/libraries/networking/src/NodeData.h index 9aeac03837..ffbc0c2376 100644 --- a/libraries/networking/src/NodeData.h +++ b/libraries/networking/src/NodeData.h @@ -16,6 +16,7 @@ #include #include +#include "NetworkPeer.h" #include "NLPacket.h" #include "ReceivedMessage.h" @@ -24,17 +25,19 @@ class Node; class NodeData : public QObject { Q_OBJECT public: - NodeData(const QUuid& nodeID = QUuid()); + NodeData(const QUuid& nodeID = QUuid(), NetworkPeer::LocalID localID = NetworkPeer::NULL_LOCAL_ID); virtual ~NodeData() = 0; virtual int parseData(ReceivedMessage& message) { return 0; } const QUuid& getNodeID() const { return _nodeID; } + NetworkPeer::LocalID getNodeLocalID() const { return _nodeLocalID; } QMutex& getMutex() { return _mutex; } private: QMutex _mutex; QUuid _nodeID; + NetworkPeer::LocalID _nodeLocalID; }; #endif // hifi_NodeData_h From 26a1f0331478d15fbe0824542b14b0cf61db4a4b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 14:02:07 -0700 Subject: [PATCH 30/64] send traits in bulk to avatar mixer client --- .../src/avatars/AvatarMixerClientData.cpp | 75 ++++++++++++++++++- .../src/avatars/AvatarMixerClientData.h | 21 ++++++ .../src/avatars/AvatarMixerSlave.cpp | 72 +++++++++++++++++- .../src/avatars/AvatarMixerSlave.h | 4 + libraries/avatars/src/AvatarTraits.h | 14 +++- libraries/avatars/src/ClientTraitsHandler.cpp | 4 +- libraries/avatars/src/ClientTraitsHandler.h | 6 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 8 files changed, 188 insertions(+), 11 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 19016ae80e..cf30aca1a7 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -17,7 +17,8 @@ #include AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID) + NodeData(nodeID), + _receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size()) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); @@ -92,7 +93,41 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { } void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { - qDebug() << "Pulling a traits message of" << message.getSize(); + // pull the trait version from the message + AvatarTraits::TraitVersion packetTraitVersion; + message.readPrimitive(&packetTraitVersion); + + bool anyTraitsChanged = false; + + while (message.getBytesLeftToRead() > 0) { + // for each trait in the packet, apply it if the trait version is newer than what we have + + AvatarTraits::TraitType traitType; + message.readPrimitive(&traitType); + + AvatarTraits::TraitWireSize traitSize; + message.readPrimitive(&traitSize); + + if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { + if (traitType == AvatarTraits::SkeletonModelURL) { + // get the URL from the binary data + auto skeletonModelURL = QUrl::fromEncoded(message.read(traitSize)); + _avatar->setSkeletonModelURL(skeletonModelURL); + + qDebug() << "Set skeleton URL to" << skeletonModelURL << "for trait packet version" << packetTraitVersion; + + _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + + anyTraitsChanged = true; + } + } else { + message.seek(message.getPosition() + traitSize); + } + } + + if (anyTraitsChanged) { + _lastReceivedTraitsChange = std::chrono::steady_clock::now(); + } } uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { @@ -172,3 +207,39 @@ void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const { jsonObject["recent_other_av_in_view"] = _recentOtherAvatarsInView; jsonObject["recent_other_av_out_of_view"] = _recentOtherAvatarsOutOfView; } + +AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const { + auto it = _lastSentTraitsTimestamps.find(otherAvatar); + + if (it != _lastSentTraitsTimestamps.end()) { + return it->second; + } else { + return TraitsCheckTimestamp(); + } +} + +AvatarTraits::TraitVersion AvatarMixerClientData::getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, + AvatarTraits::TraitType traitType) const { + auto it = _sentSimpleTraitVersions.find(otherAvatar); + + if (it != _sentSimpleTraitVersions.end()) { + return it->second[traitType]; + } + + return AvatarTraits::DEFAULT_TRAIT_VERSION; +} + +void AvatarMixerClientData::setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion traitVersion) { + + auto it = _sentSimpleTraitVersions.find(otherAvatar); + + if (it == _sentSimpleTraitVersions.end()) { + auto pair = _sentSimpleTraitVersions.insert({ + otherAvatar, { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } + }); + + it = pair.first; + } + + it->second[traitType] = traitVersion; +} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index ceb66a9d22..8792ecfa5d 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -122,6 +123,20 @@ public: void processSetTraitsMessage(ReceivedMessage& message); + using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; + + TraitsCheckTimestamp getLastReceivedTraitsChange() const { return _lastReceivedTraitsChange; } + AvatarTraits::TraitVersion getLastReceivedSimpleTraitVersion(AvatarTraits::TraitType traitType) const + { return _receivedSimpleTraitVersions[traitType]; } + + TraitsCheckTimestamp getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const; + void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint) + { _lastSentTraitsTimestamps[otherAvatar] = sendPoint; } + + AvatarTraits::TraitVersion getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType) const; + void setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, + AvatarTraits::TraitVersion traitVersion); + private: struct PacketQueue : public std::queue> { QWeakPointer node; @@ -158,6 +173,12 @@ private: int _recentOtherAvatarsOutOfView { 0 }; QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. bool _requestsDomainListData { false }; + + AvatarTraits::SimpleTraitVersions _receivedSimpleTraitVersions; + TraitsCheckTimestamp _lastReceivedTraitsChange; + + std::unordered_map _lastSentTraitsTimestamps; + std::unordered_map _sentSimpleTraitVersions; }; #endif // hifi_AvatarMixerClientData_h diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 563aac879f..c996008a48 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -79,6 +79,61 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, } } +void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList) { + + auto otherNodeLocalID = sendingNodeData->getNodeLocalID(); + + // Perform a simple check with two server clock time points + // to see if there is any new traits data for this avatar that we need to send + auto timeOfLastTraitsSent = listeningNodeData->getLastOtherAvatarTraitsSendPoint(otherNodeLocalID); + auto timeOfLastTraitsChange = sendingNodeData->getLastReceivedTraitsChange(); + + if (timeOfLastTraitsChange > timeOfLastTraitsSent) { + // there is definitely new traits data to send + + // add the avatar ID to mark the beginning of traits for this avatar + traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122()); + + auto sendingAvatar = sendingNodeData->getAvatarSharedPointer(); + + // compare trait versions so we can see what exactly needs to go out + for (int i = 0; i < AvatarTraits::TotalTraitTypes; ++i) { + AvatarTraits::TraitType traitType = static_cast(i); + + auto lastSentVersion = listeningNodeData->getLastSentSimpleTraitVersion(otherNodeLocalID, traitType); + auto lastReceivedVersion = sendingNodeData->getLastReceivedSimpleTraitVersion(traitType); + + if (lastReceivedVersion > lastSentVersion) { + // there is an update to this trait, add it to the traits packet + + // write the trait type and the trait version + traitsPacketList.writePrimitive(traitType); + traitsPacketList.writePrimitive(lastReceivedVersion); + + // update the last sent version since we're adding this to the packet + listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion); + + if (traitType == AvatarTraits::SkeletonModelURL) { + // get an encoded version of the URL, write its size and then the data itself + auto encodedSkeletonURL = sendingAvatar->getSkeletonModelURL().toEncoded(); + + traitsPacketList.writePrimitive(uint16_t(encodedSkeletonURL.size())); + traitsPacketList.write(encodedSkeletonURL); + } + } + } + + // write a null trait type to mark the end of trait data for this avatar + traitsPacketList.writePrimitive(AvatarTraits::NullTrait); + + // since we send all traits for this other avatar, update the time of last traits sent + // to match the time of last traits change + listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange); + } +} + int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) { if (AvatarMixer::shouldReplicateTo(agentNode, destinationNode)) { QByteArray individualData = nodeData->getConstAvatarData()->identityByteArray(true); @@ -326,6 +381,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // loop through our sorted avatars and allocate our bandwidth to them accordingly int remainingAvatars = (int)sortedAvatars.size(); + auto traitsPacketList = NLPacketList::create(PacketType::BulkAvatarTraits, QByteArray(), true, true); while (!sortedAvatars.empty()) { const auto avatarData = sortedAvatars.top().getAvatar(); sortedAvatars.pop(); @@ -392,11 +448,12 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 start = usecTimestampNow(); QByteArray bytes = otherAvatar->toByteArray(detail, lastEncodeForOther, lastSentJointsForOther, - hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, &lastSentJointsForOther); + hasFlagsOut, dropFaceTracking, distanceAdjust, viewerPosition, + &lastSentJointsForOther); quint64 end = usecTimestampNow(); _stats.toByteArrayElapsedTime += (end - start); - auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; + static auto maxAvatarDataBytes = avatarPacketList->getMaxSegmentSize() - NUM_BYTES_RFC4122_UUID; if (bytes.size() > maxAvatarDataBytes) { qCWarning(avatars) << "otherAvatar.toByteArray() for" << otherNode->getUUID() << "resulted in very large buffer of" << bytes.size() << "bytes - dropping facial data"; @@ -445,6 +502,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) quint64 endAvatarDataPacking = usecTimestampNow(); _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); + + // use helper to add any changed traits to our packet list + addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); } quint64 startPacketSending = usecTimestampNow(); @@ -461,6 +521,14 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // record the bytes sent for other avatar data in the AvatarMixerClientData nodeData->recordSentAvatarData(numAvatarDataBytes); + // close the current traits packet list + traitsPacketList->closeCurrentPacket(); + + if (traitsPacketList->getNumPackets() >= 1) { + // send the traits packet list + nodeList->sendPacketList(std::move(traitsPacketList), *node); + } + // record the number of avatars held back this frame nodeData->recordNumOtherAvatarStarves(numAvatarsHeldBack); nodeData->recordNumOtherAvatarSkips(numAvatarsWithSkippedFrames); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 7be119c4b2..ed27709e3e 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -99,6 +99,10 @@ private: int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode); + void addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList); + void broadcastAvatarDataToAgent(const SharedNodePointer& node); void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index c35bfae95c..90258982c3 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -13,15 +13,25 @@ #define hifi_AvatarTraits_h #include +#include +#include namespace AvatarTraits { - enum Trait : uint8_t { + enum TraitType : int8_t { + NullTrait = -1, SkeletonModelURL, - TotalTraits + TotalTraitTypes }; + using TraitTypeSet = std::set; + const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL }; + using TraitVersion = uint32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; + + using TraitWireSize = uint16_t; + + using SimpleTraitVersions = std::vector; } #endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index c4685461a0..5d1f2e4926 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -68,9 +68,11 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded(); - uint16_t encodedURLSize = encodedSkeletonURL.size(); + AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); traitsPacketList->writePrimitive(encodedURLSize); + qDebug() << "Sending trait of size" << encodedURLSize; + traitsPacketList->write(encodedSkeletonURL); traitsPacketList->endSegment(); diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 9fd3104e7e..4aea0bb433 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -25,16 +25,16 @@ public: void sendChangedTraitsToMixer(); bool hasChangedTraits() { return _changedTraits.size(); } - void markTraitChanged(AvatarTraits::Trait changedTrait) { _changedTraits.insert(changedTrait); } + void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); } - bool hasTraitChanged(AvatarTraits::Trait checkTrait) { return _changedTraits.count(checkTrait) > 0; } + bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.count(checkTrait) > 0; } void resetForNewMixer(); private: AvatarData* _owningAvatar; - std::set _changedTraits; + AvatarTraits::TraitTypeSet _changedTraits; AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; bool _performInitialSend { false }; }; diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 3990afa79a..616694c8da 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -133,7 +133,8 @@ public: EntityClone, EntityQueryInitialResultsComplete, - + BulkAvatarTraits, + NUM_PACKET_TYPE }; From a80d19a44a393c45e6855cd97c0a9d8bf49e72d2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 15:38:57 -0700 Subject: [PATCH 31/64] remove skeleton from identity, handle in clients --- assignment-client/src/Agent.cpp | 5 -- .../src/avatars/AvatarMixerClientData.cpp | 15 +--- .../src/scripts/EntityScriptServer.cpp | 3 - interface/src/avatar/AvatarManager.cpp | 4 - interface/src/avatar/MyAvatar.cpp | 2 - libraries/avatars/src/AvatarData.cpp | 27 +++---- libraries/avatars/src/AvatarData.h | 7 +- libraries/avatars/src/AvatarHashMap.cpp | 76 ++++++++++++++++++- libraries/avatars/src/AvatarHashMap.h | 7 ++ .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 3 +- 11 files changed, 104 insertions(+), 47 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index f534be9346..4cc24e2110 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -447,11 +447,6 @@ void Agent::executeScript() { auto avatarHashMap = DependencyManager::set(); _scriptEngine->registerGlobalObject("AvatarList", avatarHashMap.data()); - auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); - packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); - packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); - packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); - // register ourselves to the script engine _scriptEngine->registerGlobalObject("Agent", new AgentScriptingInterface(this)); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index cf30aca1a7..f279d76450 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -109,17 +109,10 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { message.readPrimitive(&traitSize); if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { - if (traitType == AvatarTraits::SkeletonModelURL) { - // get the URL from the binary data - auto skeletonModelURL = QUrl::fromEncoded(message.read(traitSize)); - _avatar->setSkeletonModelURL(skeletonModelURL); - - qDebug() << "Set skeleton URL to" << skeletonModelURL << "for trait packet version" << packetTraitVersion; - - _receivedSimpleTraitVersions[traitType] = packetTraitVersion; - - anyTraitsChanged = true; - } + _avatar->processTrait(traitType, message.readWithoutCopy(traitSize)); + _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + + anyTraitsChanged = true; } else { message.seek(message.getPosition() + traitSize); } diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp index 0e1126cebe..05002828f5 100644 --- a/assignment-client/src/scripts/EntityScriptServer.cpp +++ b/assignment-client/src/scripts/EntityScriptServer.cpp @@ -81,9 +81,6 @@ EntityScriptServer::EntityScriptServer(ReceivedMessage& message) : ThreadedAssig packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); auto avatarHashMap = DependencyManager::set(); - packetReceiver.registerListener(PacketType::BulkAvatarData, avatarHashMap.data(), "processAvatarDataPacket"); - packetReceiver.registerListener(PacketType::KillAvatar, avatarHashMap.data(), "processKillAvatar"); - packetReceiver.registerListener(PacketType::AvatarIdentity, avatarHashMap.data(), "processAvatarIdentityPacket"); packetReceiver.registerListener(PacketType::ReloadEntityServerScript, this, "handleReloadEntityServerScriptPacket"); packetReceiver.registerListener(PacketType::EntityScriptGetStatus, this, "handleEntityScriptGetStatusPacket"); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 09fa6dc573..8569aaf05a 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -72,10 +72,6 @@ AvatarManager::AvatarManager(QObject* parent) : qRegisterMetaType >("NodeWeakPointer"); auto nodeList = DependencyManager::get(); - auto& packetReceiver = nodeList->getPacketReceiver(); - packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); - packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar"); - packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket"); // when we hear that the user has ignored an avatar by session UUID // immediately remove that avatar instead of waiting for the absence of packets from avatar mixer diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 53ad8c0a0f..df9afeb92d 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1705,8 +1705,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (previousSkeletonModelURL != _skeletonModelURL) { _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); - } else { - qDebug() << "Not marking skeleton model URL trait changed since the new value matches the previous"; } } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 62c7a7053f..ddfeb4df24 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -42,6 +42,7 @@ #include #include "AvatarLogging.h" +#include "AvatarTraits.h" //#define WANT_DEBUG @@ -1756,7 +1757,7 @@ QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { } void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, - bool& displayNameChanged, bool& skeletonModelUrlChanged) { + bool& displayNameChanged) { QDataStream packetStream(identityData); @@ -1777,7 +1778,7 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide if (incomingSequenceNumber > _identitySequenceNumber) { Identity identity; - packetStream >> identity.skeletonModelURL + packetStream >> identity.attachmentData >> identity.displayName >> identity.sessionDisplayName @@ -1789,16 +1790,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide // set the store identity sequence number to match the incoming identity _identitySequenceNumber = incomingSequenceNumber; - if (_firstSkeletonCheck || (identity.skeletonModelURL != cannonicalSkeletonModelURL(emptyURL))) { - setSkeletonModelURL(identity.skeletonModelURL); - identityChanged = true; - skeletonModelUrlChanged = true; - if (_firstSkeletonCheck) { - displayNameChanged = true; - } - _firstSkeletonCheck = false; - } - if (identity.displayName != _displayName) { _displayName = identity.displayName; identityChanged = true; @@ -1834,7 +1825,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide #ifdef WANT_DEBUG qCDebug(avatars) << __FUNCTION__ << "identity.uuid:" << identity.uuid - << "identity.skeletonModelURL:" << identity.skeletonModelURL << "identity.displayName:" << identity.displayName << "identity.sessionDisplayName:" << identity.sessionDisplayName; } else { @@ -1846,10 +1836,18 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide } } +void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { + if (traitType == AvatarTraits::SkeletonModelURL) { + // get the URL from the binary data + auto skeletonModelURL = QUrl::fromEncoded(traitBinaryData); + qDebug() << "Setting skeleton model URL from trait packet to" << skeletonModelURL; + setSkeletonModelURL(skeletonModelURL); + } +} + QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); - const QUrl& urlToSend = cannonicalSkeletonModelURL(emptyURL); // depends on _skeletonModelURL // when mixers send identity packets to agents, they simply forward along the last incoming sequence number they received // whereas agents send a fresh outgoing sequence number when identity data has changed @@ -1857,7 +1855,6 @@ QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { _avatarEntitiesLock.withReadLock([&] { identityStream << getSessionUUID() << (udt::SequenceNumber::Type) _identitySequenceNumber - << urlToSend << _attachmentData << _displayName << getSessionDisplayNameForTransport() // depends on _sessionDisplayName diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index fcc63fdc98..12e8370b86 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -51,6 +51,7 @@ #include #include "AABox.h" +#include "AvatarTraits.h" #include "HeadData.h" #include "PathUtils.h" @@ -955,8 +956,9 @@ public: // identityChanged returns true if identity has changed, false otherwise. // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. - void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, - bool& displayNameChanged, bool& skeletonModelUrlChanged); + void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + + void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); QByteArray identityByteArray(bool setIsReplicated = false) const; @@ -1327,7 +1329,6 @@ protected: mutable HeadData* _headData { nullptr }; QUrl _skeletonModelURL; - bool _firstSkeletonCheck { true }; QUrl _skeletonFBXURL; QVector _attachmentData; QVector _oldAttachmentData; diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 174e81bb31..407e88e27c 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -19,10 +19,17 @@ #include #include "AvatarLogging.h" +#include "AvatarTraits.h" AvatarHashMap::AvatarHashMap() { auto nodeList = DependencyManager::get(); + auto& packetReceiver = nodeList->getPacketReceiver(); + packetReceiver.registerListener(PacketType::BulkAvatarData, this, "processAvatarDataPacket"); + packetReceiver.registerListener(PacketType::KillAvatar, this, "processKillAvatar"); + packetReceiver.registerListener(PacketType::AvatarIdentity, this, "processAvatarIdentityPacket"); + packetReceiver.registerListener(PacketType::BulkAvatarTraits, this, "processBulkAvatarTraits"); + connect(nodeList.data(), &NodeList::uuidChanged, this, &AvatarHashMap::sessionUUIDChanged); } @@ -182,9 +189,74 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer auto avatar = newOrExistingAvatar(identityUUID, sendingNode, isNewAvatar); bool identityChanged = false; bool displayNameChanged = false; - bool skeletonModelUrlChanged = false; // In this case, the "sendingNode" is the Avatar Mixer. - avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged, skeletonModelUrlChanged); + avatar->processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); + } +} + +bool AvatarHashMap::checkLastProcessedTraitVersion(QUuid avatarID, + AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion) { + auto it = _processedSimpleTraitVersions.find(avatarID); + if (it == _processedSimpleTraitVersions.end()) { + auto pair = _processedSimpleTraitVersions.insert({ + avatarID, + { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } + }); + + it = pair.first; + }; + + if (it->second[traitType] < newVersion) { + it->second[traitType] = newVersion; + return true; + } else { + return false; + } +} + +void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { + while (message->getBytesLeftToRead()) { + // read the avatar ID to figure out which avatar this is for + auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + // grab the avatar so we can ask it to process trait data + AvatarSharedPointer avatar; + + QReadLocker locker(&_hashLock); + auto it = _avatarHash.find(avatarID); + if (it != _avatarHash.end()) { + avatar = *it; + } + locker.unlock(); + + // read the first trait type for this avatar + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + while (traitType != AvatarTraits::NullTrait) { + AvatarTraits::TraitVersion traitVersion; + message->readPrimitive(&traitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + message->readPrimitive(&traitBinarySize); + + if (avatar) { + // check if this trait version is newer than what we already have for this avatar + bool traitIsNewer = checkLastProcessedTraitVersion(avatarID, traitType, traitVersion); + if (traitIsNewer) { + avatar->processTrait(traitType, message->readWithoutCopy(traitBinarySize)); + } else { + message->seek(message->getPosition() + traitBinarySize); + } + } else { + // though we have no avatar pointer, we still hop through the packet in case there are + // traits for avatars we do have later in the packet + message->seek(message->getPosition() + traitBinarySize); + } + + // read the next trait type, which is null if there are no more traits for this avatar + message->readPrimitive(&traitType); + } } } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index fd2cd76fbf..ed8440eb89 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -30,6 +30,7 @@ #include "ScriptAvatarData.h" #include "AvatarData.h" +#include "AvatarTraits.h" /**jsdoc * Note: An AvatarList API is also provided for Interface and client entity scripts: it is a @@ -133,6 +134,8 @@ protected slots: */ void processAvatarIdentityPacket(QSharedPointer message, SharedNodePointer sendingNode); + void processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode); + /**jsdoc * @function AvatarList.processKillAvatar * @param {} message @@ -153,6 +156,9 @@ protected: virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); + bool checkLastProcessedTraitVersion(QUuid avatarID, + AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion); + AvatarHash _avatarHash; struct PendingAvatar { std::chrono::steady_clock::time_point creationTime; @@ -163,6 +169,7 @@ protected: AvatarPendingHash _pendingAvatars; mutable QReadWriteLock _hashLock; + std::unordered_map _processedSimpleTraitVersions; private: QUuid _lastOwnerSessionUUID; }; diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 8b50e37699..94137786cd 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::FarGrabJoints); + return static_cast(AvatarMixerPacketVersion::MigrateSkeletonURLToTraits); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 616694c8da..31724ab5dc 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -134,7 +134,7 @@ public: EntityClone, EntityQueryInitialResultsComplete, BulkAvatarTraits, - + NUM_PACKET_TYPE }; @@ -291,6 +291,7 @@ enum class AvatarMixerPacketVersion : PacketVersion { FixMannequinDefaultAvatarFeet, ProceduralFaceMovementFlagsAndBlendshapes, FarGrabJoints + MigrateSkeletonURLToTraits }; enum class DomainConnectRequestVersion : PacketVersion { From be7eb572058815eec125ee1d43880acaf6bd8b66 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 7 Aug 2018 18:08:10 -0700 Subject: [PATCH 32/64] handle whitelist avatar URL override via traits --- assignment-client/src/Agent.cpp | 23 ++++++-- assignment-client/src/AssignmentClient.cpp | 19 +------ assignment-client/src/avatars/AvatarMixer.cpp | 53 +++++-------------- assignment-client/src/avatars/AvatarMixer.h | 8 +-- .../src/avatars/AvatarMixerClientData.cpp | 53 +++++++++++++++++-- .../src/avatars/AvatarMixerClientData.h | 10 ++-- .../src/avatars/AvatarMixerSlave.cpp | 16 ++---- .../src/avatars/AvatarMixerSlave.h | 6 +++ .../src/avatars/AvatarMixerSlavePool.cpp | 2 +- .../src/avatars/AvatarMixerSlavePool.h | 8 ++- interface/src/Application.cpp | 4 +- libraries/avatars/src/AvatarData.cpp | 27 +++++++--- libraries/avatars/src/AvatarData.h | 2 +- libraries/avatars/src/AvatarTraits.h | 3 ++ libraries/avatars/src/ClientTraitsHandler.cpp | 48 ++++++++++++----- libraries/avatars/src/ClientTraitsHandler.h | 12 ++++- libraries/networking/src/ExtendedIODevice.h | 39 ++++++++++++++ libraries/networking/src/udt/BasePacket.cpp | 2 +- libraries/networking/src/udt/BasePacket.h | 22 +------- libraries/networking/src/udt/PacketList.h | 18 +------ 20 files changed, 224 insertions(+), 151 deletions(-) create mode 100644 libraries/networking/src/ExtendedIODevice.h diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 4cc24e2110..049e7d0ede 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -25,16 +25,19 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include #include +#include #include #include @@ -49,12 +52,12 @@ #include #include // TODO: consider moving to scriptengine.h +#include "AssignmentDynamicFactory.h" #include "entities/AssignmentParentFinder.h" #include "RecordingScriptingInterface.h" #include "AbstractAudioInterface.h" #include "AgentScriptingInterface.h" - static const int RECEIVED_AUDIO_STREAM_CAPACITY_FRAMES = 10; Agent::Agent(ReceivedMessage& message) : @@ -63,6 +66,18 @@ Agent::Agent(ReceivedMessage& message) : _audioGate(AudioConstants::SAMPLE_RATE, AudioConstants::MONO), _avatarAudioTimer(this) { + DependencyManager::set(); + + DependencyManager::set(); + DependencyManager::set(); + DependencyManager::set(false); + + DependencyManager::registerInheritance(); + DependencyManager::set(); + + DependencyManager::set(); + DependencyManager::set(); + _entityEditSender.setPacketsPerSecond(DEFAULT_ENTITY_PPS_PER_SCRIPT); DependencyManager::get()->setPacketSender(&_entityEditSender); @@ -99,7 +114,6 @@ Agent::Agent(ReceivedMessage& message) : this, "handleOctreePacket"); packetReceiver.registerListener(PacketType::SelectedAudioFormat, this, "handleSelectedAudioFormat"); - // 100Hz timer for audio const int TARGET_INTERVAL_MSEC = 10; // 10ms connect(&_avatarAudioTimer, &QTimer::timeout, this, &Agent::processAgentAvatarAudio); @@ -439,7 +453,7 @@ void Agent::executeScript() { encodedBuffer = audio; } - AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, + AbstractAudioInterface::emitAudioPacket(encodedBuffer.data(), encodedBuffer.size(), audioSequenceNumber, false, audioTransform, scriptedAvatar->getWorldPosition(), glm::vec3(0), packetType, _selectedCodecName); }); @@ -842,6 +856,9 @@ void Agent::aboutToFinish() { DependencyManager::destroy(); DependencyManager::destroy(); DependencyManager::destroy(); + + DependencyManager::destroy(); + QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); // cleanup codec & encoder diff --git a/assignment-client/src/AssignmentClient.cpp b/assignment-client/src/AssignmentClient.cpp index d761699285..426f3ce6fc 100644 --- a/assignment-client/src/AssignmentClient.cpp +++ b/assignment-client/src/AssignmentClient.cpp @@ -21,10 +21,7 @@ #include #include #include -#include #include -#include -#include #include #include #include @@ -32,16 +29,12 @@ #include #include #include -#include -#include -#include + #include #include #include "AssignmentClientLogging.h" -#include "AssignmentDynamicFactory.h" #include "AssignmentFactory.h" -#include "avatars/ScriptableAvatar.h" const QString ASSIGNMENT_CLIENT_TARGET_NAME = "assignment-client"; const long long ASSIGNMENT_REQUEST_INTERVAL_MSECS = 1 * 1000; @@ -57,21 +50,11 @@ AssignmentClient::AssignmentClient(Assignment::Type requestAssignmentType, QStri DependencyManager::set(); DependencyManager::set(); - auto scriptableAvatar = DependencyManager::set(); auto addressManager = DependencyManager::set(); // create a NodeList as an unassigned client, must be after addressManager auto nodeList = DependencyManager::set(NodeType::Unassigned, listenPort); - auto animationCache = DependencyManager::set(); - DependencyManager::set(); - auto entityScriptingInterface = DependencyManager::set(false); - - DependencyManager::registerInheritance(); - auto dynamicFactory = DependencyManager::set(); - DependencyManager::set(); - DependencyManager::set(); - nodeList->startThread(); // set the logging target to the the CHILD_TARGET_NAME LogHandler::getInstance().setTargetName(ASSIGNMENT_CLIENT_TARGET_NAME); diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index b139870e6e..228102ee53 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -39,7 +39,8 @@ const QString AVATAR_MIXER_LOGGING_NAME = "avatar-mixer"; const int AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND = 45; AvatarMixer::AvatarMixer(ReceivedMessage& message) : - ThreadedAssignment(message) + ThreadedAssignment(message), + _slavePool(&_slaveSharedData) { // make sure we hear about node kills so we can tell the other nodes connect(DependencyManager::get().data(), &NodeList::nodeKilled, this, &AvatarMixer::handleAvatarKilled); @@ -338,17 +339,7 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { sendIdentity = true; qCDebug(avatars) << "Giving session display name" << sessionDisplayName << "to node with ID" << node->getUUID(); } - if (nodeData && nodeData->getAvatarSkeletonModelUrlMustChange()) { // never true for an empty _avatarWhitelist - nodeData->setAvatarSkeletonModelUrlMustChange(false); - AvatarData& avatar = nodeData->getAvatar(); - static const QUrl emptyURL(""); - QUrl url = avatar.cannonicalSkeletonModelURL(emptyURL); - if (!isAvatarInWhitelist(url)) { - qCDebug(avatars) << "Forbidden avatar" << nodeData->getNodeID() << avatar.getSkeletonModelURL() << "replaced with" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); - avatar.setSkeletonModelURL(_replacementAvatar); - sendIdentity = true; - } - } + if (sendIdentity && !node->isUpstream()) { sendIdentityPacket(nodeData, node); // Tell node whose name changed about its new session display name or avatar. // since this packet includes a change to either the skeleton model URL or the display name @@ -360,22 +351,6 @@ void AvatarMixer::manageIdentityData(const SharedNodePointer& node) { } } -bool AvatarMixer::isAvatarInWhitelist(const QUrl& url) { - // The avatar is in the whitelist if: - // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND - // 2. The avatar's URL's path starts with the path of that same URL in the whitelist - for (const auto& whiteListedPrefix : _avatarWhitelist) { - auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); - // check if this script URL matches the whitelist domain and, optionally, is beneath the path - if (url.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && - url.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { - return true; - } - } - - return false; -} - void AvatarMixer::throttle(std::chrono::microseconds duration, int frame) { // throttle using a modified proportional-integral controller const float FRAME_TIME = USECS_PER_SECOND / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; @@ -588,8 +563,7 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes // parse the identity packet and update the change timestamp if appropriate bool identityChanged = false; bool displayNameChanged = false; - bool skeletonModelUrlChanged = false; - avatar.processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged, skeletonModelUrlChanged); + avatar.processAvatarIdentity(message->getMessage(), identityChanged, displayNameChanged); if (identityChanged) { QMutexLocker nodeDataLocker(&nodeData->getMutex()); @@ -597,9 +571,6 @@ void AvatarMixer::handleAvatarIdentityPacket(QSharedPointer mes if (displayNameChanged) { nodeData->setAvatarSessionDisplayNameMustChange(true); } - if (skeletonModelUrlChanged && !_avatarWhitelist.isEmpty()) { - nodeData->setAvatarSkeletonModelUrlMustChange(true); - } } } } @@ -992,20 +963,22 @@ void AvatarMixer::parseDomainServerSettings(const QJsonObject& domainSettings) { qCDebug(avatars) << "This domain requires a minimum avatar height of" << _domainMinimumHeight << "and a maximum avatar height of" << _domainMaximumHeight; - const QString AVATAR_WHITELIST_DEFAULT{ "" }; static const QString AVATAR_WHITELIST_OPTION = "avatar_whitelist"; - _avatarWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION].toString(AVATAR_WHITELIST_DEFAULT).split(',', QString::KeepEmptyParts); + _slaveSharedData.skeletonURLWhitelist = domainSettings[AVATARS_SETTINGS_KEY].toObject()[AVATAR_WHITELIST_OPTION] + .toString().split(',', QString::KeepEmptyParts); static const QString REPLACEMENT_AVATAR_OPTION = "replacement_avatar"; - _replacementAvatar = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION].toString(REPLACEMENT_AVATAR_DEFAULT); + _slaveSharedData.skeletonReplacementURL = domainSettings[AVATARS_SETTINGS_KEY].toObject()[REPLACEMENT_AVATAR_OPTION] + .toString(); - if ((_avatarWhitelist.count() == 1) && _avatarWhitelist[0].isEmpty()) { - _avatarWhitelist.clear(); // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + if (_slaveSharedData.skeletonURLWhitelist.count() == 1 && _slaveSharedData.skeletonURLWhitelist[0].isEmpty()) { + // KeepEmptyParts above will parse "," as ["", ""] (which is ok), but "" as [""] (which is not ok). + _slaveSharedData.skeletonURLWhitelist.clear(); } - if (_avatarWhitelist.isEmpty()) { + if (_slaveSharedData.skeletonURLWhitelist.isEmpty()) { qCDebug(avatars) << "All avatars are allowed."; } else { - qCDebug(avatars) << "Avatars other than" << _avatarWhitelist << "will be replaced by" << (_replacementAvatar.isEmpty() ? "default" : _replacementAvatar); + qCDebug(avatars) << "Avatars other than" << _slaveSharedData.skeletonURLWhitelist << "will be replaced by" << (_slaveSharedData.skeletonReplacementURL.isEmpty() ? "default" : _slaveSharedData.skeletonReplacementURL.toString()); } } diff --git a/assignment-client/src/avatars/AvatarMixer.h b/assignment-client/src/avatars/AvatarMixer.h index 14f84c9e7b..8ae7fc9931 100644 --- a/assignment-client/src/avatars/AvatarMixer.h +++ b/assignment-client/src/avatars/AvatarMixer.h @@ -59,7 +59,6 @@ private slots: void handlePacketVersionMismatch(PacketType type, const HifiSockAddr& senderSockAddr, const QUuid& senderUUID); void start(); - private: AvatarMixerClientData* getOrCreateClientData(SharedNodePointer node); std::chrono::microseconds timeFrame(p_high_resolution_clock::time_point& timestamp); @@ -69,11 +68,6 @@ private: void sendIdentityPacket(AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); void manageIdentityData(const SharedNodePointer& node); - bool isAvatarInWhitelist(const QUrl& url); - - const QString REPLACEMENT_AVATAR_DEFAULT{ "" }; - QStringList _avatarWhitelist { }; - QString _replacementAvatar { REPLACEMENT_AVATAR_DEFAULT }; void optionallyReplicatePacket(ReceivedMessage& message, const Node& node); @@ -83,7 +77,6 @@ private: float _trailingMixRatio { 0.0f }; float _throttlingRatio { 0.0f }; - int _sumListeners { 0 }; int _numStatFrames { 0 }; int _numTightLoopFrames { 0 }; @@ -127,6 +120,7 @@ private: RateCounter<> _loopRate; // this is the rate that the main thread tight loop runs AvatarMixerSlavePool _slavePool; + SlaveSharedData _slaveSharedData; }; #endif // hifi_AvatarMixer_h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index f279d76450..552fe9a58b 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -16,6 +16,8 @@ #include #include +#include "AvatarMixerSlave.h" + AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : NodeData(nodeID), _receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size()) @@ -48,7 +50,7 @@ void AvatarMixerClientData::queuePacket(QSharedPointer message, _packetQueue.push(message); } -int AvatarMixerClientData::processPackets() { +int AvatarMixerClientData::processPackets(SlaveSharedData* slaveSharedData) { int packetsProcessed = 0; SharedNodePointer node = _packetQueue.node; assert(_packetQueue.empty() || node); @@ -64,7 +66,7 @@ int AvatarMixerClientData::processPackets() { parseData(*packet); break; case PacketType::SetAvatarTraits: - processSetTraitsMessage(*packet); + processSetTraitsMessage(*packet, slaveSharedData, *node); break; default: Q_UNREACHABLE(); @@ -92,7 +94,7 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead())); } -void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { +void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode) { // pull the trait version from the message AvatarTraits::TraitVersion packetTraitVersion; message.readPrimitive(&packetTraitVersion); @@ -111,6 +113,11 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { _avatar->processTrait(traitType, message.readWithoutCopy(traitSize)); _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + + if (traitType == AvatarTraits::SkeletonModelURL) { + // special handling for skeleton model URL, since we need to make sure it is in the whitelist + checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); + } anyTraitsChanged = true; } else { @@ -123,6 +130,46 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message) { } } +void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *slaveSharedData, Node& sendingNode, + AvatarTraits::TraitVersion traitVersion) { + const auto& whitelist = slaveSharedData->skeletonURLWhitelist; + + if (!whitelist.isEmpty()) { + bool inWhitelist = false; + auto avatarURL = _avatar->getSkeletonModelURL(); + + // The avatar is in the whitelist if: + // 1. The avatar's URL's host matches one of the hosts of the URLs in the whitelist AND + // 2. The avatar's URL's path starts with the path of that same URL in the whitelist + for (const auto& whiteListedPrefix : whitelist) { + auto whiteListURL = QUrl::fromUserInput(whiteListedPrefix); + // check if this script URL matches the whitelist domain and, optionally, is beneath the path + if (avatarURL.host().compare(whiteListURL.host(), Qt::CaseInsensitive) == 0 && + avatarURL.path().startsWith(whiteListURL.path(), Qt::CaseInsensitive)) { + inWhitelist = true; + + break; + } + } + + if (!inWhitelist) { + // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change + _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); + + qDebug() << "Sending overwritten" << _avatar->getSkeletonModelURL() << "back to sending avatar"; + + auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); + + // the returned set traits packet uses the trait version from the incoming packet + // so the client knows they should not overwrite if they have since changed the trait + _avatar->packTrait(AvatarTraits::SkeletonModelURL, *packet, traitVersion); + + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(packet), sendingNode); + } + } +} + uint64_t AvatarMixerClientData::getLastBroadcastTime(const QUuid& nodeUUID) const { // return the matching PacketSequenceNumber, or the default if we don't have it auto nodeMatch = _lastBroadcastTimes.find(nodeUUID); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 8792ecfa5d..96b420afc1 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -34,6 +34,8 @@ const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps"; const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps"; +struct SlaveSharedData; + class AvatarMixerClientData : public NodeData { Q_OBJECT public: @@ -66,8 +68,6 @@ public: void flagIdentityChange() { _identityChangeTimestamp = usecTimestampNow(); } bool getAvatarSessionDisplayNameMustChange() const { return _avatarSessionDisplayNameMustChange; } void setAvatarSessionDisplayNameMustChange(bool set = true) { _avatarSessionDisplayNameMustChange = set; } - bool getAvatarSkeletonModelUrlMustChange() const { return _avatarSkeletonModelUrlMustChange; } - void setAvatarSkeletonModelUrlMustChange(bool set = true) { _avatarSkeletonModelUrlMustChange = set; } void resetNumAvatarsSentLastFrame() { _numAvatarsSentLastFrame = 0; } void incrementNumAvatarsSentLastFrame() { ++_numAvatarsSentLastFrame; } @@ -119,9 +119,11 @@ public: QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } void queuePacket(QSharedPointer message, SharedNodePointer node); - int processPackets(); // returns number of packets processed + int processPackets(SlaveSharedData* slaveSharedData); // returns number of packets processed - void processSetTraitsMessage(ReceivedMessage& message); + void processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode); + void checkSkeletonURLAgainstWhitelist(SlaveSharedData* slaveSharedData, Node& sendingNode, + AvatarTraits::TraitVersion traitVersion); using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index c996008a48..88e394bc95 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -59,7 +59,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { auto nodeData = dynamic_cast(node->getLinkedData()); if (nodeData) { _stats.nodesProcessed++; - _stats.packetsProcessed += nodeData->processPackets(); + _stats.packetsProcessed += nodeData->processPackets(_sharedData); } auto end = usecTimestampNow(); _stats.processIncomingPacketsElapsedTime += (end - start); @@ -108,20 +108,10 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste if (lastReceivedVersion > lastSentVersion) { // there is an update to this trait, add it to the traits packet - // write the trait type and the trait version - traitsPacketList.writePrimitive(traitType); - traitsPacketList.writePrimitive(lastReceivedVersion); - - // update the last sent version since we're adding this to the packet + // update the last sent version listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion); - if (traitType == AvatarTraits::SkeletonModelURL) { - // get an encoded version of the URL, write its size and then the data itself - auto encodedSkeletonURL = sendingAvatar->getSkeletonModelURL().toEncoded(); - - traitsPacketList.writePrimitive(uint16_t(encodedSkeletonURL.size())); - traitsPacketList.write(encodedSkeletonURL); - } + sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); } } diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index ed27709e3e..5112faae29 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -78,11 +78,16 @@ public: jobElapsedTime += rhs.jobElapsedTime; return *this; } +}; +struct SlaveSharedData { + QStringList skeletonURLWhitelist; + QUrl skeletonReplacementURL; }; class AvatarMixerSlave { public: + AvatarMixerSlave(SlaveSharedData* sharedData) : _sharedData(sharedData) {}; using ConstIter = NodeList::const_iterator; void configure(ConstIter begin, ConstIter end); @@ -115,6 +120,7 @@ private: float _throttlingRatio { 0.0f }; AvatarMixerSlaveStats _stats; + SlaveSharedData* _sharedData; }; #endif // hifi_AvatarMixerSlave_h diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp index 962bba21d2..cf842ac792 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.cpp @@ -168,7 +168,7 @@ void AvatarMixerSlavePool::resize(int numThreads) { if (numThreads > _numThreads) { // start new slaves for (int i = 0; i < numThreads - _numThreads; ++i) { - auto slave = new AvatarMixerSlaveThread(*this); + auto slave = new AvatarMixerSlaveThread(*this, _slaveSharedData); slave->start(); _slaves.emplace_back(slave); } diff --git a/assignment-client/src/avatars/AvatarMixerSlavePool.h b/assignment-client/src/avatars/AvatarMixerSlavePool.h index 15bd681b2c..71a9ace0d3 100644 --- a/assignment-client/src/avatars/AvatarMixerSlavePool.h +++ b/assignment-client/src/avatars/AvatarMixerSlavePool.h @@ -32,7 +32,8 @@ class AvatarMixerSlaveThread : public QThread, public AvatarMixerSlave { using Lock = std::unique_lock; public: - AvatarMixerSlaveThread(AvatarMixerSlavePool& pool) : _pool(pool) {} + AvatarMixerSlaveThread(AvatarMixerSlavePool& pool, SlaveSharedData* slaveSharedData) : + AvatarMixerSlave(slaveSharedData), _pool(pool) {}; void run() override final; @@ -59,7 +60,8 @@ class AvatarMixerSlavePool { public: using ConstIter = NodeList::const_iterator; - AvatarMixerSlavePool(int numThreads = QThread::idealThreadCount()) { setNumThreads(numThreads); } + AvatarMixerSlavePool(SlaveSharedData* slaveSharedData, int numThreads = QThread::idealThreadCount()) : + _slaveSharedData(slaveSharedData) { setNumThreads(numThreads); } ~AvatarMixerSlavePool() { resize(0); } // Jobs the slave pool can do... @@ -98,6 +100,8 @@ private: Queue _queue; ConstIter _begin; ConstIter _end; + + SlaveSharedData* _slaveSharedData; }; #endif // hifi_AvatarMixerSlavePool_h diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 396c6cbcac..e556fd734f 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -6393,8 +6393,8 @@ void Application::nodeActivated(SharedNodePointer node) { if (_avatarOverrideUrl.isValid()) { getMyAvatar()->useFullAvatarURL(_avatarOverrideUrl); } - static const QUrl empty{}; - if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->cannonicalSkeletonModelURL(empty)) { + + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->getSkeletonModelURL()) { getMyAvatar()->resetFullAvatarURL(); } getMyAvatar()->markIdentityDataChanged(); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index ddfeb4df24..c9d8f7bb1e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1750,12 +1750,6 @@ glm::quat AvatarData::getOrientationOutbound() const { return (getLocalOrientation()); } -static const QUrl emptyURL(""); -QUrl AvatarData::cannonicalSkeletonModelURL(const QUrl& emptyURL) const { - // We don't put file urls on the wire, but instead convert to empty. - return _skeletonModelURL.scheme() == "file" ? emptyURL : _skeletonModelURL; -} - void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged) { @@ -1836,6 +1830,27 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide } } +void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion) { + destination.writePrimitive(traitType); + + if (traitVersion > 0) { + AvatarTraits::TraitVersion typedVersion = traitVersion; + destination.writePrimitive(typedVersion); + } + + if (traitType == AvatarTraits::SkeletonModelURL) { + QByteArray encodedSkeletonURL; + if (_skeletonModelURL.scheme() != "file" && _skeletonModelURL.scheme() != "qrc") { + encodedSkeletonURL = _skeletonModelURL.toEncoded(); + } + + AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); + destination.writePrimitive(encodedURLSize); + + destination.write(encodedSkeletonURL); + } +} + void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { if (traitType == AvatarTraits::SkeletonModelURL) { // get the URL from the binary data diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 12e8370b86..619f8e1722 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -426,7 +426,6 @@ public: virtual ~AvatarData(); static const QUrl& defaultFullAvatarModelUrl(); - QUrl cannonicalSkeletonModelURL(const QUrl& empty) const; virtual bool isMyAvatar() const { return false; } @@ -958,6 +957,7 @@ public: // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); + void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion = -1); void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); QByteArray identityByteArray(bool setIsReplicated = false) const; diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 90258982c3..121e1057c6 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -29,6 +29,9 @@ namespace AvatarTraits { using TraitVersion = uint32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; + using NullableTraitVersion = int64_t; + const NullableTraitVersion NULL_TRAIT_VERSION = -1; + using TraitWireSize = uint16_t; using SimpleTraitVersions = std::vector; diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 5d1f2e4926..2518dedf37 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -27,6 +27,8 @@ ClientTraitsHandler::ClientTraitsHandler(AvatarData* owningAvatar) : resetForNewMixer(); } }); + + nodeList->getPacketReceiver().registerListener(PacketType::SetAvatarTraits, this, "processTraitOverride"); } void ClientTraitsHandler::resetForNewMixer() { @@ -63,19 +65,11 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { if (_performInitialSend || changedTraitsCopy.count(AvatarTraits::SkeletonModelURL)) { traitsPacketList->startSegment(); - - traitsPacketList->writePrimitive(AvatarTraits::SkeletonModelURL); - - auto encodedSkeletonURL = _owningAvatar->getSkeletonModelURL().toEncoded(); - - AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); - traitsPacketList->writePrimitive(encodedURLSize); - - qDebug() << "Sending trait of size" << encodedURLSize; - - traitsPacketList->write(encodedSkeletonURL); - + _owningAvatar->packTrait(AvatarTraits::SkeletonModelURL, *traitsPacketList); traitsPacketList->endSegment(); + + // keep track of our skeleton version in case we get an override back + _currentSkeletonVersion = _currentTraitVersion; } nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); @@ -84,3 +78,33 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { _performInitialSend = false; } } + +void ClientTraitsHandler::processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode) { + if (sendingNode->getType() == NodeType::AvatarMixer) { + while (message->getBytesLeftToRead()) { + AvatarTraits::TraitType traitType; + message->readPrimitive(&traitType); + + AvatarTraits::TraitVersion traitVersion; + message->readPrimitive(&traitVersion); + + AvatarTraits::TraitWireSize traitBinarySize; + message->readPrimitive(&traitBinarySize); + + // only accept an override if this is for a trait type we override + // and the version matches what we last sent for skeleton + if (traitType == AvatarTraits::SkeletonModelURL + && traitVersion == _currentSkeletonVersion + && !hasTraitChanged(AvatarTraits::SkeletonModelURL)) { + // override the skeleton URL but do not mark the trait as having changed + // so that we don't unecessarily sent a new trait packet to the mixer with the overriden URL + auto encodedSkeletonURL = QUrl::fromEncoded(message->readWithoutCopy(traitBinarySize)); + _owningAvatar->setSkeletonModelURL(encodedSkeletonURL); + + _changedTraits.erase(AvatarTraits::SkeletonModelURL); + } else { + message->seek(message->getPosition() + traitBinarySize); + } + } + } +} diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 4aea0bb433..3a2b70776c 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -12,13 +12,15 @@ #ifndef hifi_ClientTraitsHandler_h #define hifi_ClientTraitsHandler_h -#include +#include #include "AvatarTraits.h" +#include "Node.h" class AvatarData; -class ClientTraitsHandler { +class ClientTraitsHandler : public QObject { + Q_OBJECT public: ClientTraitsHandler(AvatarData* owningAvatar); @@ -31,11 +33,17 @@ public: void resetForNewMixer(); +public slots: + void processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode); + private: AvatarData* _owningAvatar; AvatarTraits::TraitTypeSet _changedTraits; AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; + + AvatarTraits::NullableTraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; + bool _performInitialSend { false }; }; diff --git a/libraries/networking/src/ExtendedIODevice.h b/libraries/networking/src/ExtendedIODevice.h new file mode 100644 index 0000000000..7df1af74b6 --- /dev/null +++ b/libraries/networking/src/ExtendedIODevice.h @@ -0,0 +1,39 @@ +// +// ExtendedIODevice.h +// libraries/networking/src +// +// Created by Stephen Birarda on 8/7/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_ExtendedIODevice_h +#define hifi_ExtendedIODevice_h + +#include + +class ExtendedIODevice : public QIODevice { +public: + ExtendedIODevice(QObject* parent = nullptr) : QIODevice(parent) {}; + + template qint64 peekPrimitive(T* data); + template qint64 readPrimitive(T* data); + template qint64 writePrimitive(const T& data); +}; + +template qint64 ExtendedIODevice::peekPrimitive(T* data) { + return peek(reinterpret_cast(data), sizeof(T)); +} + +template qint64 ExtendedIODevice::readPrimitive(T* data) { + return read(reinterpret_cast(data), sizeof(T)); +} + +template qint64 ExtendedIODevice::writePrimitive(const T& data) { + static_assert(!std::is_pointer::value, "T must not be a pointer"); + return write(reinterpret_cast(&data), sizeof(T)); +} + +#endif // hifi_ExtendedIODevice_h diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 0540e60a0e..92ccdd6117 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -78,7 +78,7 @@ BasePacket::BasePacket(std::unique_ptr data, qint64 size, const HifiSock } BasePacket::BasePacket(const BasePacket& other) : - QIODevice() + ExtendedIODevice() { *this = other; } diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index d9b624b595..9c3244e08b 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -16,16 +16,15 @@ #include -#include - #include #include "../HifiSockAddr.h" #include "Constants.h" +#include "../ExtendedIODevice.h" namespace udt { -class BasePacket : public QIODevice { +class BasePacket : public ExtendedIODevice { Q_OBJECT public: static const qint64 PACKET_WRITE_ERROR; @@ -85,10 +84,6 @@ public: void setReceiveTime(p_high_resolution_clock::time_point receiveTime) { _receiveTime = receiveTime; } p_high_resolution_clock::time_point getReceiveTime() const { return _receiveTime; } - - template qint64 peekPrimitive(T* data); - template qint64 readPrimitive(T* data); - template qint64 writePrimitive(const T& data); protected: BasePacket(qint64 size); @@ -116,19 +111,6 @@ protected: p_high_resolution_clock::time_point _receiveTime; // captures the time the packet received (only used on receiving end) }; - -template qint64 BasePacket::peekPrimitive(T* data) { - return peek(reinterpret_cast(data), sizeof(T)); -} - -template qint64 BasePacket::readPrimitive(T* data) { - return read(reinterpret_cast(data), sizeof(T)); -} - -template qint64 BasePacket::writePrimitive(const T& data) { - static_assert(!std::is_pointer::value, "T must not be a pointer"); - return write(reinterpret_cast(&data), sizeof(T)); -} } // namespace udt diff --git a/libraries/networking/src/udt/PacketList.h b/libraries/networking/src/udt/PacketList.h index ff1860c3d1..b9bd6a8c15 100644 --- a/libraries/networking/src/udt/PacketList.h +++ b/libraries/networking/src/udt/PacketList.h @@ -14,8 +14,7 @@ #include -#include - +#include "../ExtendedIODevice.h" #include "Packet.h" #include "PacketHeaders.h" @@ -25,7 +24,7 @@ namespace udt { class Packet; -class PacketList : public QIODevice { +class PacketList : public ExtendedIODevice { Q_OBJECT public: using MessageNumber = uint32_t; @@ -59,9 +58,6 @@ public: virtual bool isSequential() const override { return false; } virtual qint64 size() const override { return getDataSize(); } - template qint64 readPrimitive(T* data); - template qint64 writePrimitive(const T& data); - qint64 writeString(const QString& string); protected: @@ -105,16 +101,6 @@ private: QByteArray _extendedHeader; }; -template qint64 PacketList::readPrimitive(T* data) { - static_assert(!std::is_pointer::value, "T must not be a pointer"); - return read(reinterpret_cast(data), sizeof(T)); -} - -template qint64 PacketList::writePrimitive(const T& data) { - static_assert(!std::is_pointer::value, "T must not be a pointer"); - return write(reinterpret_cast(&data), sizeof(T)); -} - template std::unique_ptr PacketList::takeFront() { static_assert(std::is_base_of::value, "T must derive from Packet."); From a0df68f32fda747afbc9d68c52524a0d4d810f9c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Aug 2018 13:53:35 -0700 Subject: [PATCH 33/64] move skeleton model URL emit to AvatarData --- assignment-client/src/avatars/ScriptableAvatar.cpp | 2 +- assignment-client/src/avatars/ScriptableAvatar.h | 2 +- interface/src/avatar/MyAvatar.cpp | 4 +--- libraries/avatars/src/AvatarData.cpp | 4 +++- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 4e47cf96f1..16a49a8999 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -1,6 +1,6 @@ // // ScriptableAvatar.cpp -// +// assignment-client/src/avatars // // Created by Clement on 7/22/14. // Copyright 2014 High Fidelity, Inc. diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 8e3d779dda..201bfe67e8 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -1,6 +1,6 @@ // // ScriptableAvatar.h -// +// assignment-client/src/avatars // // Created by Clement on 7/22/14. // Copyright 2014 High Fidelity, Inc. diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index df9afeb92d..90d1ad257b 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1699,9 +1699,9 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { } QObject::disconnect(*skeletonConnection); }); + saveAvatarUrl(); emit skeletonChanged(); - emit skeletonModelURLChanged(); if (previousSkeletonModelURL != _skeletonModelURL) { _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); @@ -1776,8 +1776,6 @@ void MyAvatar::useFullAvatarURL(const QUrl& fullAvatarURL, const QString& modelN setSkeletonModelURL(fullAvatarURL); UserActivityLogger::getInstance().changedModel("skeleton", urlString); } - - markIdentityDataChanged(); } glm::vec3 MyAvatar::getSkeletonPosition() const { diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c9d8f7bb1e..c9d8049cd3 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1891,11 +1891,13 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { if (expanded == _skeletonModelURL) { return; } + _skeletonModelURL = expanded; qCDebug(avatars) << "Changing skeleton model for avatar" << getSessionUUID() << "to" << _skeletonModelURL.toString(); updateJointMappings(); - markIdentityDataChanged(); + + emit skeletonModelURLChanged(); } void AvatarData::setDisplayName(const QString& displayName) { From ea7c0e923a9958d2fa21ba440d15bb48cd20075b Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Wed, 8 Aug 2018 13:59:21 -0700 Subject: [PATCH 34/64] make client traits handler a unique ptr in AvatarData --- .../src/avatars/ScriptableAvatar.cpp | 10 ++++--- .../src/avatars/ScriptableAvatar.h | 5 ++-- interface/src/avatar/MyAvatar.cpp | 10 +++---- interface/src/avatar/MyAvatar.h | 4 --- libraries/avatars/src/AvatarData.cpp | 5 ++++ libraries/avatars/src/AvatarData.h | 6 +++++ libraries/avatars/src/AvatarTraits.h | 27 ++++++++++++++++--- libraries/avatars/src/ClientTraitsHandler.cpp | 2 +- libraries/avatars/src/ClientTraitsHandler.h | 4 +-- 9 files changed, 49 insertions(+), 24 deletions(-) diff --git a/assignment-client/src/avatars/ScriptableAvatar.cpp b/assignment-client/src/avatars/ScriptableAvatar.cpp index 16a49a8999..6f04cfa196 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.cpp +++ b/assignment-client/src/avatars/ScriptableAvatar.cpp @@ -16,9 +16,13 @@ #include #include -#include #include +#include +#include +ScriptableAvatar::ScriptableAvatar() { + _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); +} QByteArray ScriptableAvatar::toByteArrayStateful(AvatarDataDetail dataDetail, bool dropFaceTracking) { _globalPosition = getWorldPosition(); @@ -63,8 +67,6 @@ void ScriptableAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { _animSkeleton.reset(); AvatarData::setSkeletonModelURL(skeletonModelURL); - - _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); } static AnimPose composeAnimPose(const FBXJoint& fbxJoint, const glm::quat rotation, const glm::vec3 translation) { @@ -141,5 +143,5 @@ void ScriptableAvatar::update(float deltatime) { } } - _clientTraitsHandler.sendChangedTraitsToMixer(); + _clientTraitsHandler->sendChangedTraitsToMixer(); } diff --git a/assignment-client/src/avatars/ScriptableAvatar.h b/assignment-client/src/avatars/ScriptableAvatar.h index 201bfe67e8..89f9369133 100644 --- a/assignment-client/src/avatars/ScriptableAvatar.h +++ b/assignment-client/src/avatars/ScriptableAvatar.h @@ -15,7 +15,6 @@ #include #include #include -#include #include /**jsdoc @@ -125,6 +124,8 @@ class ScriptableAvatar : public AvatarData, public Dependency { Q_OBJECT public: + ScriptableAvatar(); + /**jsdoc * @function Avatar.startAnimation * @param {string} url @@ -165,8 +166,6 @@ private: QStringList _maskedJoints; AnimationPointer _bind; // a sleazy way to get the skeleton, given the various library/cmake dependencies std::shared_ptr _animSkeleton; - - ClientTraitsHandler _clientTraitsHandler { this }; }; #endif // hifi_ScriptableAvatar_h diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 90d1ad257b..2c88f917a1 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -119,9 +119,9 @@ MyAvatar::MyAvatar(QThread* thread) : _goToOrientation(), _prevShouldDrawHead(true), _audioListenerMode(FROM_HEAD), - _hmdAtRestDetector(glm::vec3(0), glm::quat()), - _clientTraitsHandler(this) + _hmdAtRestDetector(glm::vec3(0), glm::quat()) { + _clientTraitsHandler = std::unique_ptr(new ClientTraitsHandler(this)); // give the pointer to our head to inherited _headData variable from AvatarData _headData = new MyHead(this); @@ -514,7 +514,7 @@ void MyAvatar::update(float deltaTime) { sendIdentityPacket(); } - _clientTraitsHandler.sendChangedTraitsToMixer(); + _clientTraitsHandler->sendChangedTraitsToMixer(); simulate(deltaTime); @@ -1702,10 +1702,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { saveAvatarUrl(); emit skeletonChanged(); - - if (previousSkeletonModelURL != _skeletonModelURL) { - _clientTraitsHandler.markTraitChanged(AvatarTraits::SkeletonModelURL); - } } void MyAvatar::removeAvatarEntities(const std::function& condition) { diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 8fdc4ba4e3..ba6348cc22 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -1334,7 +1334,6 @@ public slots: */ void setAnimGraphUrl(const QUrl& url); // thread-safe - /**jsdoc * @function MyAvatar.getPositionForAudio * @returns {Vec3} @@ -1347,7 +1346,6 @@ public slots: */ glm::quat getOrientationForAudio(); - /**jsdoc * @function MyAvatar.setModelScale * @param {number} scale @@ -1776,8 +1774,6 @@ private: bool _haveReceivedHeightLimitsFromDomain { false }; int _disableHandTouchCount { 0 }; - - ClientTraitsHandler _clientTraitsHandler; }; QScriptValue audioListenModeToScriptValue(QScriptEngine* engine, const AudioListenerMode& audioListenerMode); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c9d8049cd3..f32da39bba 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -43,6 +43,7 @@ #include "AvatarLogging.h" #include "AvatarTraits.h" +#include "ClientTraitsHandler.h" //#define WANT_DEBUG @@ -1897,6 +1898,10 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); + if (_clientTraitsHandler) { + _clientTraitsHandler->markTraitChanged(AvatarTraits::SkeletonModelURL); + } + emit skeletonModelURLChanged(); } diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 619f8e1722..a5d2d0749b 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -372,6 +372,8 @@ public: bool operator<(const AvatarPriority& other) const { return priority < other.priority; } }; +class ClientTraitsHandler; + class AvatarData : public QObject, public SpatiallyNestable { Q_OBJECT @@ -924,6 +926,7 @@ public: * @param {string} entityData */ Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + /**jsdoc * @function MyAvatar.clearAvatarEntity * @param {Uuid} entityID @@ -1435,6 +1438,9 @@ protected: bool _hasProcessedFirstIdentity { false }; float _density; + // null unless MyAvatar or ScriptableAvatar sending traits data to mixer + std::unique_ptr _clientTraitsHandler; + template T readLockWithNamedJointIndex(const QString& name, const T& defaultValue, F f) const { int index = getFauxJointIndex(name); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 121e1057c6..d30da0e1af 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -12,8 +12,8 @@ #ifndef hifi_AvatarTraits_h #define hifi_AvatarTraits_h +#include #include -#include #include namespace AvatarTraits { @@ -23,7 +23,28 @@ namespace AvatarTraits { TotalTraitTypes }; - using TraitTypeSet = std::set; + class TraitTypeSet { + public: + TraitTypeSet() {}; + + TraitTypeSet(std::initializer_list types) { + for (auto type : types) { + _types[type] = true; + } + }; + + bool contains(TraitType type) const { return _types[type]; } + + bool hasAny() const { return std::find(_types.begin(), _types.end(), true) != _types.end(); } + int size() const { return std::count(_types.begin(), _types.end(), true); } + + void insert(TraitType type) { _types[type] = true; } + void erase(TraitType type) { _types[type] = false; } + void clear() { std::fill(_types.begin(), _types.end(), false); } + private: + std::vector _types = { AvatarTraits::TotalTraitTypes, false }; + }; + const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL }; using TraitVersion = uint32_t; @@ -35,6 +56,6 @@ namespace AvatarTraits { using TraitWireSize = uint16_t; using SimpleTraitVersions = std::vector; -} +}; #endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 2518dedf37..8b3ded1e1c 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -63,7 +63,7 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto changedTraitsCopy { _changedTraits }; _changedTraits.clear(); - if (_performInitialSend || changedTraitsCopy.count(AvatarTraits::SkeletonModelURL)) { + if (_performInitialSend || changedTraitsCopy.contains(AvatarTraits::SkeletonModelURL)) { traitsPacketList->startSegment(); _owningAvatar->packTrait(AvatarTraits::SkeletonModelURL, *traitsPacketList); traitsPacketList->endSegment(); diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 3a2b70776c..1d4c67d0c4 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -26,10 +26,10 @@ public: void sendChangedTraitsToMixer(); - bool hasChangedTraits() { return _changedTraits.size(); } + bool hasChangedTraits() { return _changedTraits.hasAny(); } void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); } - bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.count(checkTrait) > 0; } + bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.contains(checkTrait) > 0; } void resetForNewMixer(); From e6b419d283f49b13e75e5348121b0a638179dcec Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 19:01:09 -0700 Subject: [PATCH 35/64] add instanced traits and migrate avatar entities --- .../src/avatars/AvatarMixerClientData.cpp | 77 +++++---- .../src/avatars/AvatarMixerClientData.h | 15 +- .../src/avatars/AvatarMixerSlave.cpp | 74 +++++++- libraries/avatars/src/AssociatedTraitValues.h | 158 ++++++++++++++++++ libraries/avatars/src/AvatarData.cpp | 124 ++++++++++---- libraries/avatars/src/AvatarData.h | 16 +- libraries/avatars/src/AvatarHashMap.cpp | 75 +++++---- libraries/avatars/src/AvatarHashMap.h | 9 +- libraries/avatars/src/AvatarTraits.h | 51 +++--- libraries/avatars/src/ClientTraitsHandler.cpp | 60 +++++-- libraries/avatars/src/ClientTraitsHandler.h | 25 ++- .../networking/src/udt/PacketHeaders.cpp | 2 +- libraries/networking/src/udt/PacketHeaders.h | 5 +- 13 files changed, 502 insertions(+), 189 deletions(-) create mode 100644 libraries/avatars/src/AssociatedTraitValues.h diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 552fe9a58b..34b7ec97ff 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -19,8 +19,7 @@ #include "AvatarMixerSlave.h" AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID), - _receivedSimpleTraitVersions(AvatarTraits::SimpleTraitTypes.size()) + NodeData(nodeID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); @@ -107,21 +106,47 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, Sl AvatarTraits::TraitType traitType; message.readPrimitive(&traitType); - AvatarTraits::TraitWireSize traitSize; - message.readPrimitive(&traitSize); + if (AvatarTraits::isSimpleTrait(traitType)) { + AvatarTraits::TraitWireSize traitSize; + message.readPrimitive(&traitSize); - if (packetTraitVersion > _receivedSimpleTraitVersions[traitType]) { - _avatar->processTrait(traitType, message.readWithoutCopy(traitSize)); - _receivedSimpleTraitVersions[traitType] = packetTraitVersion; + if (packetTraitVersion > _lastReceivedTraitVersions[traitType]) { + _avatar->processTrait(traitType, message.read(traitSize)); + _lastReceivedTraitVersions[traitType] = packetTraitVersion; - if (traitType == AvatarTraits::SkeletonModelURL) { - // special handling for skeleton model URL, since we need to make sure it is in the whitelist - checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); + if (traitType == AvatarTraits::SkeletonModelURL) { + // special handling for skeleton model URL, since we need to make sure it is in the whitelist + checkSkeletonURLAgainstWhitelist(slaveSharedData, sendingNode, packetTraitVersion); + } + + anyTraitsChanged = true; + } else { + message.seek(message.getPosition() + traitSize); } - - anyTraitsChanged = true; } else { - message.seek(message.getPosition() + traitSize); + AvatarTraits::TraitInstanceID instanceID = QUuid::fromRfc4122(message.readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + AvatarTraits::TraitWireSize traitSize; + message.readPrimitive(&traitSize); + + auto& instanceVersionRef = _lastReceivedTraitVersions.getInstanceValueRef(traitType, instanceID); + + if (packetTraitVersion > instanceVersionRef) { + if (traitSize == AvatarTraits::DELETED_TRAIT_SIZE) { + _avatar->processDeletedTraitInstance(traitType, instanceID); + + // to track a deleted instance but keep version information + // the avatar mixer uses the negative value of the sent version + instanceVersionRef = -packetTraitVersion; + } else { + _avatar->processTraitInstance(traitType, instanceID, message.read(traitSize)); + instanceVersionRef = packetTraitVersion; + } + + anyTraitsChanged = true; + } else { + message.seek(message.getPosition() + traitSize); + } } } @@ -257,29 +282,3 @@ AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherA return TraitsCheckTimestamp(); } } - -AvatarTraits::TraitVersion AvatarMixerClientData::getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, - AvatarTraits::TraitType traitType) const { - auto it = _sentSimpleTraitVersions.find(otherAvatar); - - if (it != _sentSimpleTraitVersions.end()) { - return it->second[traitType]; - } - - return AvatarTraits::DEFAULT_TRAIT_VERSION; -} - -void AvatarMixerClientData::setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion traitVersion) { - - auto it = _sentSimpleTraitVersions.find(otherAvatar); - - if (it == _sentSimpleTraitVersions.end()) { - auto pair = _sentSimpleTraitVersions.insert({ - otherAvatar, { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } - }); - - it = pair.first; - } - - it->second[traitType] = traitVersion; -} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 96b420afc1..dcbf8a6dba 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -22,7 +22,7 @@ #include #include -#include +#include #include #include #include @@ -128,16 +128,15 @@ public: using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; TraitsCheckTimestamp getLastReceivedTraitsChange() const { return _lastReceivedTraitsChange; } - AvatarTraits::TraitVersion getLastReceivedSimpleTraitVersion(AvatarTraits::TraitType traitType) const - { return _receivedSimpleTraitVersions[traitType]; } + + AvatarTraits::TraitVersions& getLastReceivedTraitVersions() { return _lastReceivedTraitVersions; } + const AvatarTraits::TraitVersions& getLastReceivedTraitVersions() const { return _lastReceivedTraitVersions; } TraitsCheckTimestamp getLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar) const; void setLastOtherAvatarTraitsSendPoint(Node::LocalID otherAvatar, TraitsCheckTimestamp sendPoint) { _lastSentTraitsTimestamps[otherAvatar] = sendPoint; } - AvatarTraits::TraitVersion getLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType) const; - void setLastSentSimpleTraitVersion(Node::LocalID otherAvatar, AvatarTraits::TraitType traitType, - AvatarTraits::TraitVersion traitVersion); + AvatarTraits::TraitVersions& getLastSentTraitVersions(Node::LocalID otherAvatar) { return _sentTraitVersions[otherAvatar]; } private: struct PacketQueue : public std::queue> { @@ -176,11 +175,11 @@ private: QString _baseDisplayName{}; // The santized key used in determinging unique sessionDisplayName, so that we can remove from dictionary. bool _requestsDomainListData { false }; - AvatarTraits::SimpleTraitVersions _receivedSimpleTraitVersions; + AvatarTraits::TraitVersions _lastReceivedTraitVersions; TraitsCheckTimestamp _lastReceivedTraitsChange; std::unordered_map _lastSentTraitsTimestamps; - std::unordered_map _sentSimpleTraitVersions; + std::unordered_map _sentTraitVersions; }; #endif // hifi_AvatarMixerClientData_h diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 88e394bc95..ebbaeb7a35 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -99,20 +99,76 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste auto sendingAvatar = sendingNodeData->getAvatarSharedPointer(); // compare trait versions so we can see what exactly needs to go out - for (int i = 0; i < AvatarTraits::TotalTraitTypes; ++i) { - AvatarTraits::TraitType traitType = static_cast(i); + auto& lastSentVersions = listeningNodeData->getLastSentTraitVersions(otherNodeLocalID); + const auto& lastReceivedVersions = sendingNodeData->getLastReceivedTraitVersions(); - auto lastSentVersion = listeningNodeData->getLastSentSimpleTraitVersion(otherNodeLocalID, traitType); - auto lastReceivedVersion = sendingNodeData->getLastReceivedSimpleTraitVersion(traitType); + auto simpleReceivedIt = lastReceivedVersions.simpleCBegin(); + while (simpleReceivedIt != lastReceivedVersions.simpleCEnd()) { + auto traitType = static_cast(std::distance(lastReceivedVersions.simpleCBegin(), + simpleReceivedIt)); - if (lastReceivedVersion > lastSentVersion) { - // there is an update to this trait, add it to the traits packet + // we need to double check that this is actually a simple trait type, since the instanced + // trait types are in the simple vector for access efficiency + if (AvatarTraits::isSimpleTrait(traitType)) { + auto lastReceivedVersion = *simpleReceivedIt; + auto& lastSentVersionRef = lastSentVersions[traitType]; - // update the last sent version - listeningNodeData->setLastSentSimpleTraitVersion(otherNodeLocalID, traitType, lastReceivedVersion); + if (lastReceivedVersions[traitType] > lastSentVersionRef) { + // there is an update to this trait, add it to the traits packet + sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); - sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); + // update the last sent version + lastSentVersionRef = lastReceivedVersion; + } } + + ++simpleReceivedIt; + } + + // enumerate the received instanced trait versions + auto instancedReceivedIt = lastReceivedVersions.instancedCBegin(); + while (instancedReceivedIt != lastReceivedVersions.instancedCEnd()) { + auto traitType = instancedReceivedIt->traitType; + + // get or create the sent trait versions for this trait type + auto& sentIDValuePairs = lastSentVersions.getInstanceIDValuePairs(traitType); + + // enumerate each received instance + for (auto& receivedInstance : instancedReceivedIt->instances) { + auto instanceID = receivedInstance.id; + const auto receivedVersion = receivedInstance.value; + + // to track deletes and maintain version information for traits + // the mixer stores the negative value of the received version when a trait instance is deleted + bool isDeleted = receivedVersion < 0; + const auto absoluteReceivedVersion = std::abs(receivedVersion); + + // look for existing sent version for this instance + auto sentInstanceIt = std::find_if(sentIDValuePairs.begin(), sentIDValuePairs.end(), + [instanceID](auto& sentInstance) + { + return sentInstance.id == instanceID; + }); + + if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) { + // this instance version exists and has never been sent or is newer so we need to send it + sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion); + + if (sentInstanceIt != sentIDValuePairs.end()) { + sentInstanceIt->value = receivedVersion; + } else { + sentIDValuePairs.emplace_back(instanceID, receivedVersion); + } + } else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) { + // this instance version was deleted and we haven't sent the delete to this client yet + AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion); + + // update the last sent version for this trait instance to the absolute value of the deleted version + sentInstanceIt->value = absoluteReceivedVersion; + } + } + + ++instancedReceivedIt; } // write a null trait type to mark the end of trait data for this avatar diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h new file mode 100644 index 0000000000..b2c0197e5c --- /dev/null +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -0,0 +1,158 @@ +// +// AssociatedTraitValues.h +// libraries/avatars/src +// +// Created by Stephen Birarda on 8/8/18. +// Copyright 2018 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +#ifndef hifi_AssociatedTraitValues_h +#define hifi_AssociatedTraitValues_h + +#include "AvatarTraits.h" + +namespace AvatarTraits { + template + class AssociatedTraitValues { + public: + AssociatedTraitValues() : _simpleTypes(TotalTraitTypes, defaultValue) {} + + void insert(TraitType type, T value) { _simpleTypes[type] = value; } + void erase(TraitType type) { _simpleTypes[type] = defaultValue; } + + T& getInstanceValueRef(TraitType traitType, TraitInstanceID instanceID); + void instanceInsert(TraitType traitType, TraitInstanceID instanceID, T value); + + struct InstanceIDValuePair { + TraitInstanceID id; + T value; + + InstanceIDValuePair(TraitInstanceID id, T value) : id(id), value(value) {}; + }; + + using InstanceIDValuePairs = std::vector; + + InstanceIDValuePairs& getInstanceIDValuePairs(TraitType traitType); + + void instanceErase(TraitType traitType, TraitInstanceID instanceID); + void eraseAllInstances(TraitType traitType); + + // will return defaultValue for instanced traits + T operator[](TraitType traitType) const { return _simpleTypes[traitType]; } + T& operator[](TraitType traitType) { return _simpleTypes[traitType]; } + + void reset() { + std::fill(_simpleTypes.begin(), _simpleTypes.end(), defaultValue); + _instancedTypes.clear(); + } + + typename std::vector::const_iterator simpleCBegin() const { return _simpleTypes.cbegin(); } + typename std::vector::const_iterator simpleCEnd() const { return _simpleTypes.cend(); } + + typename std::vector::iterator simpleBegin() { return _simpleTypes.begin(); } + typename std::vector::iterator simpleEnd() { return _simpleTypes.end(); } + + struct TraitWithInstances { + TraitType traitType; + InstanceIDValuePairs instances; + + TraitWithInstances(TraitType traitType) : traitType(traitType) {}; + TraitWithInstances(TraitType traitType, TraitInstanceID instanceID, T value) : + traitType(traitType), instances({{ instanceID, value }}) {}; + }; + + typename std::vector::const_iterator instancedCBegin() const { return _instancedTypes.cbegin(); } + typename std::vector::const_iterator instancedCEnd() const { return _instancedTypes.cend(); } + + typename std::vector::iterator instancedBegin() { return _instancedTypes.begin(); } + typename std::vector::iterator instancedEnd() { return _instancedTypes.end(); } + + private: + std::vector _simpleTypes; + + typename std::vector::iterator instancesForTrait(TraitType traitType) { + return std::find_if(_instancedTypes.begin(), _instancedTypes.end(), + [traitType](TraitWithInstances& traitWithInstances){ + return traitWithInstances.traitType == traitType; + }); + } + + std::vector _instancedTypes; + }; + + template + inline typename AssociatedTraitValues::InstanceIDValuePairs& + AssociatedTraitValues::getInstanceIDValuePairs(TraitType traitType) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + return it->instances; + } else { + _instancedTypes.emplace_back(traitType); + return _instancedTypes.back().instances; + } + } + + template + inline T& AssociatedTraitValues::getInstanceValueRef(TraitType traitType, TraitInstanceID instanceID) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + auto& instancesVector = it->instances; + auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(), + [instanceID](InstanceIDValuePair& idValuePair){ + return idValuePair.id == instanceID; + }); + if (instanceIt != instancesVector.end()) { + return instanceIt->value; + } else { + instancesVector.emplace_back(instanceID, defaultValue); + return instancesVector.back().value; + } + } else { + _instancedTypes.emplace_back(traitType, instanceID, defaultValue); + return _instancedTypes.back().instances.back().value; + } + } + + template + inline void AssociatedTraitValues::instanceInsert(TraitType traitType, TraitInstanceID instanceID, T value) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + auto instancesVector = it->instances; + auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(), + [instanceID](InstanceIDValuePair& idValuePair){ + return idValuePair.id == instanceID; + }); + if (instanceIt != instancesVector.end()) { + instanceIt->value = value; + } else { + instancesVector.emplace_back(instanceID, value); + } + } else { + _instancedTypes.emplace_back(traitType, instanceID, value); + } + } + + template + inline void AssociatedTraitValues::instanceErase(TraitType traitType, TraitInstanceID instanceID) { + auto it = instancesForTrait(traitType); + + if (it != _instancedTypes.end()) { + auto instancesVector = it->instances; + instancesVector.erase(std::remove_if(instancesVector.begin(), + instancesVector.end(), + [&instanceID](InstanceIDValuePair& idValuePair){ + return idValuePair.id == instanceID; + })); + } + } + + using TraitVersions = AssociatedTraitValues; +}; + +#endif // hifi_AssociatedTraitValues_h diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f32da39bba..36dbe00937 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1778,7 +1778,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide >> identity.displayName >> identity.sessionDisplayName >> identity.isReplicated - >> identity.avatarEntityData >> identity.lookAtSnappingEnabled ; @@ -1802,16 +1801,6 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide identityChanged = true; } - bool avatarEntityDataChanged = false; - _avatarEntitiesLock.withReadLock([&] { - avatarEntityDataChanged = (identity.avatarEntityData != _avatarEntityData); - }); - - if (avatarEntityDataChanged) { - setAvatarEntityData(identity.avatarEntityData); - identityChanged = true; - } - if (identity.lookAtSnappingEnabled != _lookAtSnappingEnabled) { setProperty("lookAtSnappingEnabled", identity.lookAtSnappingEnabled); identityChanged = true; @@ -1831,19 +1820,25 @@ void AvatarData::processAvatarIdentity(const QByteArray& identityData, bool& ide } } -void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion) { +QUrl AvatarData::getWireSafeSkeletonModelURL() const { + if (_skeletonModelURL.scheme() != "file" && _skeletonModelURL.scheme() != "qrc") { + return _skeletonModelURL; + } else { + return QUrl(); + } +} + +void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, + AvatarTraits::TraitVersion traitVersion) { destination.writePrimitive(traitType); - if (traitVersion > 0) { + if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; destination.writePrimitive(typedVersion); } if (traitType == AvatarTraits::SkeletonModelURL) { - QByteArray encodedSkeletonURL; - if (_skeletonModelURL.scheme() != "file" && _skeletonModelURL.scheme() != "qrc") { - encodedSkeletonURL = _skeletonModelURL.toEncoded(); - } + QByteArray encodedSkeletonURL = getWireSafeSkeletonModelURL().toEncoded(); AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); destination.writePrimitive(encodedURLSize); @@ -1852,15 +1847,61 @@ void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& } } +void AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { + destination.writePrimitive(traitType); + + if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { + AvatarTraits::TraitVersion typedVersion = traitVersion; + destination.writePrimitive(typedVersion); + } + + destination.write(traitInstanceID.toRfc4122()); + + if (traitType == AvatarTraits::AvatarEntity) { + // grab a read lock on the avatar entities and check for entity data for the given ID + QByteArray entityBinaryData; + + _avatarEntitiesLock.withReadLock([this, &entityBinaryData, &traitInstanceID] { + if (_avatarEntityData.contains(traitInstanceID)) { + entityBinaryData = _avatarEntityData[traitInstanceID]; + } + }); + + if (!entityBinaryData.isNull()) { + AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size(); + + qDebug() << QJsonDocument::fromBinaryData(entityBinaryData).toJson(); + + destination.writePrimitive(entityBinarySize); + destination.write(entityBinaryData); + } else { + destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); + } + } +} + void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { if (traitType == AvatarTraits::SkeletonModelURL) { // get the URL from the binary data auto skeletonModelURL = QUrl::fromEncoded(traitBinaryData); - qDebug() << "Setting skeleton model URL from trait packet to" << skeletonModelURL; setSkeletonModelURL(skeletonModelURL); } } +void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData) { + if (traitType == AvatarTraits::AvatarEntity) { + updateAvatarEntity(instanceID, traitBinaryData); + } +} + +void AvatarData::processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { + if (traitType == AvatarTraits::AvatarEntity) { + removeAvatarEntityAndDetach(instanceID); + } +} + QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { QByteArray identityData; QDataStream identityStream(&identityData, QIODevice::Append); @@ -1868,17 +1909,13 @@ QByteArray AvatarData::identityByteArray(bool setIsReplicated) const { // when mixers send identity packets to agents, they simply forward along the last incoming sequence number they received // whereas agents send a fresh outgoing sequence number when identity data has changed - _avatarEntitiesLock.withReadLock([&] { - identityStream << getSessionUUID() - << (udt::SequenceNumber::Type) _identitySequenceNumber - << _attachmentData - << _displayName - << getSessionDisplayNameForTransport() // depends on _sessionDisplayName - << (_isReplicated || setIsReplicated) - << _avatarEntityData - << _lookAtSnappingEnabled - ; - }); + identityStream << getSessionUUID() + << (udt::SequenceNumber::Type) _identitySequenceNumber + << _attachmentData + << _displayName + << getSessionDisplayNameForTransport() // depends on _sessionDisplayName + << (_isReplicated || setIsReplicated) + << _lookAtSnappingEnabled; return identityData; } @@ -1899,7 +1936,7 @@ void AvatarData::setSkeletonModelURL(const QUrl& skeletonModelURL) { updateJointMappings(); if (_clientTraitsHandler) { - _clientTraitsHandler->markTraitChanged(AvatarTraits::SkeletonModelURL); + _clientTraitsHandler->markTraitUpdated(AvatarTraits::SkeletonModelURL); } emit skeletonModelURLChanged(); @@ -2095,7 +2132,6 @@ void AvatarData::sendIdentityPacket() { nodeList->sendPacketList(std::move(packetList), *node); }); - _avatarEntityDataLocallyEdited = false; _identityDataChanged = false; } @@ -2650,23 +2686,37 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent if (itr == _avatarEntityData.end()) { if (_avatarEntityData.size() < MAX_NUM_AVATAR_ENTITIES) { _avatarEntityData.insert(entityID, entityData); - _avatarEntityDataLocallyEdited = true; - markIdentityDataChanged(); } } else { itr.value() = entityData; - _avatarEntityDataLocallyEdited = true; - markIdentityDataChanged(); } }); + + if (_clientTraitsHandler) { + // we have a client traits handler, so we need to mark this instanced trait as changed + // so that changes will be sent next frame + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); + } } void AvatarData::clearAvatarEntity(const QUuid& entityID) { _avatarEntitiesLock.withWriteLock([&] { _avatarEntityData.remove(entityID); - _avatarEntityDataLocallyEdited = true; - markIdentityDataChanged(); }); + + if (_clientTraitsHandler) { + // we have a client traits handler, so we need to mark this removed instance trait as changed + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + } +} + +void AvatarData::removeAvatarEntityAndDetach(const QUuid &entityID) { + _avatarEntitiesLock.withWriteLock([this, &entityID]{ + _avatarEntityData.remove(entityID); + }); + + insertDetachedEntityID(entityID); } AvatarEntityMap AvatarData::getAvatarEntityData() const { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index a5d2d0749b..97ae90f694 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -947,12 +947,10 @@ public: const HeadData* getHeadData() const { return _headData; } struct Identity { - QUrl skeletonModelURL; QVector attachmentData; QString displayName; QString sessionDisplayName; bool isReplicated; - AvatarEntityMap avatarEntityData; bool lookAtSnappingEnabled; }; @@ -960,12 +958,21 @@ public: // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); - void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, int64_t traitVersion = -1); + void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, + AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + void packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); + void processTraitInstance(AvatarTraits::TraitType traitType, + AvatarTraits::TraitInstanceID instanceID, QByteArray traitBinaryData); + void processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID); QByteArray identityByteArray(bool setIsReplicated = false) const; + QUrl getWireSafeSkeletonModelURL() const; const QUrl& getSkeletonModelURL() const { return _skeletonModelURL; } + const QString& getDisplayName() const { return _displayName; } const QString& getSessionDisplayName() const { return _sessionDisplayName; } bool getLookAtSnappingEnabled() const { return _lookAtSnappingEnabled; } @@ -1311,6 +1318,8 @@ protected: virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer + void removeAvatarEntityAndDetach(const QUuid& entityID); + // Body scale float _targetScale; float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; @@ -1415,7 +1424,6 @@ protected: mutable ReadWriteLockable _avatarEntitiesLock; AvatarEntityIDs _avatarEntityDetached; // recently detached from this avatar AvatarEntityMap _avatarEntityData; - bool _avatarEntityDataLocallyEdited { false }; bool _avatarEntityDataChanged { false }; // used to transform any sensor into world space, including the _hmdSensorMat, or hand controllers. diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 407e88e27c..529614b20d 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -194,26 +194,6 @@ void AvatarHashMap::processAvatarIdentityPacket(QSharedPointer } } -bool AvatarHashMap::checkLastProcessedTraitVersion(QUuid avatarID, - AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion) { - auto it = _processedSimpleTraitVersions.find(avatarID); - if (it == _processedSimpleTraitVersions.end()) { - auto pair = _processedSimpleTraitVersions.insert({ - avatarID, - { AvatarTraits::TotalTraitTypes, AvatarTraits::DEFAULT_TRAIT_VERSION } - }); - - it = pair.first; - }; - - if (it->second[traitType] < newVersion) { - it->second[traitType] = newVersion; - return true; - } else { - return false; - } -} - void AvatarHashMap::processBulkAvatarTraits(QSharedPointer message, SharedNodePointer sendingNode) { while (message->getBytesLeftToRead()) { // read the avatar ID to figure out which avatar this is for @@ -233,24 +213,55 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitType traitType; message->readPrimitive(&traitType); + // grab the last trait versions for this avatar + auto& lastProcessedVersions = _processedTraitVersions[avatarID]; + while (traitType != AvatarTraits::NullTrait) { - AvatarTraits::TraitVersion traitVersion; - message->readPrimitive(&traitVersion); + AvatarTraits::TraitVersion packetTraitVersion; + message->readPrimitive(&packetTraitVersion); AvatarTraits::TraitWireSize traitBinarySize; - message->readPrimitive(&traitBinarySize); + bool skipBinaryTrait = false; - if (avatar) { - // check if this trait version is newer than what we already have for this avatar - bool traitIsNewer = checkLastProcessedTraitVersion(avatarID, traitType, traitVersion); - if (traitIsNewer) { - avatar->processTrait(traitType, message->readWithoutCopy(traitBinarySize)); - } else { - message->seek(message->getPosition() + traitBinarySize); + if (!avatar) { + skipBinaryTrait = true; + } + + if (AvatarTraits::isSimpleTrait(traitType)) { + message->readPrimitive(&traitBinarySize); + + if (avatar) { + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + avatar->processTrait(traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } else { + skipBinaryTrait = true; + } } } else { - // though we have no avatar pointer, we still hop through the packet in case there are - // traits for avatars we do have later in the packet + AvatarTraits::TraitInstanceID traitInstanceID = + QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); + + message->readPrimitive(&traitBinarySize); + + if (avatar) { + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); + } else { + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); + } + processedInstanceVersion = packetTraitVersion; + } else { + skipBinaryTrait = true; + } + } + } + + if (skipBinaryTrait) { + // we didn't read this trait because it was older or because we didn't have an avatar to process it for message->seek(message->getPosition() + traitBinarySize); } diff --git a/libraries/avatars/src/AvatarHashMap.h b/libraries/avatars/src/AvatarHashMap.h index ed8440eb89..ba16fa9568 100644 --- a/libraries/avatars/src/AvatarHashMap.h +++ b/libraries/avatars/src/AvatarHashMap.h @@ -30,7 +30,7 @@ #include "ScriptAvatarData.h" #include "AvatarData.h" -#include "AvatarTraits.h" +#include "AssociatedTraitValues.h" /**jsdoc * Note: An AvatarList API is also provided for Interface and client entity scripts: it is a @@ -155,10 +155,7 @@ protected: virtual void removeAvatar(const QUuid& sessionUUID, KillAvatarReason removalReason = KillAvatarReason::NoReason); virtual void handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason = KillAvatarReason::NoReason); - - bool checkLastProcessedTraitVersion(QUuid avatarID, - AvatarTraits::TraitType traitType, AvatarTraits::TraitVersion newVersion); - + AvatarHash _avatarHash; struct PendingAvatar { std::chrono::steady_clock::time_point creationTime; @@ -169,7 +166,7 @@ protected: AvatarPendingHash _pendingAvatars; mutable QReadWriteLock _hashLock; - std::unordered_map _processedSimpleTraitVersions; + std::unordered_map _processedTraitVersions; private: QUuid _lastOwnerSessionUUID; }; diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index d30da0e1af..acac215799 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -16,46 +16,43 @@ #include #include +#include + namespace AvatarTraits { enum TraitType : int8_t { NullTrait = -1, SkeletonModelURL, + AvatarEntity, TotalTraitTypes }; - class TraitTypeSet { - public: - TraitTypeSet() {}; - - TraitTypeSet(std::initializer_list types) { - for (auto type : types) { - _types[type] = true; - } - }; + using TraitInstanceID = QUuid; - bool contains(TraitType type) const { return _types[type]; } + inline bool isSimpleTrait(TraitType traitType) { + return traitType == SkeletonModelURL; + } - bool hasAny() const { return std::find(_types.begin(), _types.end(), true) != _types.end(); } - int size() const { return std::count(_types.begin(), _types.end(), true); } - - void insert(TraitType type) { _types[type] = true; } - void erase(TraitType type) { _types[type] = false; } - void clear() { std::fill(_types.begin(), _types.end(), false); } - private: - std::vector _types = { AvatarTraits::TotalTraitTypes, false }; - }; - - const TraitTypeSet SimpleTraitTypes = { SkeletonModelURL }; - - using TraitVersion = uint32_t; + using TraitVersion = int32_t; const TraitVersion DEFAULT_TRAIT_VERSION = 0; + const TraitVersion NULL_TRAIT_VERSION = -1; - using NullableTraitVersion = int64_t; - const NullableTraitVersion NULL_TRAIT_VERSION = -1; + using TraitWireSize = int16_t; + const TraitWireSize DELETED_TRAIT_SIZE = -1; - using TraitWireSize = uint16_t; + inline void packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination, + TraitVersion traitVersion = NULL_TRAIT_VERSION) { + destination.writePrimitive(traitType); - using SimpleTraitVersions = std::vector; + if (traitVersion > DEFAULT_TRAIT_VERSION) { + AvatarTraits::TraitVersion typedVersion = traitVersion; + destination.writePrimitive(typedVersion); + } + + destination.write(instanceID.toRfc4122()); + + destination.writePrimitive(DELETED_TRAIT_SIZE); + + } }; #endif // hifi_AvatarTraits_h diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 8b3ded1e1c..cf67304937 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -36,11 +36,11 @@ void ClientTraitsHandler::resetForNewMixer() { _currentTraitVersion = AvatarTraits::DEFAULT_TRAIT_VERSION; // mark that all traits should be sent next time - _performInitialSend = true; + _shouldPerformInitialSend = true; } void ClientTraitsHandler::sendChangedTraitsToMixer() { - if (hasChangedTraits() || _performInitialSend) { + if (hasChangedTraits() || _shouldPerformInitialSend) { // we have at least one changed trait to send auto nodeList = DependencyManager::get(); @@ -51,31 +51,57 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { } // we have a mixer to send to, setup our set traits packet + auto traitsPacketList = NLPacketList::create(PacketType::SetAvatarTraits, QByteArray(), true, true); // bump and write the current trait version to an extended header // the trait version is the same for all traits in this packet list - ++_currentTraitVersion; - QByteArray extendedHeader(reinterpret_cast(&_currentTraitVersion), sizeof(_currentTraitVersion)); - - auto traitsPacketList = NLPacketList::create(PacketType::SetAvatarTraits, extendedHeader, true); + traitsPacketList->writePrimitive(++_currentTraitVersion); // take a copy of the set of changed traits and clear the stored set - auto changedTraitsCopy { _changedTraits }; - _changedTraits.clear(); + auto traitStatusesCopy { _traitStatuses }; + _traitStatuses.reset(); + _hasChangedTraits = false; - if (_performInitialSend || changedTraitsCopy.contains(AvatarTraits::SkeletonModelURL)) { - traitsPacketList->startSegment(); - _owningAvatar->packTrait(AvatarTraits::SkeletonModelURL, *traitsPacketList); - traitsPacketList->endSegment(); + auto simpleIt = traitStatusesCopy.simpleCBegin(); + while (simpleIt != traitStatusesCopy.simpleCEnd()) { + // because the vector contains all trait types (for access using trait type as index) + // we double check that it is a simple iterator here + auto traitType = static_cast(std::distance(traitStatusesCopy.simpleCBegin(), simpleIt)); - // keep track of our skeleton version in case we get an override back - _currentSkeletonVersion = _currentTraitVersion; + if (AvatarTraits::isSimpleTrait(traitType)) { + if (_shouldPerformInitialSend || *simpleIt == Updated) { + if (traitType == AvatarTraits::SkeletonModelURL) { + _owningAvatar->packTrait(traitType, *traitsPacketList); + + // keep track of our skeleton version in case we get an override back + _currentSkeletonVersion = _currentTraitVersion; + } + } + } + + ++simpleIt; + } + + auto instancedIt = traitStatusesCopy.instancedCBegin(); + while (instancedIt != traitStatusesCopy.instancedCEnd()) { + for (auto& instanceIDValuePair : instancedIt->instances) { + if (_shouldPerformInitialSend || instanceIDValuePair.value == Updated) { + // this is a changed trait we need to send, ask the owning avatar to pack it + _owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); + } else if (instanceIDValuePair.value == Deleted) { + // pack delete for this trait instance + AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id, + *traitsPacketList); + } + } + + ++instancedIt; } nodeList->sendPacketList(std::move(traitsPacketList), *avatarMixer); // if this was an initial send of all traits, consider it completed - _performInitialSend = false; + _shouldPerformInitialSend = false; } } @@ -95,13 +121,13 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m // and the version matches what we last sent for skeleton if (traitType == AvatarTraits::SkeletonModelURL && traitVersion == _currentSkeletonVersion - && !hasTraitChanged(AvatarTraits::SkeletonModelURL)) { + && _traitStatuses[AvatarTraits::SkeletonModelURL] != Updated) { // override the skeleton URL but do not mark the trait as having changed // so that we don't unecessarily sent a new trait packet to the mixer with the overriden URL auto encodedSkeletonURL = QUrl::fromEncoded(message->readWithoutCopy(traitBinarySize)); _owningAvatar->setSkeletonModelURL(encodedSkeletonURL); - _changedTraits.erase(AvatarTraits::SkeletonModelURL); + _traitStatuses.erase(AvatarTraits::SkeletonModelURL); } else { message->seek(message->getPosition() + traitBinarySize); } diff --git a/libraries/avatars/src/ClientTraitsHandler.h b/libraries/avatars/src/ClientTraitsHandler.h index 1d4c67d0c4..27ba58d46b 100644 --- a/libraries/avatars/src/ClientTraitsHandler.h +++ b/libraries/avatars/src/ClientTraitsHandler.h @@ -14,7 +14,7 @@ #include -#include "AvatarTraits.h" +#include "AssociatedTraitValues.h" #include "Node.h" class AvatarData; @@ -26,10 +26,14 @@ public: void sendChangedTraitsToMixer(); - bool hasChangedTraits() { return _changedTraits.hasAny(); } - void markTraitChanged(AvatarTraits::TraitType changedTrait) { _changedTraits.insert(changedTrait); } + bool hasChangedTraits() { return _hasChangedTraits; } - bool hasTraitChanged(AvatarTraits::TraitType checkTrait) { return _changedTraits.contains(checkTrait) > 0; } + void markTraitUpdated(AvatarTraits::TraitType updatedTrait) + { _traitStatuses[updatedTrait] = Updated; _hasChangedTraits = true; } + void markInstancedTraitUpdated(AvatarTraits::TraitType traitType, QUuid updatedInstanceID) + { _traitStatuses.instanceInsert(traitType, updatedInstanceID, Updated); _hasChangedTraits = true; } + void markInstancedTraitDeleted(AvatarTraits::TraitType traitType, QUuid deleteInstanceID) + { _traitStatuses.instanceInsert(traitType, deleteInstanceID, Deleted); _hasChangedTraits = true; } void resetForNewMixer(); @@ -37,14 +41,21 @@ public slots: void processTraitOverride(QSharedPointer message, SharedNodePointer sendingNode); private: + enum ClientTraitStatus { + Unchanged, + Updated, + Deleted + }; + AvatarData* _owningAvatar; - AvatarTraits::TraitTypeSet _changedTraits; + AvatarTraits::AssociatedTraitValues _traitStatuses; AvatarTraits::TraitVersion _currentTraitVersion { AvatarTraits::DEFAULT_TRAIT_VERSION }; - AvatarTraits::NullableTraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; + AvatarTraits::TraitVersion _currentSkeletonVersion { AvatarTraits::NULL_TRAIT_VERSION }; - bool _performInitialSend { false }; + bool _shouldPerformInitialSend { false }; + bool _hasChangedTraits { false }; }; #endif // hifi_ClientTraitsHandler_h diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp index 94137786cd..9ed9a4f385 100644 --- a/libraries/networking/src/udt/PacketHeaders.cpp +++ b/libraries/networking/src/udt/PacketHeaders.cpp @@ -40,7 +40,7 @@ PacketVersion versionForPacketType(PacketType packetType) { case PacketType::AvatarData: case PacketType::BulkAvatarData: case PacketType::KillAvatar: - return static_cast(AvatarMixerPacketVersion::MigrateSkeletonURLToTraits); + return static_cast(AvatarMixerPacketVersion::MigrateAvatarEntitiesToTraits); case PacketType::MessagesData: return static_cast(MessageDataVersion::TextOrBinaryData); // ICE packets diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h index 31724ab5dc..8a2add3bb3 100644 --- a/libraries/networking/src/udt/PacketHeaders.h +++ b/libraries/networking/src/udt/PacketHeaders.h @@ -290,8 +290,9 @@ enum class AvatarMixerPacketVersion : PacketVersion { FBXReaderNodeReparenting, FixMannequinDefaultAvatarFeet, ProceduralFaceMovementFlagsAndBlendshapes, - FarGrabJoints - MigrateSkeletonURLToTraits + FarGrabJoints, + MigrateSkeletonURLToTraits, + MigrateAvatarEntitiesToTraits }; enum class DomainConnectRequestVersion : PacketVersion { From a56e9b08603e5cbacf1119a28f759d91e15ac1c2 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 19:01:52 -0700 Subject: [PATCH 36/64] allow agent to create and get avatar entities from script --- assignment-client/src/Agent.cpp | 7 +++++++ libraries/avatars/src/ScriptAvatarData.cpp | 20 ++++++++++++++++++++ libraries/avatars/src/ScriptAvatarData.h | 4 ++++ 3 files changed, 31 insertions(+) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 049e7d0ede..a8466fa368 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -365,6 +365,8 @@ void Agent::executeScript() { // setup an Avatar for the script to use auto scriptedAvatar = DependencyManager::get(); + scriptedAvatar->setID(getSessionUUID()); + connect(_scriptEngine.data(), SIGNAL(update(float)), scriptedAvatar.data(), SLOT(update(float)), Qt::ConnectionType::QueuedConnection); scriptedAvatar->setForceFaceTrackerConnected(true); @@ -606,6 +608,11 @@ void Agent::setIsAvatar(bool isAvatar) { } QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); + + _entityEditSender.setMyAvatar(nullptr); + } else { + auto scriptableAvatar = DependencyManager::get(); + _entityEditSender.setMyAvatar(scriptableAvatar.data()); } } diff --git a/libraries/avatars/src/ScriptAvatarData.cpp b/libraries/avatars/src/ScriptAvatarData.cpp index 8491e5368b..a716a40ad8 100644 --- a/libraries/avatars/src/ScriptAvatarData.cpp +++ b/libraries/avatars/src/ScriptAvatarData.cpp @@ -269,6 +269,26 @@ QVector ScriptAvatarData::getAttachmentData() const { // END // +#if PR_BUILD || DEV_BUILD +// +// ENTITY PROPERTIES +// START +// +AvatarEntityMap ScriptAvatarData::getAvatarEntities() const { + AvatarEntityMap scriptEntityData; + + if (AvatarSharedPointer sharedAvatarData = _avatarData.lock()) { + return sharedAvatarData->getAvatarEntityData(); + } + + return scriptEntityData; +} +// +// ENTITY PROPERTIES +// END +// +#endif + // // AUDIO PROPERTIES diff --git a/libraries/avatars/src/ScriptAvatarData.h b/libraries/avatars/src/ScriptAvatarData.h index 13713ff15f..91bac61728 100644 --- a/libraries/avatars/src/ScriptAvatarData.h +++ b/libraries/avatars/src/ScriptAvatarData.h @@ -116,6 +116,10 @@ public: Q_INVOKABLE QStringList getJointNames() const; Q_INVOKABLE QVector getAttachmentData() const; +#if DEV_BUILD || PR_BUILD + Q_INVOKABLE AvatarEntityMap getAvatarEntities() const; +#endif + // // AUDIO PROPERTIES // From f9230eca7f4f4a44eee606f6a8b927e24c4ec8c3 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 22:05:15 -0700 Subject: [PATCH 37/64] don't send override avatar URL if override matches --- .../src/avatars/AvatarMixerClientData.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 34b7ec97ff..274a76d0fa 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -178,19 +178,22 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *sl } if (!inWhitelist) { - // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change - _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); + // make sure we're not unecessarily overriding the default avatar with the default avatar + if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData->skeletonReplacementURL) { + // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change + qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() + << "to replacement" << slaveSharedData->skeletonReplacementURL << "for" << sendingNode.getUUID(); + _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); - qDebug() << "Sending overwritten" << _avatar->getSkeletonModelURL() << "back to sending avatar"; + auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); - auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); - - // the returned set traits packet uses the trait version from the incoming packet - // so the client knows they should not overwrite if they have since changed the trait - _avatar->packTrait(AvatarTraits::SkeletonModelURL, *packet, traitVersion); + // the returned set traits packet uses the trait version from the incoming packet + // so the client knows they should not overwrite if they have since changed the trait + _avatar->packTrait(AvatarTraits::SkeletonModelURL, *packet, traitVersion); - auto nodeList = DependencyManager::get(); - nodeList->sendPacket(std::move(packet), sendingNode); + auto nodeList = DependencyManager::get(); + nodeList->sendPacket(std::move(packet), sendingNode); + } } } } From de6fe43dda15804eb7e9ea8991a3850c6d589370 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 22:05:53 -0700 Subject: [PATCH 38/64] ensure joint mapping is processed for current FST url --- libraries/avatars/src/AvatarData.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 36dbe00937..4e396e2a9e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2034,6 +2034,13 @@ void AvatarData::setJointMappingsFromNetworkReply() { QNetworkReply* networkReply = static_cast(sender()); + // before we process this update, make sure that the skeleton model URL hasn't changed + // since we made the FST request + if (networkReply->url() != _skeletonModelURL) { + qCDebug(avatars) << "Refusing to set joint mappings for FST URL that does not match the current URL"; + return; + } + { QWriteLocker writeLock(&_jointDataLock); QByteArray line; From 1158ec8d50b290ab36afcc6be9faf6d87899e898 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Thu, 9 Aug 2018 22:06:28 -0700 Subject: [PATCH 39/64] improve change avoidance after avatar url override --- libraries/avatars/src/ClientTraitsHandler.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index cf67304937..a31808a916 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -122,12 +122,19 @@ void ClientTraitsHandler::processTraitOverride(QSharedPointer m if (traitType == AvatarTraits::SkeletonModelURL && traitVersion == _currentSkeletonVersion && _traitStatuses[AvatarTraits::SkeletonModelURL] != Updated) { + // override the skeleton URL but do not mark the trait as having changed - // so that we don't unecessarily sent a new trait packet to the mixer with the overriden URL + // so that we don't unecessarily send a new trait packet to the mixer with the overriden URL auto encodedSkeletonURL = QUrl::fromEncoded(message->readWithoutCopy(traitBinarySize)); + + auto hasChangesBefore = _hasChangedTraits; + _owningAvatar->setSkeletonModelURL(encodedSkeletonURL); + // setSkeletonModelURL will flag us for changes to the SkeletonModelURL so we reset some state here to + // avoid unnecessarily sending the overriden skeleton model URL back to the mixer _traitStatuses.erase(AvatarTraits::SkeletonModelURL); + _hasChangedTraits = hasChangesBefore; } else { message->seek(message->getPosition() + traitBinarySize); } From fc5b72e9b952c8bcd82692c1dd8d21ee6afe5c60 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 15:09:35 -0700 Subject: [PATCH 40/64] cleanup sent trait versions for removed avatars on mixer --- assignment-client/src/avatars/AvatarMixer.cpp | 3 ++- assignment-client/src/avatars/AvatarMixerClientData.cpp | 7 +++++++ assignment-client/src/avatars/AvatarMixerClientData.h | 5 +---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp index 228102ee53..167c1cd29c 100644 --- a/assignment-client/src/avatars/AvatarMixer.cpp +++ b/assignment-client/src/avatars/AvatarMixer.cpp @@ -470,7 +470,8 @@ void AvatarMixer::handleAvatarKilled(SharedNodePointer avatarNode) { QMetaObject::invokeMethod(node->getLinkedData(), "cleanupKilledNode", Qt::AutoConnection, - Q_ARG(const QUuid&, QUuid(avatarNode->getUUID()))); + Q_ARG(const QUuid&, QUuid(avatarNode->getUUID())), + Q_ARG(Node::LocalID, avatarNode->getLocalID())); } ); } diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 274a76d0fa..cc4356fb1a 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -285,3 +285,10 @@ AvatarMixerClientData::TraitsCheckTimestamp AvatarMixerClientData::getLastOtherA return TraitsCheckTimestamp(); } } + +void AvatarMixerClientData::cleanupKilledNode(const QUuid& nodeUUID, Node::LocalID nodeLocalID) { + removeLastBroadcastSequenceNumber(nodeUUID); + removeLastBroadcastTime(nodeUUID); + _lastSentTraitsTimestamps.erase(nodeLocalID); + _sentTraitVersions.erase(nodeLocalID); +} diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index dcbf8a6dba..02b37e08f8 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -57,10 +57,7 @@ public: void setLastBroadcastTime(const QUuid& nodeUUID, uint64_t broadcastTime) { _lastBroadcastTimes[nodeUUID] = broadcastTime; } Q_INVOKABLE void removeLastBroadcastTime(const QUuid& nodeUUID) { _lastBroadcastTimes.erase(nodeUUID); } - Q_INVOKABLE void cleanupKilledNode(const QUuid& nodeUUID) { - removeLastBroadcastSequenceNumber(nodeUUID); - removeLastBroadcastTime(nodeUUID); - } + Q_INVOKABLE void cleanupKilledNode(const QUuid& nodeUUID, Node::LocalID nodeLocalID); uint16_t getLastReceivedSequenceNumber() const { return _lastReceivedSequenceNumber; } From 0f03764c97693e026e0eed7be8ee5cdd541a8d35 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 15:10:50 -0700 Subject: [PATCH 41/64] create missing avatar when processing traits --- libraries/avatars/src/AvatarHashMap.cpp | 45 +++++++++---------------- 1 file changed, 16 insertions(+), 29 deletions(-) diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 529614b20d..0ab602e233 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -200,14 +200,8 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess auto avatarID = QUuid::fromRfc4122(message->readWithoutCopy(NUM_BYTES_RFC4122_UUID)); // grab the avatar so we can ask it to process trait data - AvatarSharedPointer avatar; - - QReadLocker locker(&_hashLock); - auto it = _avatarHash.find(avatarID); - if (it != _avatarHash.end()) { - avatar = *it; - } - locker.unlock(); + bool isNewAvatar; + auto avatar = newOrExistingAvatar(avatarID, sendingNode, isNewAvatar); // read the first trait type for this avatar AvatarTraits::TraitType traitType; @@ -223,21 +217,16 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess AvatarTraits::TraitWireSize traitBinarySize; bool skipBinaryTrait = false; - if (!avatar) { - skipBinaryTrait = true; - } if (AvatarTraits::isSimpleTrait(traitType)) { message->readPrimitive(&traitBinarySize); - if (avatar) { - // check if this trait version is newer than what we already have for this avatar - if (packetTraitVersion > lastProcessedVersions[traitType]) { - avatar->processTrait(traitType, message->read(traitBinarySize)); - lastProcessedVersions[traitType] = packetTraitVersion; - } else { - skipBinaryTrait = true; - } + // check if this trait version is newer than what we already have for this avatar + if (packetTraitVersion > lastProcessedVersions[traitType]) { + avatar->processTrait(traitType, message->read(traitBinarySize)); + lastProcessedVersions[traitType] = packetTraitVersion; + } else { + skipBinaryTrait = true; } } else { AvatarTraits::TraitInstanceID traitInstanceID = @@ -245,18 +234,16 @@ void AvatarHashMap::processBulkAvatarTraits(QSharedPointer mess message->readPrimitive(&traitBinarySize); - if (avatar) { - auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); - if (packetTraitVersion > processedInstanceVersion) { - if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { - avatar->processDeletedTraitInstance(traitType, traitInstanceID); - } else { - avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); - } - processedInstanceVersion = packetTraitVersion; + auto& processedInstanceVersion = lastProcessedVersions.getInstanceValueRef(traitType, traitInstanceID); + if (packetTraitVersion > processedInstanceVersion) { + if (traitBinarySize == AvatarTraits::DELETED_TRAIT_SIZE) { + avatar->processDeletedTraitInstance(traitType, traitInstanceID); } else { - skipBinaryTrait = true; + avatar->processTraitInstance(traitType, traitInstanceID, message->read(traitBinarySize)); } + processedInstanceVersion = packetTraitVersion; + } else { + skipBinaryTrait = true; } } From e33f349d5390ec7f4535d1fca9b050a6fd99bdfa Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 16:06:09 -0700 Subject: [PATCH 42/64] fix flagging for avatar entity update/delete --- interface/src/Application.cpp | 18 +++----- interface/src/avatar/MyAvatar.cpp | 1 - .../src/avatars-renderer/Avatar.cpp | 20 ++++++--- libraries/avatars/src/AvatarData.cpp | 41 +++++++++++-------- libraries/avatars/src/AvatarData.h | 6 +-- .../entities/src/EntityEditPacketSender.cpp | 7 +--- .../entities/src/EntityEditPacketSender.h | 1 - .../entities/src/EntityScriptingInterface.cpp | 13 ++++-- 8 files changed, 56 insertions(+), 51 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index e556fd734f..60af79bfda 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1013,7 +1013,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo // This is done so as not break previous command line scripts if (testScriptPath.left(URL_SCHEME_HTTP.length()) == URL_SCHEME_HTTP || testScriptPath.left(URL_SCHEME_FTP.length()) == URL_SCHEME_FTP) { - + setProperty(hifi::properties::TEST, QUrl::fromUserInput(testScriptPath)); } else if (QFileInfo(testScriptPath).exists()) { setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath)); @@ -1830,14 +1830,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo } }); - connect(getEntities()->getTree().get(), &EntityTree::deletingEntity, [](const EntityItemID& entityItemID) { - auto avatarManager = DependencyManager::get(); - auto myAvatar = avatarManager ? avatarManager->getMyAvatar() : nullptr; - if (myAvatar) { - myAvatar->clearAvatarEntity(entityItemID); - } - }); - EntityTree::setAddMaterialToEntityOperator([this](const QUuid& entityID, graphics::MaterialLayer material, const std::string& parentMaterialName) { // try to find the renderable auto renderable = getEntities()->renderableForEntityId(entityID); @@ -2617,7 +2609,7 @@ Application::~Application() { // Can't log to file passed this point, FileLogger about to be deleted qInstallMessageHandler(LogHandler::verboseMessageHandler); - + _renderEventHandler->deleteLater(); } @@ -5498,8 +5490,8 @@ void Application::update(float deltaTime) { quint64 now = usecTimestampNow(); // Check for flagged EntityData having arrived. auto entityTreeRenderer = getEntities(); - if (isServerlessMode() || - (_octreeProcessor.isLoadSequenceComplete() )) { + if (isServerlessMode() || + (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; @@ -6393,7 +6385,7 @@ void Application::nodeActivated(SharedNodePointer node) { if (_avatarOverrideUrl.isValid()) { getMyAvatar()->useFullAvatarURL(_avatarOverrideUrl); } - + if (getMyAvatar()->getFullAvatarURLFromPreferences() != getMyAvatar()->getSkeletonModelURL()) { getMyAvatar()->resetFullAvatarURL(); } diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 2c88f917a1..deef69d980 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1292,7 +1292,6 @@ void MyAvatar::loadData() { // HACK: manually remove empty 'avatarEntityData' else legacy data may persist in settings file settings.remove("avatarEntityData"); } - setAvatarEntityDataChanged(true); // Flying preferences must be loaded before calling setFlyingEnabled() Setting::Handle firstRunVal { Settings::firstRun, true }; diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 0b43fd5433..150ecb6d44 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -224,14 +224,24 @@ void Avatar::setAvatarEntityDataChanged(bool value) { void Avatar::updateAvatarEntities() { PerformanceTimer perfTimer("attachments"); + + // AVATAR ENTITY UPDATE FLOW // - if queueEditEntityMessage sees clientOnly flag it does _myAvatar->updateAvatarEntity() - // - updateAvatarEntity saves the bytes and sets _avatarEntityDataLocallyEdited - // - MyAvatar::update notices _avatarEntityDataLocallyEdited and calls sendIdentityPacket - // - sendIdentityPacket sends the entity bytes to the server which relays them to other interfaces - // - AvatarHashMap::processAvatarIdentityPacket on other interfaces call avatar->setAvatarEntityData() - // - setAvatarEntityData saves the bytes and sets _avatarEntityDataChanged = true + // - updateAvatarEntity saves the bytes and flags the trait instance for the entity as updated + // - ClientTraitsHandler::sendChangedTraitsToMixer sends the entity bytes to the mixer which relays them to other interfaces + // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processTraitInstace + // - AvatarData::processTraitInstance calls updateAvatarEntity, which sets _avatarEntityDataChanged = true // - (My)Avatar::simulate notices _avatarEntityDataChanged and here we are... + // AVATAR ENTITY DELETE FLOW + // - EntityScriptingInterface::deleteEntity calls _myAvatar->clearAvatarEntity() for deleted avatar entities + // - clearAvatarEntity removes the avatar entity and flags the trait instance for the entity as deleted + // - ClientTraitsHandler::sendChangedTraitsToMixer sends a deletion to the mixer which relays to other interfaces + // - AvatarHashMap::processBulkAvatarTraits on other interfaces calls avatar->processDeletedTraitInstace + // - AvatarData::processDeletedTraitInstance calls clearAvatarEntity + // - AvatarData::clearAvatarEntity sets _avatarEntityDataChanged = true and adds the ID to the detached list + // - Avatar::simulate notices _avatarEntityDataChanged and here we are... + if (!_avatarEntityDataChanged) { return; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 4e396e2a9e..f1b9986186 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1898,7 +1898,7 @@ void AvatarData::processTraitInstance(AvatarTraits::TraitType traitType, void AvatarData::processDeletedTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID) { if (traitType == AvatarTraits::AvatarEntity) { - removeAvatarEntityAndDetach(instanceID); + clearAvatarEntity(instanceID); } } @@ -2687,7 +2687,7 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { const int MAX_NUM_AVATAR_ENTITIES = 42; -void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate) { _avatarEntitiesLock.withWriteLock([&] { AvatarEntityMap::iterator itr = _avatarEntityData.find(entityID); if (itr == _avatarEntityData.end()) { @@ -2699,6 +2699,10 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } }); + if (requiresTreeUpdate) { + _avatarEntityDataChanged = true; + } + if (_clientTraitsHandler) { // we have a client traits handler, so we need to mark this instanced trait as changed // so that changes will be sent next frame @@ -2706,26 +2710,27 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } } -void AvatarData::clearAvatarEntity(const QUuid& entityID) { - _avatarEntitiesLock.withWriteLock([&] { - _avatarEntityData.remove(entityID); +void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree) { + + bool removedEntity = false; + + _avatarEntitiesLock.withWriteLock([this, &removedEntity, &entityID] { + removedEntity = _avatarEntityData.remove(entityID); }); - if (_clientTraitsHandler) { - // we have a client traits handler, so we need to mark this removed instance trait as changed - // so that changes are sent next frame - _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + if (removedEntity) { + if (requiresRemovalFromTree) { + insertDetachedEntityID(entityID); + } + + if (_clientTraitsHandler) { + // we have a client traits handler, so we need to mark this removed instance trait as changed + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + } } } -void AvatarData::removeAvatarEntityAndDetach(const QUuid &entityID) { - _avatarEntitiesLock.withWriteLock([this, &entityID]{ - _avatarEntityData.remove(entityID); - }); - - insertDetachedEntityID(entityID); -} - AvatarEntityMap AvatarData::getAvatarEntityData() const { AvatarEntityMap result; _avatarEntitiesLock.withReadLock([&] { @@ -2738,6 +2743,8 @@ void AvatarData::insertDetachedEntityID(const QUuid entityID) { _avatarEntitiesLock.withWriteLock([&] { _avatarEntityDetached.insert(entityID); }); + + _avatarEntityDataChanged = true; } void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 97ae90f694..6f1871974d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -925,13 +925,13 @@ public: * @param {Uuid} entityID * @param {string} entityData */ - Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate = true); /**jsdoc * @function MyAvatar.clearAvatarEntity * @param {Uuid} entityID */ - Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID); + Q_INVOKABLE void clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFromTree = true); /**jsdoc @@ -1318,8 +1318,6 @@ protected: virtual const QString& getSessionDisplayNameForTransport() const { return _sessionDisplayName; } virtual void maybeUpdateSessionDisplayNameFromTransport(const QString& sessionDisplayName) { } // No-op in AvatarMixer - void removeAvatarEntityAndDetach(const QUuid& entityID); - // Body scale float _targetScale; float _domainMinimumHeight { MIN_AVATAR_HEIGHT }; diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index 0982775b09..d288126348 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -74,7 +74,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, jsonProperties = QJsonDocument(jsonObject); QByteArray binaryProperties = jsonProperties.toBinaryData(); - _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties, false); entity->setLastBroadcast(usecTimestampNow()); } @@ -149,11 +149,6 @@ void EntityEditPacketSender::queueEditEntityMessage(PacketType type, void EntityEditPacketSender::queueEraseEntityMessage(const EntityItemID& entityItemID) { - // in case this was a clientOnly entity: - if(_myAvatar) { - _myAvatar->clearAvatarEntity(entityItemID); - } - QByteArray bufferOut(NLPacket::maxPayloadSize(PacketType::EntityErase), 0); if (EntityItemProperties::encodeEraseEntityMessage(entityItemID, bufferOut)) { diff --git a/libraries/entities/src/EntityEditPacketSender.h b/libraries/entities/src/EntityEditPacketSender.h index 31f91707b8..9bf9095f7f 100644 --- a/libraries/entities/src/EntityEditPacketSender.h +++ b/libraries/entities/src/EntityEditPacketSender.h @@ -27,7 +27,6 @@ public: void setMyAvatar(AvatarData* myAvatar) { _myAvatar = myAvatar; } AvatarData* getMyAvatar() { return _myAvatar; } - void clearAvatarEntity(QUuid entityID) { assert(_myAvatar); _myAvatar->clearAvatarEntity(entityID); } /// Queues an array of several voxel edit messages. Will potentially send a pending multi-command packet. Determines /// which voxel-server node or nodes the packet should be sent to. Can be called even before voxel servers are known, in diff --git a/libraries/entities/src/EntityScriptingInterface.cpp b/libraries/entities/src/EntityScriptingInterface.cpp index c080fbbb88..d9924cb9fd 100644 --- a/libraries/entities/src/EntityScriptingInterface.cpp +++ b/libraries/entities/src/EntityScriptingInterface.cpp @@ -574,7 +574,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { _activityTracking.deletedEntityCount++; EntityItemID entityID(id); - bool shouldDelete = true; + bool shouldSendDeleteToServer = true; // If we have a local entity tree set, then also update it. if (_entityTree) { @@ -591,16 +591,21 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { auto avatarHashMap = DependencyManager::get(); AvatarSharedPointer myAvatar = avatarHashMap->getAvatarBySessionID(myNodeID); myAvatar->insertDetachedEntityID(id); - shouldDelete = false; + shouldSendDeleteToServer = false; return; } if (entity->getLocked()) { - shouldDelete = false; + shouldSendDeleteToServer = false; } else { // only delete local entities, server entities will round trip through the server filters if (entity->getClientOnly() || _entityTree->isServerlessMode()) { + shouldSendDeleteToServer = false; _entityTree->deleteEntity(entityID); + + if (entity->getClientOnly() && getEntityPacketSender()->getMyAvatar()) { + getEntityPacketSender()->getMyAvatar()->clearAvatarEntity(entityID, false); + } } } } @@ -608,7 +613,7 @@ void EntityScriptingInterface::deleteEntity(QUuid id) { } // if at this point, we know the id, and we should still delete the entity, send the update to the entity server - if (shouldDelete) { + if (shouldSendDeleteToServer) { getEntityPacketSender()->queueEraseEntityMessage(entityID); } } From 6c204b682d5862d8c1bfa007e69089768e64a97c Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Fri, 10 Aug 2018 16:47:28 -0700 Subject: [PATCH 43/64] include trait bytes written in over budget calculation --- .../src/avatars/AvatarMixerSlave.cpp | 25 +++++++++------ .../src/avatars/AvatarMixerSlave.h | 6 ++-- libraries/avatars/src/AvatarData.cpp | 31 ++++++++++++------- libraries/avatars/src/AvatarData.h | 8 ++--- libraries/avatars/src/AvatarTraits.h | 13 +++++--- 5 files changed, 50 insertions(+), 33 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index ebbaeb7a35..14818870d1 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -79,9 +79,9 @@ int AvatarMixerSlave::sendIdentityPacket(const AvatarMixerClientData* nodeData, } } -void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, - const AvatarMixerClientData* sendingNodeData, - NLPacketList& traitsPacketList) { +qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList) { auto otherNodeLocalID = sendingNodeData->getNodeLocalID(); @@ -90,11 +90,13 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste auto timeOfLastTraitsSent = listeningNodeData->getLastOtherAvatarTraitsSendPoint(otherNodeLocalID); auto timeOfLastTraitsChange = sendingNodeData->getLastReceivedTraitsChange(); + qint64 bytesWritten = 0; + if (timeOfLastTraitsChange > timeOfLastTraitsSent) { // there is definitely new traits data to send // add the avatar ID to mark the beginning of traits for this avatar - traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122()); + bytesWritten += traitsPacketList.write(sendingNodeData->getNodeID().toRfc4122()); auto sendingAvatar = sendingNodeData->getAvatarSharedPointer(); @@ -115,7 +117,7 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste if (lastReceivedVersions[traitType] > lastSentVersionRef) { // there is an update to this trait, add it to the traits packet - sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); + bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); // update the last sent version lastSentVersionRef = lastReceivedVersion; @@ -152,7 +154,7 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste if (!isDeleted && (sentInstanceIt == sentIDValuePairs.end() || receivedVersion > sentInstanceIt->value)) { // this instance version exists and has never been sent or is newer so we need to send it - sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion); + bytesWritten += sendingAvatar->packTraitInstance(traitType, instanceID, traitsPacketList, receivedVersion); if (sentInstanceIt != sentIDValuePairs.end()) { sentInstanceIt->value = receivedVersion; @@ -161,7 +163,7 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste } } else if (isDeleted && sentInstanceIt != sentIDValuePairs.end() && absoluteReceivedVersion > sentInstanceIt->value) { // this instance version was deleted and we haven't sent the delete to this client yet - AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion); + bytesWritten += AvatarTraits::packInstancedTraitDelete(traitType, instanceID, traitsPacketList, absoluteReceivedVersion); // update the last sent version for this trait instance to the absolute value of the deleted version sentInstanceIt->value = absoluteReceivedVersion; @@ -172,12 +174,14 @@ void AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* liste } // write a null trait type to mark the end of trait data for this avatar - traitsPacketList.writePrimitive(AvatarTraits::NullTrait); + bytesWritten += traitsPacketList.writePrimitive(AvatarTraits::NullTrait); // since we send all traits for this other avatar, update the time of last traits sent // to match the time of last traits change listeningNodeData->setLastOtherAvatarTraitsSendPoint(otherNodeLocalID, timeOfLastTraitsChange); } + + return bytesWritten; } int AvatarMixerSlave::sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode) { @@ -239,6 +243,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) // keep track of outbound data rate specifically for avatar data int numAvatarDataBytes = 0; int identityBytesSent = 0; + int traitBytesSent = 0; // max number of avatarBytes per frame auto maxAvatarBytesPerFrame = (_maxKbpsPerNode * BYTES_PER_KILOBIT) / AVATAR_MIXER_BROADCAST_FRAMES_PER_SECOND; @@ -550,7 +555,9 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node) _stats.avatarDataPackingElapsedTime += (endAvatarDataPacking - startAvatarDataPacking); // use helper to add any changed traits to our packet list - addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + traitBytesSent += addChangedTraitsToBulkPacket(nodeData, otherNodeData, *traitsPacketList); + + traitsPacketList->getDataSize(); } quint64 startPacketSending = usecTimestampNow(); diff --git a/assignment-client/src/avatars/AvatarMixerSlave.h b/assignment-client/src/avatars/AvatarMixerSlave.h index 5112faae29..bcb70f8743 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.h +++ b/assignment-client/src/avatars/AvatarMixerSlave.h @@ -104,9 +104,9 @@ private: int sendIdentityPacket(const AvatarMixerClientData* nodeData, const SharedNodePointer& destinationNode); int sendReplicatedIdentityPacket(const Node& agentNode, const AvatarMixerClientData* nodeData, const Node& destinationNode); - void addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, - const AvatarMixerClientData* sendingNodeData, - NLPacketList& traitsPacketList); + qint64 addChangedTraitsToBulkPacket(AvatarMixerClientData* listeningNodeData, + const AvatarMixerClientData* sendingNodeData, + NLPacketList& traitsPacketList); void broadcastAvatarDataToAgent(const SharedNodePointer& node); void broadcastAvatarDataToDownstreamMixer(const SharedNodePointer& node); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f1b9986186..d63fde4cbe 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1828,35 +1828,40 @@ QUrl AvatarData::getWireSafeSkeletonModelURL() const { } } -void AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, +qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { - destination.writePrimitive(traitType); + qint64 bytesWritten = 0; + bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; - destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(typedVersion); } if (traitType == AvatarTraits::SkeletonModelURL) { QByteArray encodedSkeletonURL = getWireSafeSkeletonModelURL().toEncoded(); AvatarTraits::TraitWireSize encodedURLSize = encodedSkeletonURL.size(); - destination.writePrimitive(encodedURLSize); + bytesWritten += destination.writePrimitive(encodedURLSize); - destination.write(encodedSkeletonURL); + bytesWritten += destination.write(encodedSkeletonURL); } + + return bytesWritten; } -void AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, +qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID traitInstanceID, ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion) { - destination.writePrimitive(traitType); + qint64 bytesWritten = 0; + + bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; - destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(typedVersion); } - destination.write(traitInstanceID.toRfc4122()); + bytesWritten += destination.write(traitInstanceID.toRfc4122()); if (traitType == AvatarTraits::AvatarEntity) { // grab a read lock on the avatar entities and check for entity data for the given ID @@ -1873,12 +1878,14 @@ void AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTrai qDebug() << QJsonDocument::fromBinaryData(entityBinaryData).toJson(); - destination.writePrimitive(entityBinarySize); - destination.write(entityBinaryData); + bytesWritten += destination.writePrimitive(entityBinarySize); + bytesWritten += destination.write(entityBinaryData); } else { - destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); + bytesWritten += destination.writePrimitive(AvatarTraits::DELETED_TRAIT_SIZE); } } + + return bytesWritten; } void AvatarData::processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData) { diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 6f1871974d..0f5e1d9ee4 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -958,10 +958,10 @@ public: // identityChanged returns true if identity has changed, false otherwise. Similarly for displayNameChanged and skeletonModelUrlChange. void processAvatarIdentity(const QByteArray& identityData, bool& identityChanged, bool& displayNameChanged); - void packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, - AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); - void packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, - ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + qint64 packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice& destination, + AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); + qint64 packTraitInstance(AvatarTraits::TraitType traitType, AvatarTraits::TraitInstanceID instanceID, + ExtendedIODevice& destination, AvatarTraits::TraitVersion traitVersion = AvatarTraits::NULL_TRAIT_VERSION); void processTrait(AvatarTraits::TraitType traitType, QByteArray traitBinaryData); void processTraitInstance(AvatarTraits::TraitType traitType, diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index acac215799..16e897319b 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -39,19 +39,22 @@ namespace AvatarTraits { using TraitWireSize = int16_t; const TraitWireSize DELETED_TRAIT_SIZE = -1; - inline void packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination, + inline qint64 packInstancedTraitDelete(TraitType traitType, TraitInstanceID instanceID, ExtendedIODevice& destination, TraitVersion traitVersion = NULL_TRAIT_VERSION) { - destination.writePrimitive(traitType); + qint64 bytesWritten = 0; + + bytesWritten += destination.writePrimitive(traitType); if (traitVersion > DEFAULT_TRAIT_VERSION) { AvatarTraits::TraitVersion typedVersion = traitVersion; - destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(typedVersion); } - destination.write(instanceID.toRfc4122()); + bytesWritten += destination.write(instanceID.toRfc4122()); - destination.writePrimitive(DELETED_TRAIT_SIZE); + bytesWritten += destination.writePrimitive(DELETED_TRAIT_SIZE); + return bytesWritten; } }; From f0ba61ff0522b8aca88b3a9fc5014e4a219dbdea Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 12:29:28 -0700 Subject: [PATCH 44/64] add missing local ID, reset client processed trait versions --- assignment-client/src/avatars/AvatarMixerClientData.cpp | 2 +- interface/src/avatar/AvatarManager.cpp | 1 - libraries/avatars/src/AvatarHashMap.cpp | 3 +++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index cc4356fb1a..5c84e3f755 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -19,7 +19,7 @@ #include "AvatarMixerSlave.h" AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID, Node::LocalID nodeLocalID) : - NodeData(nodeID) + NodeData(nodeID, nodeLocalID) { // in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID _avatar->setID(nodeID); diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index 8569aaf05a..0d180bc40d 100644 --- a/interface/src/avatar/AvatarManager.cpp +++ b/interface/src/avatar/AvatarManager.cpp @@ -14,7 +14,6 @@ #include #include -#include #include "AvatarLogging.h" diff --git a/libraries/avatars/src/AvatarHashMap.cpp b/libraries/avatars/src/AvatarHashMap.cpp index 0ab602e233..64b26131be 100644 --- a/libraries/avatars/src/AvatarHashMap.cpp +++ b/libraries/avatars/src/AvatarHashMap.cpp @@ -279,6 +279,9 @@ void AvatarHashMap::removeAvatar(const QUuid& sessionUUID, KillAvatarReason remo } void AvatarHashMap::handleRemovedAvatar(const AvatarSharedPointer& removedAvatar, KillAvatarReason removalReason) { + // remove any information about processed traits for this avatar + _processedTraitVersions.erase(removedAvatar->getID()); + qCDebug(avatars) << "Removed avatar with UUID" << uuidStringWithoutCurlyBraces(removedAvatar->getSessionUUID()) << "from AvatarHashMap" << removalReason; emit avatarRemovedEvent(removedAvatar->getSessionUUID()); From cd05d9335a8ea0d433e9ef0fdc3c365295fb25fc Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 13:29:06 -0700 Subject: [PATCH 45/64] remove debug of entity json document --- libraries/avatars/src/AvatarData.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index d63fde4cbe..8677d31cb5 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1876,8 +1876,6 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr if (!entityBinaryData.isNull()) { AvatarTraits::TraitWireSize entityBinarySize = entityBinaryData.size(); - qDebug() << QJsonDocument::fromBinaryData(entityBinaryData).toJson(); - bytesWritten += destination.writePrimitive(entityBinarySize); bytesWritten += destination.write(entityBinaryData); } else { From 7e127749f789ce882bc76455f38688ef85071a70 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 14:27:11 -0700 Subject: [PATCH 46/64] move AssignmentDynamicFactory to entity-server only --- assignment-client/src/Agent.cpp | 4 ---- assignment-client/src/entities/EntityServer.cpp | 6 ++++++ 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index a8466fa368..06a14927d3 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -52,7 +52,6 @@ #include #include // TODO: consider moving to scriptengine.h -#include "AssignmentDynamicFactory.h" #include "entities/AssignmentParentFinder.h" #include "RecordingScriptingInterface.h" #include "AbstractAudioInterface.h" @@ -72,9 +71,6 @@ Agent::Agent(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(false); - DependencyManager::registerInheritance(); - DependencyManager::set(); - DependencyManager::set(); DependencyManager::set(); diff --git a/assignment-client/src/entities/EntityServer.cpp b/assignment-client/src/entities/EntityServer.cpp index 868e570e0c..089fb3e52f 100644 --- a/assignment-client/src/entities/EntityServer.cpp +++ b/assignment-client/src/entities/EntityServer.cpp @@ -24,6 +24,7 @@ #include #include +#include "../AssignmentDynamicFactory.h" #include "AssignmentParentFinder.h" #include "EntityNodeData.h" #include "EntityServerConsts.h" @@ -42,6 +43,9 @@ EntityServer::EntityServer(ReceivedMessage& message) : DependencyManager::set(); DependencyManager::set(); + DependencyManager::registerInheritance(); + DependencyManager::set(); + auto& packetReceiver = DependencyManager::get()->getPacketReceiver(); packetReceiver.registerListenerForTypes({ PacketType::EntityAdd, PacketType::EntityClone, @@ -71,6 +75,8 @@ EntityServer::~EntityServer() { void EntityServer::aboutToFinish() { DependencyManager::get()->cleanup(); + DependencyManager::destroy(); + OctreeServer::aboutToFinish(); } From eda84d281600a856f5b0ce5f75d3aea4253512a1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 14:29:42 -0700 Subject: [PATCH 47/64] flag deleted avatar entities when replacing full map --- libraries/avatars/src/AvatarData.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 8677d31cb5..5094327c9a 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2729,7 +2729,7 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFr } if (_clientTraitsHandler) { - // we have a client traits handler, so we need to mark this removed instance trait as changed + // we have a client traits handler, so we need to mark this removed instance trait as deleted // so that changes are sent next frame _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); } @@ -2769,6 +2769,12 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { foreach (auto entityID, previousAvatarEntityIDs) { if (!_avatarEntityData.contains(entityID)) { _avatarEntityDetached.insert(entityID); + + if (_clientTraitsHandler) { + // we have a client traits handler, so we flag this removed entity as deleted + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); + } } } } From 25876bca6397ed1b9036ef5a444beee08e78c02f Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 14:55:07 -0700 Subject: [PATCH 48/64] send attachment clears through setAvatarEntityData --- interface/src/AvatarBookmarks.cpp | 15 ++------------- interface/src/avatar/MyAvatar.cpp | 19 +++---------------- interface/src/avatar/MyAvatar.h | 2 +- 3 files changed, 6 insertions(+), 30 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index 6afab71c90..c7c87aee3e 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -151,13 +151,7 @@ bool isWearableEntity(const EntityItemPointer& entity) { void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); - + myAvatar->clearAvatarEntities(); addAvatarEntities(avatarEntities); } @@ -180,12 +174,7 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { if (!bookmark.empty()) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - myAvatar->removeAvatarEntities([&](const QUuid& entityID) { - auto entity = entityTree->findEntityByID(entityID); - return entity && isWearableEntity(entity); - }); + myAvatar->clearAvatarEntities(); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index deef69d980..709ddfd17a 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,21 +1703,6 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } -void MyAvatar::removeAvatarEntities(const std::function& condition) { - auto treeRenderer = DependencyManager::get(); - EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; - if (entityTree) { - entityTree->withWriteLock([&] { - AvatarEntityMap avatarEntities = getAvatarEntityData(); - for (auto entityID : avatarEntities.keys()) { - if (!condition || condition(entityID)) { - entityTree->deleteEntity(entityID, true, true); - } - } - }); - } -} - QVariantList MyAvatar::getAvatarEntitiesVariant() { QVariantList avatarEntitiesData; QScriptEngine scriptEngine; @@ -2114,7 +2099,9 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) attachmentDataToEntityProperties(data, properties); newEntitiesProperties.push_back(properties); } - removeAvatarEntities(); + + clearAvatarEntities(); + for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index ba6348cc22..247a0c79c8 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,7 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void removeAvatarEntities(const std::function& condition = {}); + void clearAvatarEntities() { setAvatarEntityData(AvatarEntityMap()); } /**jsdoc * @function MyAvatar.isFlying From 99f532a20e0d7119dfa2a0a68b103cf73eb088a1 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 16:40:20 -0700 Subject: [PATCH 49/64] always check added avatar entities once in updateAvatarEntities --- libraries/avatars/src/AvatarData.cpp | 6 ++---- libraries/avatars/src/AvatarData.h | 2 +- libraries/entities/src/EntityEditPacketSender.cpp | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index 5094327c9a..c7cc32ee3e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2692,7 +2692,7 @@ void AvatarData::setAttachmentsVariant(const QVariantList& variant) { const int MAX_NUM_AVATAR_ENTITIES = 42; -void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate) { +void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData) { _avatarEntitiesLock.withWriteLock([&] { AvatarEntityMap::iterator itr = _avatarEntityData.find(entityID); if (itr == _avatarEntityData.end()) { @@ -2704,9 +2704,7 @@ void AvatarData::updateAvatarEntity(const QUuid& entityID, const QByteArray& ent } }); - if (requiresTreeUpdate) { - _avatarEntityDataChanged = true; - } + _avatarEntityDataChanged = true; if (_clientTraitsHandler) { // we have a client traits handler, so we need to mark this instanced trait as changed diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 0f5e1d9ee4..53a2a69119 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -925,7 +925,7 @@ public: * @param {Uuid} entityID * @param {string} entityData */ - Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData, bool requiresTreeUpdate = true); + Q_INVOKABLE void updateAvatarEntity(const QUuid& entityID, const QByteArray& entityData); /**jsdoc * @function MyAvatar.clearAvatarEntity diff --git a/libraries/entities/src/EntityEditPacketSender.cpp b/libraries/entities/src/EntityEditPacketSender.cpp index d288126348..4aa66db227 100644 --- a/libraries/entities/src/EntityEditPacketSender.cpp +++ b/libraries/entities/src/EntityEditPacketSender.cpp @@ -74,7 +74,7 @@ void EntityEditPacketSender::queueEditAvatarEntityMessage(PacketType type, jsonProperties = QJsonDocument(jsonObject); QByteArray binaryProperties = jsonProperties.toBinaryData(); - _myAvatar->updateAvatarEntity(entityItemID, binaryProperties, false); + _myAvatar->updateAvatarEntity(entityItemID, binaryProperties); entity->setLastBroadcast(usecTimestampNow()); } From 4875738a05242ef57f9b3743b1d06d6ef6e09a49 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 17:06:13 -0700 Subject: [PATCH 50/64] always add to detached, flag update on full replace --- .../src/avatars-renderer/Avatar.cpp | 8 +++---- libraries/avatars/src/AvatarData.cpp | 22 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp index 150ecb6d44..1ec5ff595a 100644 --- a/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp +++ b/libraries/avatars-renderer/src/avatars-renderer/Avatar.cpp @@ -355,21 +355,21 @@ void Avatar::updateAvatarEntities() { stateItr.value().success = success; } - AvatarEntityIDs recentlyDettachedAvatarEntities = getAndClearRecentlyDetachedIDs(); - if (!recentlyDettachedAvatarEntities.empty()) { + AvatarEntityIDs recentlyDetachedAvatarEntities = getAndClearRecentlyDetachedIDs(); + if (!recentlyDetachedAvatarEntities.empty()) { // only lock this thread when absolutely necessary AvatarEntityMap avatarEntityData; _avatarEntitiesLock.withReadLock([&] { avatarEntityData = _avatarEntityData; }); - foreach (auto entityID, recentlyDettachedAvatarEntities) { + foreach (auto entityID, recentlyDetachedAvatarEntities) { if (!avatarEntityData.contains(entityID)) { entityTree->deleteEntity(entityID, true, true); } } // remove stale data hashes - foreach (auto entityID, recentlyDettachedAvatarEntities) { + foreach (auto entityID, recentlyDetachedAvatarEntities) { MapOfAvatarEntityDataHashes::iterator stateItr = _avatarEntityDataHashes.find(entityID); if (stateItr != _avatarEntityDataHashes.end()) { _avatarEntityDataHashes.erase(stateItr); diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index c7cc32ee3e..f32f883a7e 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -2721,16 +2721,12 @@ void AvatarData::clearAvatarEntity(const QUuid& entityID, bool requiresRemovalFr removedEntity = _avatarEntityData.remove(entityID); }); - if (removedEntity) { - if (requiresRemovalFromTree) { - insertDetachedEntityID(entityID); - } + insertDetachedEntityID(entityID); - if (_clientTraitsHandler) { - // we have a client traits handler, so we need to mark this removed instance trait as deleted - // so that changes are sent next frame - _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); - } + if (removedEntity && _clientTraitsHandler) { + // we have a client traits handler, so we need to mark this removed instance trait as deleted + // so that changes are sent next frame + _clientTraitsHandler->markInstancedTraitDeleted(AvatarTraits::AvatarEntity, entityID); } } @@ -2775,6 +2771,14 @@ void AvatarData::setAvatarEntityData(const AvatarEntityMap& avatarEntityData) { } } } + + if (_clientTraitsHandler) { + // if we have a client traits handler, flag any updated or created entities + // so that we send changes for them next frame + foreach (auto entityID, _avatarEntityData) { + _clientTraitsHandler->markInstancedTraitUpdated(AvatarTraits::AvatarEntity, entityID); + } + } } }); } From 3e2d4dc69618468395a4a80241353956813aafa5 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Mon, 20 Aug 2018 17:47:32 -0700 Subject: [PATCH 51/64] fixes and improvments addressing CR comments --- .../src/avatars/AvatarMixerClientData.cpp | 15 +++++++------- .../src/avatars/AvatarMixerClientData.h | 6 +++--- .../src/avatars/AvatarMixerSlave.cpp | 20 ++++++++----------- interface/src/Application.cpp | 5 +---- libraries/avatars/src/AssociatedTraitValues.h | 2 +- libraries/avatars/src/AvatarData.cpp | 6 ++---- libraries/avatars/src/AvatarTraits.h | 8 ++++---- libraries/avatars/src/ClientTraitsHandler.cpp | 12 +++++------ libraries/networking/src/NodeData.cpp | 2 -- libraries/networking/src/NodeData.h | 2 +- libraries/networking/src/udt/BasePacket.cpp | 6 ------ libraries/networking/src/udt/BasePacket.h | 2 +- 12 files changed, 34 insertions(+), 52 deletions(-) diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp index 5c84e3f755..b2490fc7b4 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.cpp +++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp @@ -49,7 +49,7 @@ void AvatarMixerClientData::queuePacket(QSharedPointer message, _packetQueue.push(message); } -int AvatarMixerClientData::processPackets(SlaveSharedData* slaveSharedData) { +int AvatarMixerClientData::processPackets(const SlaveSharedData& slaveSharedData) { int packetsProcessed = 0; SharedNodePointer node = _packetQueue.node; assert(_packetQueue.empty() || node); @@ -93,7 +93,8 @@ int AvatarMixerClientData::parseData(ReceivedMessage& message) { return _avatar->parseDataFromBuffer(message.readWithoutCopy(message.getBytesLeftToRead())); } -void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode) { +void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, + const SlaveSharedData& slaveSharedData, Node& sendingNode) { // pull the trait version from the message AvatarTraits::TraitVersion packetTraitVersion; message.readPrimitive(&packetTraitVersion); @@ -155,9 +156,9 @@ void AvatarMixerClientData::processSetTraitsMessage(ReceivedMessage& message, Sl } } -void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *slaveSharedData, Node& sendingNode, +void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(const SlaveSharedData &slaveSharedData, Node& sendingNode, AvatarTraits::TraitVersion traitVersion) { - const auto& whitelist = slaveSharedData->skeletonURLWhitelist; + const auto& whitelist = slaveSharedData.skeletonURLWhitelist; if (!whitelist.isEmpty()) { bool inWhitelist = false; @@ -179,11 +180,11 @@ void AvatarMixerClientData::checkSkeletonURLAgainstWhitelist(SlaveSharedData *sl if (!inWhitelist) { // make sure we're not unecessarily overriding the default avatar with the default avatar - if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData->skeletonReplacementURL) { + if (_avatar->getWireSafeSkeletonModelURL() != slaveSharedData.skeletonReplacementURL) { // we need to change this avatar's skeleton URL, and send them a traits packet informing them of the change qDebug() << "Overwriting avatar URL" << _avatar->getWireSafeSkeletonModelURL() - << "to replacement" << slaveSharedData->skeletonReplacementURL << "for" << sendingNode.getUUID(); - _avatar->setSkeletonModelURL(slaveSharedData->skeletonReplacementURL); + << "to replacement" << slaveSharedData.skeletonReplacementURL << "for" << sendingNode.getUUID(); + _avatar->setSkeletonModelURL(slaveSharedData.skeletonReplacementURL); auto packet = NLPacket::create(PacketType::SetAvatarTraits, -1, true); diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h index 02b37e08f8..1c2694af48 100644 --- a/assignment-client/src/avatars/AvatarMixerClientData.h +++ b/assignment-client/src/avatars/AvatarMixerClientData.h @@ -116,10 +116,10 @@ public: QVector& getLastOtherAvatarSentJoints(QUuid otherAvatar) { return _lastOtherAvatarSentJoints[otherAvatar]; } void queuePacket(QSharedPointer message, SharedNodePointer node); - int processPackets(SlaveSharedData* slaveSharedData); // returns number of packets processed + int processPackets(const SlaveSharedData& slaveSharedData); // returns number of packets processed - void processSetTraitsMessage(ReceivedMessage& message, SlaveSharedData* slaveSharedData, Node& sendingNode); - void checkSkeletonURLAgainstWhitelist(SlaveSharedData* slaveSharedData, Node& sendingNode, + void processSetTraitsMessage(ReceivedMessage& message, const SlaveSharedData& slaveSharedData, Node& sendingNode); + void checkSkeletonURLAgainstWhitelist(const SlaveSharedData& slaveSharedData, Node& sendingNode, AvatarTraits::TraitVersion traitVersion); using TraitsCheckTimestamp = std::chrono::steady_clock::time_point; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 14818870d1..f347ff1f10 100644 --- a/assignment-client/src/avatars/AvatarMixerSlave.cpp +++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp @@ -59,7 +59,7 @@ void AvatarMixerSlave::processIncomingPackets(const SharedNodePointer& node) { auto nodeData = dynamic_cast(node->getLinkedData()); if (nodeData) { _stats.nodesProcessed++; - _stats.packetsProcessed += nodeData->processPackets(_sharedData); + _stats.packetsProcessed += nodeData->processPackets(*_sharedData); } auto end = usecTimestampNow(); _stats.processIncomingPacketsElapsedTime += (end - start); @@ -109,19 +109,15 @@ qint64 AvatarMixerSlave::addChangedTraitsToBulkPacket(AvatarMixerClientData* lis auto traitType = static_cast(std::distance(lastReceivedVersions.simpleCBegin(), simpleReceivedIt)); - // we need to double check that this is actually a simple trait type, since the instanced - // trait types are in the simple vector for access efficiency - if (AvatarTraits::isSimpleTrait(traitType)) { - auto lastReceivedVersion = *simpleReceivedIt; - auto& lastSentVersionRef = lastSentVersions[traitType]; + auto lastReceivedVersion = *simpleReceivedIt; + auto& lastSentVersionRef = lastSentVersions[traitType]; - if (lastReceivedVersions[traitType] > lastSentVersionRef) { - // there is an update to this trait, add it to the traits packet - bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); + if (lastReceivedVersions[traitType] > lastSentVersionRef) { + // there is an update to this trait, add it to the traits packet + bytesWritten += sendingAvatar->packTrait(traitType, traitsPacketList, lastReceivedVersion); - // update the last sent version - lastSentVersionRef = lastReceivedVersion; - } + // update the last sent version + lastSentVersionRef = lastReceivedVersion; } ++simpleReceivedIt; diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 60af79bfda..2d76791a5e 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5488,10 +5488,7 @@ void Application::update(float deltaTime) { // we haven't yet enabled physics. we wait until we think we have all the collision information // for nearby entities before starting bullet up. quint64 now = usecTimestampNow(); - // Check for flagged EntityData having arrived. - auto entityTreeRenderer = getEntities(); - if (isServerlessMode() || - (entityTreeRenderer && _octreeProcessor.octreeSequenceIsComplete(entityTreeRenderer->getLastOctreeMessageSequence()) )) { + if (isServerlessMode() || _octreeProcessor.isLoadSequenceComplete()) { // we've received a new full-scene octree stats packet, or it's been long enough to try again anyway _lastPhysicsCheckTime = now; _fullSceneCounterAtLastPhysicsCheck = _fullSceneReceivedCounter; diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h index b2c0197e5c..e8bbce891d 100644 --- a/libraries/avatars/src/AssociatedTraitValues.h +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -18,7 +18,7 @@ namespace AvatarTraits { template class AssociatedTraitValues { public: - AssociatedTraitValues() : _simpleTypes(TotalTraitTypes, defaultValue) {} + AssociatedTraitValues() : _simpleTypes(FirstInstancedTrait, defaultValue) {} void insert(TraitType type, T value) { _simpleTypes[type] = value; } void erase(TraitType type) { _simpleTypes[type] = defaultValue; } diff --git a/libraries/avatars/src/AvatarData.cpp b/libraries/avatars/src/AvatarData.cpp index f32f883a7e..c1ec19b307 100644 --- a/libraries/avatars/src/AvatarData.cpp +++ b/libraries/avatars/src/AvatarData.cpp @@ -1834,8 +1834,7 @@ qint64 AvatarData::packTrait(AvatarTraits::TraitType traitType, ExtendedIODevice bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { - AvatarTraits::TraitVersion typedVersion = traitVersion; - bytesWritten += destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(traitVersion); } if (traitType == AvatarTraits::SkeletonModelURL) { @@ -1857,8 +1856,7 @@ qint64 AvatarData::packTraitInstance(AvatarTraits::TraitType traitType, AvatarTr bytesWritten += destination.writePrimitive(traitType); if (traitVersion > AvatarTraits::DEFAULT_TRAIT_VERSION) { - AvatarTraits::TraitVersion typedVersion = traitVersion; - bytesWritten += destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(traitVersion); } bytesWritten += destination.write(traitInstanceID.toRfc4122()); diff --git a/libraries/avatars/src/AvatarTraits.h b/libraries/avatars/src/AvatarTraits.h index 16e897319b..f0c807a432 100644 --- a/libraries/avatars/src/AvatarTraits.h +++ b/libraries/avatars/src/AvatarTraits.h @@ -22,14 +22,15 @@ namespace AvatarTraits { enum TraitType : int8_t { NullTrait = -1, SkeletonModelURL, - AvatarEntity, + FirstInstancedTrait, + AvatarEntity = FirstInstancedTrait, TotalTraitTypes }; using TraitInstanceID = QUuid; inline bool isSimpleTrait(TraitType traitType) { - return traitType == SkeletonModelURL; + return traitType > NullTrait && traitType < FirstInstancedTrait; } using TraitVersion = int32_t; @@ -46,8 +47,7 @@ namespace AvatarTraits { bytesWritten += destination.writePrimitive(traitType); if (traitVersion > DEFAULT_TRAIT_VERSION) { - AvatarTraits::TraitVersion typedVersion = traitVersion; - bytesWritten += destination.writePrimitive(typedVersion); + bytesWritten += destination.writePrimitive(traitVersion); } bytesWritten += destination.write(instanceID.toRfc4122()); diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index a31808a916..151b2a3809 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -68,14 +68,12 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { // we double check that it is a simple iterator here auto traitType = static_cast(std::distance(traitStatusesCopy.simpleCBegin(), simpleIt)); - if (AvatarTraits::isSimpleTrait(traitType)) { - if (_shouldPerformInitialSend || *simpleIt == Updated) { - if (traitType == AvatarTraits::SkeletonModelURL) { - _owningAvatar->packTrait(traitType, *traitsPacketList); + if (_shouldPerformInitialSend || *simpleIt == Updated) { + if (traitType == AvatarTraits::SkeletonModelURL) { + _owningAvatar->packTrait(traitType, *traitsPacketList); - // keep track of our skeleton version in case we get an override back - _currentSkeletonVersion = _currentTraitVersion; - } + // keep track of our skeleton version in case we get an override back + _currentSkeletonVersion = _currentTraitVersion; } } diff --git a/libraries/networking/src/NodeData.cpp b/libraries/networking/src/NodeData.cpp index dd926852cd..d22b970154 100644 --- a/libraries/networking/src/NodeData.cpp +++ b/libraries/networking/src/NodeData.cpp @@ -18,5 +18,3 @@ NodeData::NodeData(const QUuid& nodeID, NetworkPeer::LocalID nodeLocalID) : { } - -NodeData::~NodeData() {} diff --git a/libraries/networking/src/NodeData.h b/libraries/networking/src/NodeData.h index ffbc0c2376..b4cb87d0c2 100644 --- a/libraries/networking/src/NodeData.h +++ b/libraries/networking/src/NodeData.h @@ -26,7 +26,7 @@ class NodeData : public QObject { Q_OBJECT public: NodeData(const QUuid& nodeID = QUuid(), NetworkPeer::LocalID localID = NetworkPeer::NULL_LOCAL_ID); - virtual ~NodeData() = 0; + virtual ~NodeData() = default; virtual int parseData(ReceivedMessage& message) { return 0; } const QUuid& getNodeID() const { return _nodeID; } diff --git a/libraries/networking/src/udt/BasePacket.cpp b/libraries/networking/src/udt/BasePacket.cpp index 92ccdd6117..12a174b7d3 100644 --- a/libraries/networking/src/udt/BasePacket.cpp +++ b/libraries/networking/src/udt/BasePacket.cpp @@ -77,12 +77,6 @@ BasePacket::BasePacket(std::unique_ptr data, qint64 size, const HifiSock } -BasePacket::BasePacket(const BasePacket& other) : - ExtendedIODevice() -{ - *this = other; -} - BasePacket& BasePacket::operator=(const BasePacket& other) { _packetSize = other._packetSize; _packet = std::unique_ptr(new char[_packetSize]); diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 9c3244e08b..9054131337 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -88,7 +88,7 @@ public: protected: BasePacket(qint64 size); BasePacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); - BasePacket(const BasePacket& other); + BasePacket(const BasePacket& other) { *this = other; } BasePacket& operator=(const BasePacket& other); BasePacket(BasePacket&& other); BasePacket& operator=(BasePacket&& other); From 25ed166f07631f2b6afdce3b2ed625b0cdb2f771 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 11:49:48 -0700 Subject: [PATCH 52/64] fix logic for initial send of deleted trait instance --- libraries/avatars/src/ClientTraitsHandler.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/libraries/avatars/src/ClientTraitsHandler.cpp b/libraries/avatars/src/ClientTraitsHandler.cpp index 151b2a3809..c4073cb86a 100644 --- a/libraries/avatars/src/ClientTraitsHandler.cpp +++ b/libraries/avatars/src/ClientTraitsHandler.cpp @@ -83,10 +83,12 @@ void ClientTraitsHandler::sendChangedTraitsToMixer() { auto instancedIt = traitStatusesCopy.instancedCBegin(); while (instancedIt != traitStatusesCopy.instancedCEnd()) { for (auto& instanceIDValuePair : instancedIt->instances) { - if (_shouldPerformInitialSend || instanceIDValuePair.value == Updated) { - // this is a changed trait we need to send, ask the owning avatar to pack it + if ((_shouldPerformInitialSend && instanceIDValuePair.value != Deleted) + || instanceIDValuePair.value == Updated) { + // this is a changed trait we need to send or we haven't send out trait information yet + // ask the owning avatar to pack it _owningAvatar->packTraitInstance(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); - } else if (instanceIDValuePair.value == Deleted) { + } else if (!_shouldPerformInitialSend && instanceIDValuePair.value == Deleted) { // pack delete for this trait instance AvatarTraits::packInstancedTraitDelete(instancedIt->traitType, instanceIDValuePair.id, *traitsPacketList); From 8c58ae4e60b3628077cd72e70568254611e95eb7 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 11:56:28 -0700 Subject: [PATCH 53/64] fix insert of second trait instance --- libraries/avatars/src/AssociatedTraitValues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h index e8bbce891d..fb780e252e 100644 --- a/libraries/avatars/src/AssociatedTraitValues.h +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -123,7 +123,7 @@ namespace AvatarTraits { auto it = instancesForTrait(traitType); if (it != _instancedTypes.end()) { - auto instancesVector = it->instances; + auto& instancesVector = it->instances; auto instanceIt = std::find_if(instancesVector.begin(), instancesVector.end(), [instanceID](InstanceIDValuePair& idValuePair){ return idValuePair.id == instanceID; From 8226ffb916a111bbc255bfdc6547502616a2a397 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 11:57:21 -0700 Subject: [PATCH 54/64] fix ref to instances vector for instance erase --- libraries/avatars/src/AssociatedTraitValues.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/avatars/src/AssociatedTraitValues.h b/libraries/avatars/src/AssociatedTraitValues.h index fb780e252e..cf1edef7f7 100644 --- a/libraries/avatars/src/AssociatedTraitValues.h +++ b/libraries/avatars/src/AssociatedTraitValues.h @@ -143,7 +143,7 @@ namespace AvatarTraits { auto it = instancesForTrait(traitType); if (it != _instancedTypes.end()) { - auto instancesVector = it->instances; + auto& instancesVector = it->instances; instancesVector.erase(std::remove_if(instancesVector.begin(), instancesVector.end(), [&instanceID](InstanceIDValuePair& idValuePair){ From 5ae604f341510caba2efb639e774118eda59fc97 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 12:14:32 -0700 Subject: [PATCH 55/64] REmove the texture Transform for deferred_light slv which then makes it work correctly on AMD --- .../gpu-gl-common/src/gpu/gl/GLBackendShader.cpp | 4 +++- .../render-utils/src/DeferredLightingEffect.cpp | 14 ++++++++------ libraries/render-utils/src/deferred_light.slv | 6 +++--- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 88d2e8609f..4e00fb4871 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -212,6 +212,8 @@ GLShader* GLBackend::compileBackendProgram(const Shader& program, const Shader:: glprogram = ::gl::buildProgram(shaderGLObjects); if (!::gl::linkProgram(glprogram, compilationLogs[version].message)) { + qCWarning(gpugllogging) << "GLBackend::compileBackendProgram - Program didn't link:\n" << compilationLogs[version].message.c_str(); + compilationLogs[version].compiled = false; glDeleteProgram(glprogram); glprogram = 0; return nullptr; @@ -254,7 +256,7 @@ GLint GLBackend::getRealUniformLocation(GLint location) const { // uniforms. If someone is requesting a uniform that isn't in the remapping structure // that's a bug from the calling code, because it means that location wasn't in the // reflection - qWarning() << "Unexpected location requested for shader"; + qWarning() << "Unexpected location requested for shader: #" << location; return INVALID_UNIFORM_INDEX; } return itr->second; diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 2995d17f03..fb73ee0dfc 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -135,7 +135,7 @@ static void loadLightProgram(int programId, bool lightVolume, gpu::PipelinePoint if (lightVolume) { PrepareStencil::testShape(*state); - state->setCullMode(gpu::State::CULL_BACK); + state->setCullMode(gpu::State::CULL_NONE); //state->setCullMode(gpu::State::CULL_FRONT); //state->setDepthTest(true, false, gpu::GREATER_EQUAL); //state->setDepthClampEnable(true); @@ -496,9 +496,10 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, batch.setPipeline(program); } + // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) // Adjust the texcoordTransform in the case we are rendeirng a sub region(mini mirror) - auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport); - batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); + // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport); + // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); // Setup the global lighting deferredLightingEffect->setupKeyLightBatch(args, batch); @@ -560,7 +561,8 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext batch.setViewportTransform(viewport); batch.setStateScissorRect(viewport); - auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport); + // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) + // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport); auto& lightIndices = lightClusters->_visibleLightIndices; @@ -569,14 +571,14 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext // Local light pipeline batch.setPipeline(deferredLightingEffect->_localLight); - batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); + // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); // Draw outline as well ? if (lightingModel->isShowLightContourEnabled()) { batch.setPipeline(deferredLightingEffect->_localLightOutline); - batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); + // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); } diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 654e9a69b2..7468be9655 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,7 +16,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; -layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; +//layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; void main(void) { const float depth = 1.0; @@ -30,8 +30,8 @@ void main(void) { _texCoord01.xy = (pos.xy + 1.0) * 0.5; - _texCoord01.xy *= texcoordFrameTransform.zw; - _texCoord01.xy += texcoordFrameTransform.xy; + // _texCoord01.xy *= texcoordFrameTransform.zw; + // _texCoord01.xy += texcoordFrameTransform.xy; gl_Position = pos; } From a177e49877cb97bea92106841b67d90ea26be484 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 12:18:11 -0700 Subject: [PATCH 56/64] Revert "send attachment clears through setAvatarEntityData" This reverts commit 25876bca6397ed1b9036ef5a444beee08e78c02f. --- interface/src/AvatarBookmarks.cpp | 15 +++++++++++++-- interface/src/avatar/MyAvatar.cpp | 19 ++++++++++++++++--- interface/src/avatar/MyAvatar.h | 2 +- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/interface/src/AvatarBookmarks.cpp b/interface/src/AvatarBookmarks.cpp index c7c87aee3e..6afab71c90 100644 --- a/interface/src/AvatarBookmarks.cpp +++ b/interface/src/AvatarBookmarks.cpp @@ -151,7 +151,13 @@ bool isWearableEntity(const EntityItemPointer& entity) { void AvatarBookmarks::updateAvatarEntities(const QVariantList &avatarEntities) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->clearAvatarEntities(); + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + myAvatar->removeAvatarEntities([&](const QUuid& entityID) { + auto entity = entityTree->findEntityByID(entityID); + return entity && isWearableEntity(entity); + }); + addAvatarEntities(avatarEntities); } @@ -174,7 +180,12 @@ void AvatarBookmarks::loadBookmark(const QString& bookmarkName) { if (!bookmark.empty()) { auto myAvatar = DependencyManager::get()->getMyAvatar(); - myAvatar->clearAvatarEntities(); + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + myAvatar->removeAvatarEntities([&](const QUuid& entityID) { + auto entity = entityTree->findEntityByID(entityID); + return entity && isWearableEntity(entity); + }); const QString& avatarUrl = bookmark.value(ENTRY_AVATAR_URL, "").toString(); myAvatar->useFullAvatarURL(avatarUrl); qCDebug(interfaceapp) << "Avatar On " << avatarUrl; diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index 709ddfd17a..deef69d980 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -1703,6 +1703,21 @@ void MyAvatar::setSkeletonModelURL(const QUrl& skeletonModelURL) { emit skeletonChanged(); } +void MyAvatar::removeAvatarEntities(const std::function& condition) { + auto treeRenderer = DependencyManager::get(); + EntityTreePointer entityTree = treeRenderer ? treeRenderer->getTree() : nullptr; + if (entityTree) { + entityTree->withWriteLock([&] { + AvatarEntityMap avatarEntities = getAvatarEntityData(); + for (auto entityID : avatarEntities.keys()) { + if (!condition || condition(entityID)) { + entityTree->deleteEntity(entityID, true, true); + } + } + }); + } +} + QVariantList MyAvatar::getAvatarEntitiesVariant() { QVariantList avatarEntitiesData; QScriptEngine scriptEngine; @@ -2099,9 +2114,7 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) attachmentDataToEntityProperties(data, properties); newEntitiesProperties.push_back(properties); } - - clearAvatarEntities(); - + removeAvatarEntities(); for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); } diff --git a/interface/src/avatar/MyAvatar.h b/interface/src/avatar/MyAvatar.h index 247a0c79c8..ba6348cc22 100644 --- a/interface/src/avatar/MyAvatar.h +++ b/interface/src/avatar/MyAvatar.h @@ -931,7 +931,7 @@ public: * @returns {object[]} */ Q_INVOKABLE QVariantList getAvatarEntitiesVariant(); - void clearAvatarEntities() { setAvatarEntityData(AvatarEntityMap()); } + void removeAvatarEntities(const std::function& condition = {}); /**jsdoc * @function MyAvatar.isFlying From 1723f3d3d88b94e4de62d62e960437220cf670b9 Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 12:21:29 -0700 Subject: [PATCH 57/64] don't use entity tree to clear all avatar entities --- interface/src/avatar/MyAvatar.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp index deef69d980..b30f05225e 100755 --- a/interface/src/avatar/MyAvatar.cpp +++ b/interface/src/avatar/MyAvatar.cpp @@ -2114,7 +2114,10 @@ void MyAvatar::setAttachmentData(const QVector& attachmentData) attachmentDataToEntityProperties(data, properties); newEntitiesProperties.push_back(properties); } - removeAvatarEntities(); + + // clear any existing avatar entities + setAvatarEntityData(AvatarEntityMap()); + for (auto& properties : newEntitiesProperties) { DependencyManager::get()->addEntity(properties, true); } From b7d8b92fb5e2795402401ae35eb5618873d067e7 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 12:25:27 -0700 Subject: [PATCH 58/64] Add comment in shader and fix bad cull mode --- libraries/render-utils/src/DeferredLightingEffect.cpp | 2 +- libraries/render-utils/src/deferred_light.slv | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index fb73ee0dfc..91d196f896 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -135,7 +135,7 @@ static void loadLightProgram(int programId, bool lightVolume, gpu::PipelinePoint if (lightVolume) { PrepareStencil::testShape(*state); - state->setCullMode(gpu::State::CULL_NONE); + state->setCullMode(gpu::State::CULL_BACK); //state->setCullMode(gpu::State::CULL_FRONT); //state->setDepthTest(true, false, gpu::GREATER_EQUAL); //state->setDepthClampEnable(true); diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 7468be9655..0bc7a0f807 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,6 +16,7 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; +// NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) //layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; void main(void) { From 8c5aab7fa04577836fdcbfed4cd2564b6091a574 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 12:30:31 -0700 Subject: [PATCH 59/64] tabs! --- libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp index 4e00fb4871..7267e29be2 100644 --- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp +++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendShader.cpp @@ -256,7 +256,7 @@ GLint GLBackend::getRealUniformLocation(GLint location) const { // uniforms. If someone is requesting a uniform that isn't in the remapping structure // that's a bug from the calling code, because it means that location wasn't in the // reflection - qWarning() << "Unexpected location requested for shader: #" << location; + qWarning() << "Unexpected location requested for shader: #" << location; return INVALID_UNIFORM_INDEX; } return itr->second; From 7d8db482a3b551aef6b9dbcea9b8de90c41774ab Mon Sep 17 00:00:00 2001 From: Stephen Birarda Date: Tue, 21 Aug 2018 13:21:21 -0700 Subject: [PATCH 60/64] put back ExtendedIODevice ctor for ubuntu warning --- libraries/networking/src/udt/BasePacket.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/networking/src/udt/BasePacket.h b/libraries/networking/src/udt/BasePacket.h index 9054131337..4981cb4720 100644 --- a/libraries/networking/src/udt/BasePacket.h +++ b/libraries/networking/src/udt/BasePacket.h @@ -88,7 +88,7 @@ public: protected: BasePacket(qint64 size); BasePacket(std::unique_ptr data, qint64 size, const HifiSockAddr& senderSockAddr); - BasePacket(const BasePacket& other) { *this = other; } + BasePacket(const BasePacket& other) : ExtendedIODevice() { *this = other; } BasePacket& operator=(const BasePacket& other); BasePacket(BasePacket&& other); BasePacket& operator=(BasePacket&& other); From cfb820762d1e670d2b3ff17cd112f41926691213 Mon Sep 17 00:00:00 2001 From: Alexander Ivash Date: Wed, 22 Aug 2018 00:42:01 +0300 Subject: [PATCH 61/64] bulk adjusting to coding guidelines --- interface/resources/qml/hifi/AvatarApp.qml | 54 +++++++------- .../qml/hifi/avatarapp/AdjustWearables.qml | 50 ++++++------- .../qml/hifi/avatarapp/AvatarsModel.qml | 74 ++++++++++--------- .../hifi/avatarapp/CreateFavoriteDialog.qml | 8 +- .../qml/hifi/avatarapp/DialogButtons.qml | 2 +- .../qml/hifi/avatarapp/MessageBox.qml | 6 +- .../qml/hifi/avatarapp/MessageBoxes.qml | 27 ++++--- .../resources/qml/hifi/avatarapp/Settings.qml | 6 +- .../qml/hifi/avatarapp/TransparencyMask.qml | 2 +- 9 files changed, 123 insertions(+), 106 deletions(-) diff --git a/interface/resources/qml/hifi/AvatarApp.qml b/interface/resources/qml/hifi/AvatarApp.qml index 217525498d..d8eae311d8 100644 --- a/interface/resources/qml/hifi/AvatarApp.qml +++ b/interface/resources/qml/hifi/AvatarApp.qml @@ -57,7 +57,7 @@ Rectangle { try { var marketResponse = JSON.parse(xmlhttp.responseText.trim()) - if(marketResponse.status === 'success') { + if (marketResponse.status === 'success') { avatar.modelName = marketResponse.data.title; } } @@ -72,14 +72,14 @@ Rectangle { function getAvatarModelName() { - if(currentAvatar === null) { + if (currentAvatar === null) { return ''; } - if(currentAvatar.modelName !== undefined) { + if (currentAvatar.modelName !== undefined) { return currentAvatar.modelName; } else { var marketId = allAvatars.extractMarketId(currentAvatar.avatarUrl); - if(marketId !== '') { + if (marketId !== '') { fetchAvatarModelName(marketId, currentAvatar); } } @@ -103,51 +103,51 @@ Rectangle { property url externalAvatarThumbnailUrl: '../../images/avatarapp/guy-in-circle.svg' function fromScript(message) { - if(message.method === 'initialize') { + if (message.method === 'initialize') { jointNames = message.data.jointNames; emitSendToScript({'method' : getAvatarsMethod}); - } else if(message.method === 'wearableUpdated') { + } else if (message.method === 'wearableUpdated') { adjustWearables.refreshWearable(message.entityID, message.wearableIndex, message.properties, message.updateUI); - } else if(message.method === 'wearablesUpdated') { + } else if (message.method === 'wearablesUpdated') { var wearablesModel = currentAvatar.wearables; wearablesModel.clear(); message.wearables.forEach(function(wearable) { wearablesModel.append(wearable); }); adjustWearables.refresh(currentAvatar); - } else if(message.method === 'scaleChanged') { + } else if (message.method === 'scaleChanged') { currentAvatar.avatarScale = message.value; updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'externalAvatarApplied') { + } else if (message.method === 'externalAvatarApplied') { currentAvatar.avatarUrl = message.avatarURL; currentAvatar.thumbnailUrl = allAvatars.makeThumbnailUrl(message.avatarURL); currentAvatar.entry.avatarUrl = currentAvatar.avatarUrl; currentAvatar.modelName = undefined; updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'settingChanged') { + } else if (message.method === 'settingChanged') { currentAvatarSettings[message.name] = message.value; - } else if(message.method === 'changeSettings') { + } else if (message.method === 'changeSettings') { currentAvatarSettings = message.settings; - } else if(message.method === 'bookmarkLoaded') { + } else if (message.method === 'bookmarkLoaded') { setCurrentAvatar(message.data.currentAvatar, message.data.name); var avatarIndex = allAvatars.findAvatarIndex(currentAvatar.name); allAvatars.move(avatarIndex, 0, 1); view.setPage(0); - } else if(message.method === 'bookmarkAdded') { + } else if (message.method === 'bookmarkAdded') { var avatar = allAvatars.findAvatar(message.bookmarkName); - if(avatar !== undefined) { + if (avatar !== undefined) { var avatarObject = allAvatars.makeAvatarObject(message.bookmark, message.bookmarkName); for(var prop in avatarObject) { avatar[prop] = avatarObject[prop]; } - if(currentAvatar.name === message.bookmarkName) { + if (currentAvatar.name === message.bookmarkName) { currentAvatar = currentAvatarModel.makeAvatarEntry(avatarObject); } } else { allAvatars.addAvatarEntry(message.bookmark, message.bookmarkName); } updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'bookmarkDeleted') { + } else if (message.method === 'bookmarkDeleted') { pageOfAvatars.isUpdating = true; var index = pageOfAvatars.findAvatarIndex(message.name); @@ -159,15 +159,16 @@ Rectangle { var itemsOnPage = pageOfAvatars.count; var newItemIndex = view.currentPage * view.itemsPerPage + itemsOnPage; - if(newItemIndex <= (allAvatars.count - 1)) { + if (newItemIndex <= (allAvatars.count - 1)) { pageOfAvatars.append(allAvatars.get(newItemIndex)); } else { - if(!pageOfAvatars.hasGetAvatars()) + if (!pageOfAvatars.hasGetAvatars()) { pageOfAvatars.appendGetAvatars(); + } } pageOfAvatars.isUpdating = false; - } else if(message.method === getAvatarsMethod) { + } else if (message.method === getAvatarsMethod) { var getAvatarsData = message.data; allAvatars.populate(getAvatarsData.bookmarks); setCurrentAvatar(getAvatarsData.currentAvatar, ''); @@ -175,16 +176,16 @@ Rectangle { currentAvatarSettings = getAvatarsData.currentAvatarSettings; updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'updateAvatarInBookmarks') { + } else if (message.method === 'updateAvatarInBookmarks') { updateCurrentAvatarInBookmarks(currentAvatar); - } else if(message.method === 'selectAvatarEntity') { + } else if (message.method === 'selectAvatarEntity') { adjustWearables.selectWearableByID(message.entityID); } } function updateCurrentAvatarInBookmarks(avatar) { var bookmarkAvatarIndex = allAvatars.findAvatarIndexByValue(avatar); - if(bookmarkAvatarIndex === -1) { + if (bookmarkAvatarIndex === -1) { avatar.name = ''; view.setPage(0); } else { @@ -597,8 +598,9 @@ Rectangle { pageOfAvatars.append(avatarItem); } - if(pageOfAvatars.count !== itemsPerPage) + if (pageOfAvatars.count !== itemsPerPage) { pageOfAvatars.appendGetAvatars(); + } currentPage = pageIndex; pageOfAvatars.isUpdating = false; @@ -619,7 +621,7 @@ Rectangle { } function removeGetAvatars() { - if(hasGetAvatars()) { + if (hasGetAvatars()) { remove(count - 1) } } @@ -687,13 +689,13 @@ Rectangle { hoverEnabled: enabled onClicked: { - if(isInManageState) { + if (isInManageState) { var currentItem = delegateRoot.GridView.view.model.get(index); popup.showDeleteFavorite(currentItem.name, function() { view.deleteAvatar(currentItem); }); } else { - if(delegateRoot.GridView.view.currentIndex !== index) { + if (delegateRoot.GridView.view.currentIndex !== index) { var currentItem = delegateRoot.GridView.view.model.get(index); popup.showLoadFavorite(currentItem.name, function() { view.selectAvatar(currentItem); diff --git a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml index bec037fdf2..e80dab60c2 100644 --- a/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml +++ b/interface/resources/qml/hifi/avatarapp/AdjustWearables.qml @@ -43,10 +43,10 @@ Rectangle { wearablesCombobox.model.clear(); wearablesCombobox.currentIndex = -1; - for(var i = 0; i < avatar.wearables.count; ++i) { + for (var i = 0; i < avatar.wearables.count; ++i) { var wearable = avatar.wearables.get(i).properties; - for(var j = (wearable.modelURL.length - 1); j >= 0; --j) { - if(wearable.modelURL[j] === '/') { + for (var j = (wearable.modelURL.length - 1); j >= 0; --j) { + if (wearable.modelURL[j] === '/') { wearable.text = wearable.modelURL.substring(j + 1); break; } @@ -54,34 +54,34 @@ Rectangle { wearablesCombobox.model.append(wearable); } - if(wearablesCombobox.model.count !== 0) { + if (wearablesCombobox.model.count !== 0) { wearablesCombobox.currentIndex = 0; } } function refreshWearable(wearableID, wearableIndex, properties, updateUI) { - if(wearableIndex === -1) { + if (wearableIndex === -1) { wearableIndex = wearablesCombobox.model.findIndexById(wearableID); } var wearable = wearablesCombobox.model.get(wearableIndex); - if(!wearable) { + if (!wearable) { return; } var wearableModelItemProperties = wearablesModel.get(wearableIndex).properties; - for(var prop in properties) { + for (var prop in properties) { wearable[prop] = properties[prop]; wearableModelItemProperties[prop] = wearable[prop]; - if(updateUI) { - if(prop === 'localPosition') { + if (updateUI) { + if (prop === 'localPosition') { positionVector.set(wearable[prop]); - } else if(prop === 'localRotationAngles') { + } else if (prop === 'localRotationAngles') { rotationVector.set(wearable[prop]); - } else if(prop === 'dimensions') { + } else if (prop === 'dimensions') { scalespinner.set(wearable[prop].x / wearable.naturalDimensions.x); } } @@ -95,9 +95,9 @@ Rectangle { } function selectWearableByID(entityID) { - for(var i = 0; i < wearablesCombobox.model.count; ++i) { + for (var i = 0; i < wearablesCombobox.model.count; ++i) { var wearable = wearablesCombobox.model.get(i); - if(wearable.id === entityID) { + if (wearable.id === entityID) { wearablesCombobox.currentIndex = i; break; } @@ -220,9 +220,9 @@ Rectangle { model: ListModel { function findIndexById(id) { - for(var i = 0; i < count; ++i) { + for (var i = 0; i < count; ++i) { var wearable = get(i); - if(wearable.id === id) { + if (wearable.id === id) { return i; } } @@ -245,7 +245,7 @@ Rectangle { jointsCombobox.set(joint); isSoft.set(soft); - if(currentWearable) { + if (currentWearable) { wearableSelected(currentWearable.id); } } @@ -298,7 +298,7 @@ Rectangle { } onCurrentIndexChanged: { - if(notify) notifyJointChanged(); + if (notify) notifyJointChanged(); } } } @@ -352,9 +352,9 @@ Rectangle { property bool notify: false; - onXvalueChanged: if(notify) notifyPositionChanged(); - onYvalueChanged: if(notify) notifyPositionChanged(); - onZvalueChanged: if(notify) notifyPositionChanged(); + onXvalueChanged: if (notify) notifyPositionChanged(); + onYvalueChanged: if (notify) notifyPositionChanged(); + onZvalueChanged: if (notify) notifyPositionChanged(); decimals: 2 realFrom: -10 @@ -412,9 +412,9 @@ Rectangle { property bool notify: false; - onXvalueChanged: if(notify) notifyRotationChanged(); - onYvalueChanged: if(notify) notifyRotationChanged(); - onZvalueChanged: if(notify) notifyRotationChanged(); + onXvalueChanged: if (notify) notifyRotationChanged(); + onYvalueChanged: if (notify) notifyRotationChanged(); + onZvalueChanged: if (notify) notifyRotationChanged(); decimals: 0 realFrom: -180 @@ -453,7 +453,7 @@ Rectangle { property bool notify: false; - onCheckedChanged: if(notify) notifyIsSoftChanged(); + onCheckedChanged: if (notify) notifyIsSoftChanged(); } Column { @@ -482,7 +482,7 @@ Rectangle { colorScheme: hifi.colorSchemes.light property bool notify: false; - onRealValueChanged: if(notify) notifyScaleChanged(); + onRealValueChanged: if (notify) notifyScaleChanged(); function set(value) { notify = false; diff --git a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml index f9a231ec48..bfd66f1a30 100644 --- a/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml +++ b/interface/resources/qml/hifi/avatarapp/AvatarsModel.qml @@ -24,8 +24,9 @@ ListModel { function makeThumbnailUrl(avatarUrl) { var marketId = extractMarketId(avatarUrl); - if(marketId === '') + if (marketId === '') { return ''; + } var avatarThumbnailUrl = "https://hifi-metaverse.s3-us-west-1.amazonaws.com/marketplace/previews/%marketId%/large/hifi-mp-%marketId%.jpg" .split('%marketId%').join(marketId); @@ -57,7 +58,7 @@ ListModel { function populate(bookmarks) { clear(); - for(var avatarName in bookmarks) { + for (var avatarName in bookmarks) { var avatar = bookmarks[avatarName]; var avatarEntry = makeAvatarObject(avatar, avatarName); @@ -66,19 +67,19 @@ ListModel { } function arraysAreEqual(a1, a2, comparer) { - if(Array.isArray(a1) && Array.isArray(a2)) { - if(a1.length !== a2.length) { + if (Array.isArray(a1) && Array.isArray(a2)) { + if (a1.length !== a2.length) { return false; } - for(var i = 0; i < a1.length; ++i) { - if(!comparer(a1[i], a2[i])) { + for (var i = 0; i < a1.length; ++i) { + if (!comparer(a1[i], a2[i])) { return false; } } - } else if(Array.isArray(a1)) { + } else if (Array.isArray(a1)) { return a1.length === 0; - } else if(Array.isArray(a2)) { + } else if (Array.isArray(a2)) { return a2.length === 0; } @@ -86,26 +87,26 @@ ListModel { } function modelsAreEqual(m1, m2, comparer) { - if(m1.count !== m2.count) { + if (m1.count !== m2.count) { return false; } - for(var i = 0; i < m1.count; ++i) { + for (var i = 0; i < m1.count; ++i) { var e1 = m1.get(i); var allDifferent = true; // it turns out order of wearables can randomly change so make position-independent comparison here - for(var j = 0; j < m2.count; ++j) { + for (var j = 0; j < m2.count; ++j) { var e2 = m2.get(j); - if(comparer(e1, e2)) { + if (comparer(e1, e2)) { allDifferent = false; break; } } - if(allDifferent) { + if (allDifferent) { return false; } } @@ -114,18 +115,20 @@ ListModel { } function compareNumericObjects(o1, o2) { - if(o1 === undefined && o2 !== undefined) + if (o1 === undefined && o2 !== undefined) { return false; - if(o1 !== undefined && o2 === undefined) + } + if (o1 !== undefined && o2 === undefined) { return false; + } - for(var prop in o1) { - if(o1.hasOwnProperty(prop) && o2.hasOwnProperty(prop)) { + for (var prop in o1) { + if (o1.hasOwnProperty(prop) && o2.hasOwnProperty(prop)) { var v1 = o1[prop]; var v2 = o2[prop]; - if(v1 !== v2 && Math.round(v1 * 500) != Math.round(v2 * 500)) { + if (v1 !== v2 && Math.round(v1 * 500) != Math.round(v2 * 500)) { return false; } } @@ -135,7 +138,7 @@ ListModel { } function compareObjects(o1, o2, props, arrayProp) { - for(var i = 0; i < props.length; ++i) { + for (var i = 0; i < props.length; ++i) { var prop = props[i]; var propertyName = prop.propertyName; var comparer = prop.comparer; @@ -143,12 +146,12 @@ ListModel { var o1Value = arrayProp ? o1[arrayProp][propertyName] : o1[propertyName]; var o2Value = arrayProp ? o2[arrayProp][propertyName] : o2[propertyName]; - if(comparer) { - if(comparer(o1Value, o2Value) === false) { + if (comparer) { + if (comparer(o1Value, o2Value) === false) { return false; } } else { - if(JSON.stringify(o1Value) !== JSON.stringify(o2Value)) { + if (JSON.stringify(o1Value) !== JSON.stringify(o2Value)) { return false; } } @@ -174,21 +177,23 @@ ListModel { var index = -1; // 2DO: find better way of determining selected avatar in bookmarks - for(var i = 0; i < allAvatars.count; ++i) { + for (var i = 0; i < allAvatars.count; ++i) { var thesame = true; var bookmarkedAvatar = allAvatars.get(i); - if(bookmarkedAvatar.avatarUrl !== avatar.avatarUrl) - continue; - - if(bookmarkedAvatar.avatarScale !== avatar.avatarScale) - continue; - - if(!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) { + if (bookmarkedAvatar.avatarUrl !== avatar.avatarUrl) { continue; } - if(thesame) { + if (bookmarkedAvatar.avatarScale !== avatar.avatarScale) { + continue; + } + + if (!modelsAreEqual(bookmarkedAvatar.wearables, avatar.wearables, compareWearables)) { + continue; + } + + if (thesame) { index = i; break; } @@ -198,8 +203,8 @@ ListModel { } function findAvatarIndex(avatarName) { - for(var i = 0; i < count; ++i) { - if(get(i).name === avatarName) { + for (var i = 0; i < count; ++i) { + if (get(i).name === avatarName) { return i; } } @@ -208,8 +213,9 @@ ListModel { function findAvatar(avatarName) { var avatarIndex = findAvatarIndex(avatarName); - if(avatarIndex === -1) + if (avatarIndex === -1) { return undefined; + } return get(avatarIndex); } diff --git a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml index ab995e79e8..1387c0791a 100644 --- a/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml +++ b/interface/resources/qml/hifi/avatarapp/CreateFavoriteDialog.qml @@ -125,9 +125,9 @@ Rectangle { size: 15 color: 'red' visible: { - for(var i = 0; i < avatars.count; ++i) { + for (var i = 0; i < avatars.count; ++i) { var avatarName = avatars.get(i).name; - if(avatarName === favoriteName.text) { + if (avatarName === favoriteName.text) { return true; } } @@ -165,7 +165,7 @@ Rectangle { } onYesClicked: function() { - if(onSaveClicked) { + if (onSaveClicked) { onSaveClicked(); } else { root.close(); @@ -173,7 +173,7 @@ Rectangle { } onNoClicked: function() { - if(onCancelClicked) { + if (onCancelClicked) { onCancelClicked(); } else { root.close(); diff --git a/interface/resources/qml/hifi/avatarapp/DialogButtons.qml b/interface/resources/qml/hifi/avatarapp/DialogButtons.qml index 46c17bb4dc..d6eb0139ed 100644 --- a/interface/resources/qml/hifi/avatarapp/DialogButtons.qml +++ b/interface/resources/qml/hifi/avatarapp/DialogButtons.qml @@ -33,7 +33,7 @@ Row { onClicked: { console.debug('whitebutton.clicked', onNoClicked); - if(onNoClicked) { + if (onNoClicked) { onNoClicked(); } } diff --git a/interface/resources/qml/hifi/avatarapp/MessageBox.qml b/interface/resources/qml/hifi/avatarapp/MessageBox.qml index e4aa0847c5..f111303214 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBox.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBox.qml @@ -128,7 +128,7 @@ Rectangle { wrapMode: Text.WordWrap; onLinkActivated: { - if(onLinkClicked) + if (onLinkClicked) onLinkClicked(link); } } @@ -166,7 +166,7 @@ Rectangle { noText: root.button1text onYesClicked: function() { - if(onButton2Clicked) { + if (onButton2Clicked) { onButton2Clicked(); } else { root.close(); @@ -174,7 +174,7 @@ Rectangle { } onNoClicked: function() { - if(onButton1Clicked) { + if (onButton1Clicked) { onButton1Clicked(); } else { root.close(); diff --git a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml index f78c973bd6..b7782c697d 100644 --- a/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml +++ b/interface/resources/qml/hifi/avatarapp/MessageBoxes.qml @@ -18,15 +18,17 @@ MessageBox { popup.button2text = 'CONFIRM'; popup.onButton2Clicked = function() { - if(callback) + if (callback) { callback(); + } popup.close(); } popup.onLinkClicked = function(link) { - if(linkCallback) + if (linkCallback) { linkCallback(link); + } } popup.open(); @@ -43,8 +45,9 @@ MessageBox { popup.inputText.placeholderText = 'Enter Wearable URL'; popup.onButton2Clicked = function() { - if(callback) + if (callback) { callback(popup.inputText.text); + } popup.close(); } @@ -68,15 +71,17 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.onLinkClicked = function(link) { popup.close(); - if(linkCallback) + if (linkCallback) { linkCallback(link); + } } popup.open(); @@ -92,8 +97,9 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.open(); } @@ -107,8 +113,9 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.open(); } @@ -129,15 +136,17 @@ MessageBox { popup.onButton2Clicked = function() { popup.close(); - if(callback) + if (callback) { callback(); + } } popup.onLinkClicked = function(link) { popup.close(); - if(linkCallback) + if (linkCallback) { linkCallback(link); + } } popup.open(); diff --git a/interface/resources/qml/hifi/avatarapp/Settings.qml b/interface/resources/qml/hifi/avatarapp/Settings.qml index e1b55866c2..71bfbb084d 100644 --- a/interface/resources/qml/hifi/avatarapp/Settings.qml +++ b/interface/resources/qml/hifi/avatarapp/Settings.qml @@ -33,13 +33,13 @@ Rectangle { scaleSlider.value = Math.round(avatarScale * 10); scaleSlider.notify = true;; - if(settings.dominantHand === 'left') { + if (settings.dominantHand === 'left') { leftHandRadioButton.checked = true; } else { rightHandRadioButton.checked = true; } - if(settings.collisionsEnabled) { + if (settings.collisionsEnabled) { collisionsEnabledRadiobutton.checked = true; } else { collisionsDisabledRadioButton.checked = true; @@ -113,7 +113,7 @@ Rectangle { onValueChanged: { console.debug('value changed: ', value); - if(notify) { + if (notify) { console.debug('notifying.. '); root.scaleChanged(value / 10); } diff --git a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml index 4884d1e1ad..6c50a6093a 100644 --- a/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml +++ b/interface/resources/qml/hifi/avatarapp/TransparencyMask.qml @@ -32,7 +32,7 @@ Item { highp vec4 maskColor = texture2D(mask, vec2(qt_TexCoord0.x, qt_TexCoord0.y)); highp vec4 sourceColor = texture2D(source, vec2(qt_TexCoord0.x, qt_TexCoord0.y)); - if(maskColor.a > 0.0) + if (maskColor.a > 0.0) gl_FragColor = sourceColor; else gl_FragColor = maskColor; From 6b994ba39a27cbc4ff895947973d05a7433f865e Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 14:51:54 -0700 Subject: [PATCH 62/64] Completely remove the texcoord frame transform as we don;t use it at all --- libraries/render-utils/src/DeferredLightingEffect.cpp | 11 ----------- libraries/render-utils/src/deferred_light.slv | 10 +++++++--- 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/libraries/render-utils/src/DeferredLightingEffect.cpp b/libraries/render-utils/src/DeferredLightingEffect.cpp index 91d196f896..af5a3f3e63 100644 --- a/libraries/render-utils/src/DeferredLightingEffect.cpp +++ b/libraries/render-utils/src/DeferredLightingEffect.cpp @@ -496,11 +496,6 @@ void RenderDeferredSetup::run(const render::RenderContextPointer& renderContext, batch.setPipeline(program); } - // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) - // Adjust the texcoordTransform in the case we are rendeirng a sub region(mini mirror) - // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), args->_viewport); - // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast< const float* >(&textureFrameTransform)); - // Setup the global lighting deferredLightingEffect->setupKeyLightBatch(args, batch); @@ -561,24 +556,18 @@ void RenderDeferredLocals::run(const render::RenderContextPointer& renderContext batch.setViewportTransform(viewport); batch.setStateScissorRect(viewport); - // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) - // auto textureFrameTransform = gpu::Framebuffer::evalSubregionTexcoordTransformCoefficients(deferredFramebuffer->getFrameSize(), viewport); - - auto& lightIndices = lightClusters->_visibleLightIndices; if (!lightIndices.empty() && lightIndices[0] > 0) { deferredLightingEffect->setupLocalLightsBatch(batch, lightClusters); // Local light pipeline batch.setPipeline(deferredLightingEffect->_localLight); - // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); // Draw outline as well ? if (lightingModel->isShowLightContourEnabled()) { batch.setPipeline(deferredLightingEffect->_localLightOutline); - // batch._glUniform4fv(ru::Uniform::TexcoordTransform, 1, reinterpret_cast(&textureFrameTransform)); batch.draw(gpu::TRIANGLE_STRIP, 4); } diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 0bc7a0f807..4ea3133718 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,8 +16,10 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; +#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM // NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) -//layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; +layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; +#endif void main(void) { const float depth = 1.0; @@ -31,8 +33,10 @@ void main(void) { _texCoord01.xy = (pos.xy + 1.0) * 0.5; - // _texCoord01.xy *= texcoordFrameTransform.zw; - // _texCoord01.xy += texcoordFrameTransform.xy; +#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM + _texCoord01.xy *= texcoordFrameTransform.zw; + _texCoord01.xy += texcoordFrameTransform.xy; +#endif gl_Position = pos; } From 30c0c44822fe0817e93d1007465a9792441cce84 Mon Sep 17 00:00:00 2001 From: sam gateau Date: Tue, 21 Aug 2018 14:52:42 -0700 Subject: [PATCH 63/64] Completely remove the texcoord frame transform as we don;t use it at all --- libraries/render-utils/src/deferred_light.slv | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/libraries/render-utils/src/deferred_light.slv b/libraries/render-utils/src/deferred_light.slv index 4ea3133718..164fd9fb3b 100644 --- a/libraries/render-utils/src/deferred_light.slv +++ b/libraries/render-utils/src/deferred_light.slv @@ -16,11 +16,6 @@ layout(location=RENDER_UTILS_ATTR_TEXCOORD01) out vec4 _texCoord01; -#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM -// NOTE: WE are assuming that the deferred lighting pass is always full screen so this texture transform is not needed (and cause problems on AMD) -layout(location=RENDER_UTILS_UNIFORM_LIGHT_TEXCOORD_TRANSFORM) uniform vec4 texcoordFrameTransform; -#endif - void main(void) { const float depth = 1.0; const vec4 UNIT_QUAD[4] = vec4[4]( @@ -33,10 +28,5 @@ void main(void) { _texCoord01.xy = (pos.xy + 1.0) * 0.5; -#ifdef RENDER_UTILS_USE_TEXCOORD_FRAME_TRANSFORM - _texCoord01.xy *= texcoordFrameTransform.zw; - _texCoord01.xy += texcoordFrameTransform.xy; -#endif - gl_Position = pos; } From e9683011f8e01f55b2f59306947c0b5b1fe22962 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Tue, 21 Aug 2018 16:58:38 -0700 Subject: [PATCH 64/64] Fix parabola rendering on AMD GPUs --- libraries/render-utils/src/parabola.slf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libraries/render-utils/src/parabola.slf b/libraries/render-utils/src/parabola.slf index 8863f37083..ea51d7e3af 100644 --- a/libraries/render-utils/src/parabola.slf +++ b/libraries/render-utils/src/parabola.slf @@ -9,10 +9,10 @@ // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // +<@include DeferredBufferWrite.slh@> + layout(location=0) in vec4 _color; -layout(location=0) out vec4 _fragColor; - void main(void) { - _fragColor = _color; + packDeferredFragmentUnlit(vec3(1.0, 0.0, 0.0), 1.0, _color.rgb); }