From da4f2b8c2f1810528cac4001aa9f97b3a2c30326 Mon Sep 17 00:00:00 2001
From: Gabriel Calero
Date: Fri, 30 Mar 2018 15:34:00 -0300
Subject: [PATCH 001/192] Fix zoom in unresponsive in radar mode
---
scripts/system/+android/radar.js | 47 +++++++++++++++++---------------
1 file changed, 25 insertions(+), 22 deletions(-)
diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js
index 455299dd5f..2b87f8955d 100644
--- a/scripts/system/+android/radar.js
+++ b/scripts/system/+android/radar.js
@@ -21,7 +21,7 @@ function printd(str) {
}
var radar = false;
-var radarHeight = 10; // camera position meters above the avatar
+var radarHeight = MyAvatar.position.y + 10; // camera position (absolute y)
var tablet;
var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
@@ -46,11 +46,11 @@ var uniqueColor;
function moveTo(position) {
if (radar) {
MyAvatar.position = position;
- Camera.position = Vec3.sum(MyAvatar.position, {
- x : 0,
+ Camera.position = {
+ x : MyAvatar.position.x,
y : radarHeight,
- z : 0
- });
+ z : MyAvatar.position.z
+ };
}
}
@@ -386,12 +386,12 @@ function pinchUpdate(event) {
radarHeight -= pinchIncrement;
}
}
- var deltaHeight = avatarY + radarHeight - Camera.position.y;
- Camera.position = Vec3.sum(Camera.position, {
- x : 0,
- y : deltaHeight,
- z : 0
- });
+ Camera.position = {
+ x: Camera.position.x,
+ y:radarHeight,
+ z:Camera.position.z
+ };
+
if (!draggingCamera) {
startedDraggingCamera = true;
draggingCamera = true;
@@ -401,7 +401,8 @@ function pinchUpdate(event) {
}
function isInsideSquare(coords0, coords1, halfside) {
- return Math.abs(coords0.x - coords1.x) <= halfside
+ return coords0 != undefined && coords1!= undefined &&
+ Math.abs(coords0.x - coords1.x) <= halfside
&& Math.abs(coords0.y - coords1.y) <= halfside;
}
@@ -412,7 +413,7 @@ function dragScrollUpdate(event) {
// drag management
var pickRay = Camera.computePickRay(event.x, event.y);
var dragAt = Vec3.sum(pickRay.origin, Vec3.multiply(pickRay.direction,
- radarHeight));
+ radarHeight-MyAvatar.position.y));
if (lastDragAt === undefined || lastDragAt === null) {
lastDragAt = dragAt;
@@ -722,7 +723,7 @@ function Teleporter() {
return {
dragTeleportBegin : function(event) {
printd("[newTeleport] TELEPORT began");
- var overlayDimensions = entityIconModelDimensions();
+ var overlayDimensions = entityIconModelDimensions(MyAvatar.position.y);
// var destination = computeDestination(event, MyAvatar.position,
// Camera.position, radarHeight);
// Dimension teleport and cancel overlays (not show them yet)
@@ -843,7 +844,7 @@ var avatarIconDimensionsVal = {
};
function avatarIconPlaneDimensions() {
// given the current height, give a size
- var xy = -0.003531 * radarHeight + 0.1;
+ var xy = -0.003531 * (radarHeight - MyAvatar.position.y) + 0.1;
avatarIconDimensionsVal.x = Math.abs(xy);
avatarIconDimensionsVal.y = Math.abs(xy);
// reuse object
@@ -1165,9 +1166,10 @@ var entityIconModelDimensionsVal = {
y : 0.00001,
z : 0
};
-function entityIconModelDimensions() {
+function entityIconModelDimensions(y) {
// given the current height, give a size
- var xz = -0.002831 * radarHeight + 0.1;
+ // TODO: receive entity.position.y and substract to radarHeight
+ var xz = -0.002831 * (radarHeight - y) + 0.1;
entityIconModelDimensionsVal.x = xz;
entityIconModelDimensionsVal.z = xz;
// reuse object
@@ -1177,8 +1179,8 @@ function entityIconModelDimensions() {
* entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy
* plane
*/
-function entityIconPlaneDimensions() {
- var dim = entityIconModelDimensions();
+function entityIconPlaneDimensions(y) {
+ var dim = entityIconModelDimensions(y);
var z = dim.z;
dim.z = dim.y;
dim.y = z;
@@ -1255,15 +1257,15 @@ function hideAllEntitiesIcons() {
function renderAllEntitiesIcons() {
var entityPos;
var entityProps;
- var iconDimensions = entityIconModelDimensions();
- var planeDimensions = entityIconPlaneDimensions(); // plane overlays uses
- // xy instead of xz
+
for ( var QUuid in entitiesData) {
if (entitiesData.hasOwnProperty(QUuid)) {
entityProps = Entities.getEntityProperties(QUuid, [ "position",
"visible" ]);
if (entityProps != null) {
entityPos = entityProps.position;
+ var planeDimensions = entityIconPlaneDimensions(entityPos.y); // plane overlays uses
+ // xy instead of xz
if (entitiesData[QUuid].icon != undefined && entityPos) {
var iconPos = findLineToHeightIntersectionCoords(
entityPos.x,
@@ -1276,6 +1278,7 @@ function renderAllEntitiesIcons() {
printd("entity icon pos bad for " + QUuid);
continue;
}
+ var iconDimensions = entityIconModelDimensions(entityPos.y);
var dimensions = entitiesData[QUuid].planar ? planeDimensions
: iconDimensions;
Overlays.editOverlay(entitiesData[QUuid].icon, {
From fc86525863df9eb5a2a01c948fa08f30e6cf616d Mon Sep 17 00:00:00 2001
From: Olivier Prat
Date: Wed, 4 Apr 2018 12:00:06 +0200
Subject: [PATCH 002/192] Cleaned up a bit shadow map clear
---
libraries/render-utils/src/RenderShadowTask.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp
index 69c5b3c689..faa5889307 100644
--- a/libraries/render-utils/src/RenderShadowTask.cpp
+++ b/libraries/render-utils/src/RenderShadowTask.cpp
@@ -149,9 +149,7 @@ void RenderShadowMap::run(const render::RenderContextPointer& renderContext, con
batch.setStateScissorRect(viewport);
batch.setFramebuffer(fbo);
- batch.clearFramebuffer(
- gpu::Framebuffer::BUFFER_COLOR0 | gpu::Framebuffer::BUFFER_DEPTH,
- vec4(vec3(1.0, 1.0, 1.0), 0.0), 1.0, 0, true);
+ batch.clearDepthFramebuffer(1.0, false);
glm::mat4 projMat;
Transform viewMat;
From 36bc5f841627e170cb0d15c35586106d5c176eb5 Mon Sep 17 00:00:00 2001
From: Gabriel Calero
Date: Thu, 5 Apr 2018 15:35:08 -0300
Subject: [PATCH 003/192] Fix the view snap when user starts zooming
---
scripts/system/+android/radar.js | 37 ++++++++++++++++++++++----------
1 file changed, 26 insertions(+), 11 deletions(-)
diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js
index 2b87f8955d..120bec33c3 100644
--- a/scripts/system/+android/radar.js
+++ b/scripts/system/+android/radar.js
@@ -21,7 +21,8 @@ function printd(str) {
}
var radar = false;
-var radarHeight = MyAvatar.position.y + 10; // camera position (absolute y)
+var RADAR_HEIGHT_INIT_DELTA = 10;
+var radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA; // camera position (absolute y)
var tablet;
var RADAR_CAMERA_OFFSET = -1; // 1 meter below the avatar
@@ -386,6 +387,7 @@ function pinchUpdate(event) {
radarHeight -= pinchIncrement;
}
}
+
Camera.position = {
x: Camera.position.x,
y:radarHeight,
@@ -655,6 +657,7 @@ function Teleporter() {
return;
}
+
Camera.position = Vec3.sum(Camera.position, {
x : xDelta,
y : 0,
@@ -1301,11 +1304,12 @@ function startRadar() {
saveAllOthersAvatarsData();
Camera.mode = "independent";
- Camera.position = Vec3.sum(MyAvatar.position, {
- x : 0,
- y : radarHeight,
- z : 0
- });
+ Camera.position = {
+ x : MyAvatar.position.x,
+ y : radarHeight,
+ z : MyAvatar.position.z
+ };
+
Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
radar = true;
@@ -1394,15 +1398,25 @@ function connectRadarModeEvents() {
Controller.keyPressEvent.connect(keyPressEvent);
Controller.mousePressEvent.connect(mousePress); // single click/touch
Controller.touchUpdateEvent.connect(touchUpdate);
+ Window.domainChanged.connect(domainChanged);
MyAvatar.positionGoneTo.connect(positionGoneTo);
}
-function positionGoneTo() {
- Camera.position = Vec3.sum(MyAvatar.position, {
- x : 0,
+function domainChanged() {
+ radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA;
+ Camera.position = {
+ x : MyAvatar.position.x,
y : radarHeight,
- z : 0
- });
+ z : MyAvatar.position.z
+ };
+}
+
+function positionGoneTo() {
+ Camera.position = {
+ x : MyAvatar.position.x,
+ y : radarHeight,
+ z : MyAvatar.position.z
+ };
}
function disconnectRadarModeEvents() {
@@ -1411,6 +1425,7 @@ function disconnectRadarModeEvents() {
Controller.mousePressEvent.disconnect(mousePress);
Controller.touchUpdateEvent.disconnect(touchUpdate);
MyAvatar.positionGoneTo.disconnect(positionGoneTo);
+ Window.domainChanged.disconnect(domainChanged);
}
function init() {
From 573f399023eafd625212284f827ea3207f006f76 Mon Sep 17 00:00:00 2001
From: Olivier Prat
Date: Fri, 6 Apr 2018 14:45:16 +0200
Subject: [PATCH 004/192] Fixed incorrect shadow frustum far clip computation
due to not taking into account shadow receivers
---
.../render-utils/src/RenderShadowTask.cpp | 27 ++++++++++--------
libraries/render-utils/src/RenderShadowTask.h | 2 +-
libraries/render/src/render/CullTask.cpp | 28 +++++++++++++------
libraries/render/src/render/CullTask.h | 2 +-
4 files changed, 38 insertions(+), 21 deletions(-)
diff --git a/libraries/render-utils/src/RenderShadowTask.cpp b/libraries/render-utils/src/RenderShadowTask.cpp
index faa5889307..fbb4bba263 100644
--- a/libraries/render-utils/src/RenderShadowTask.cpp
+++ b/libraries/render-utils/src/RenderShadowTask.cpp
@@ -230,12 +230,11 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
const auto queryResolution = setupOutput.getN(2);
// Fetch and cull the items from the scene
- // Enable models to not cast shadows (otherwise, models will always cast shadows)
- static const auto shadowCasterFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask).withShadowCaster();
+ static const auto shadowCasterReceiverFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(tagBits, tagMask);
- const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterFilter, queryResolution).asVarying();
+ const auto fetchInput = FetchSpatialTree::Inputs(shadowCasterReceiverFilter, queryResolution).asVarying();
const auto shadowSelection = task.addJob("FetchShadowTree", fetchInput);
- const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterFilter).asVarying();
+ const auto selectionInputs = FetchSpatialSelection::Inputs(shadowSelection, shadowCasterReceiverFilter).asVarying();
const auto shadowItems = task.addJob("FetchShadowSelection", selectionInputs);
// Cull objects that are not visible in camera view. Hopefully the cull functor only performs LOD culling, not
@@ -259,21 +258,22 @@ void RenderShadowTask::build(JobModel& task, const render::Varying& input, rende
char jobName[64];
sprintf(jobName, "ShadowCascadeSetup%d", i);
const auto cascadeSetupOutput = task.addJob(jobName, i, _cullFunctor, tagBits, tagMask);
- const auto shadowFilter = cascadeSetupOutput.getN(0);
+ const auto shadowRenderFilter = cascadeSetupOutput.getN(0);
+ const auto shadowBoundsFilter = cascadeSetupOutput.getN(1);
auto antiFrustum = render::Varying(ViewFrustumPointer());
- cascadeFrustums[i] = cascadeSetupOutput.getN(1);
+ cascadeFrustums[i] = cascadeSetupOutput.getN(2);
if (i > 1) {
antiFrustum = cascadeFrustums[i - 2];
}
// CPU jobs: finer grained culling
- const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowFilter, antiFrustum).asVarying();
+ const auto cullInputs = CullShapeBounds::Inputs(sortedShapes, shadowRenderFilter, shadowBoundsFilter, antiFrustum).asVarying();
const auto culledShadowItemsAndBounds = task.addJob("CullShadowCascade", cullInputs, shadowCullFunctor, RenderDetails::SHADOW);
// GPU jobs: Render to shadow map
sprintf(jobName, "RenderShadowMap%d", i);
task.addJob(jobName, culledShadowItemsAndBounds, shapePlumber, i);
- task.addJob("ShadowCascadeTeardown", shadowFilter);
+ task.addJob("ShadowCascadeTeardown", shadowRenderFilter);
}
task.addJob("ShadowTeardown", setupOutput);
@@ -404,7 +404,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
const auto globalShadow = lightStage->getCurrentKeyShadow();
if (globalShadow && _cascadeIndexgetCascadeCount()) {
- output.edit0() = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask).withShadowCaster();
+ auto baseFilter = ItemFilter::Builder::visibleWorldItems().withTypeShape().withOpaque().withoutLayered().withTagBits(_tagBits, _tagMask);
+ // Second item filter is to filter items to keep in shadow frustum computation (here we need to keep shadow receivers)
+ output.edit1() = baseFilter;
+ // First item filter is to filter items to render in shadow map (so only keep casters)
+ output.edit0() = baseFilter.withShadowCaster();
// Set the keylight render args
auto& cascade = globalShadow->getCascade(_cascadeIndex);
@@ -417,10 +421,11 @@ void RenderShadowCascadeSetup::run(const render::RenderContextPointer& renderCon
texelSize *= minTexelCount;
_cullFunctor._minSquareSize = texelSize * texelSize;
- output.edit1() = cascadeFrustum;
+ output.edit2() = cascadeFrustum;
} else {
output.edit0() = ItemFilter::Builder::nothing();
- output.edit1() = ViewFrustumPointer();
+ output.edit1() = ItemFilter::Builder::nothing();
+ output.edit2() = ViewFrustumPointer();
}
}
diff --git a/libraries/render-utils/src/RenderShadowTask.h b/libraries/render-utils/src/RenderShadowTask.h
index 98b70c0c9f..19ffcb4234 100644
--- a/libraries/render-utils/src/RenderShadowTask.h
+++ b/libraries/render-utils/src/RenderShadowTask.h
@@ -118,7 +118,7 @@ private:
class RenderShadowCascadeSetup {
public:
- using Outputs = render::VaryingSet2;
+ using Outputs = render::VaryingSet3;
using JobModel = render::Job::ModelO;
RenderShadowCascadeSetup(unsigned int cascadeIndex, RenderShadowTask::CullFunctor& cullFunctor, uint8_t tagBits = 0x00, uint8_t tagMask = 0x00) :
diff --git a/libraries/render/src/render/CullTask.cpp b/libraries/render/src/render/CullTask.cpp
index f04427540a..b5819f114f 100644
--- a/libraries/render/src/render/CullTask.cpp
+++ b/libraries/render/src/render/CullTask.cpp
@@ -368,17 +368,19 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
RenderArgs* args = renderContext->args;
const auto& inShapes = inputs.get0();
- const auto& filter = inputs.get1();
- const auto& antiFrustum = inputs.get2();
+ const auto& cullFilter = inputs.get1();
+ const auto& boundsFilter = inputs.get2();
+ const auto& antiFrustum = inputs.get3();
auto& outShapes = outputs.edit0();
auto& outBounds = outputs.edit1();
outShapes.clear();
outBounds = AABox();
- if (!filter.selectsNothing()) {
+ if (!cullFilter.selectsNothing() || !boundsFilter.selectsNothing()) {
auto& details = args->_details.edit(_detailType);
Test test(_cullFunctor, args, details, antiFrustum);
+ auto scene = args->_scene;
for (auto& inItems : inShapes) {
auto key = inItems.first;
@@ -393,16 +395,26 @@ void CullShapeBounds::run(const RenderContextPointer& renderContext, const Input
if (antiFrustum == nullptr) {
for (auto& item : inItems.second) {
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound)) {
- outItems->second.emplace_back(item);
- outBounds += item.bound;
+ const auto shapeKey = scene->getItem(item.id).getKey();
+ if (cullFilter.test(shapeKey)) {
+ outItems->second.emplace_back(item);
+ }
+ if (boundsFilter.test(shapeKey)) {
+ outBounds += item.bound;
+ }
}
}
} else {
for (auto& item : inItems.second) {
if (test.solidAngleTest(item.bound) && test.frustumTest(item.bound) && test.antiFrustumTest(item.bound)) {
- outItems->second.emplace_back(item);
- outBounds += item.bound;
- }
+ const auto shapeKey = scene->getItem(item.id).getKey();
+ if (cullFilter.test(shapeKey)) {
+ outItems->second.emplace_back(item);
+ }
+ if (boundsFilter.test(shapeKey)) {
+ outBounds += item.bound;
+ }
+ }
}
}
details._rendered += (int)outItems->second.size();
diff --git a/libraries/render/src/render/CullTask.h b/libraries/render/src/render/CullTask.h
index 3c5a30de89..47abe8a960 100644
--- a/libraries/render/src/render/CullTask.h
+++ b/libraries/render/src/render/CullTask.h
@@ -110,7 +110,7 @@ namespace render {
class CullShapeBounds {
public:
- using Inputs = render::VaryingSet3;
+ using Inputs = render::VaryingSet4;
using Outputs = render::VaryingSet2;
using JobModel = Job::ModelIO;
From d5c252c9ad70cfb2e46c95b75cd3a71d7b8f5196 Mon Sep 17 00:00:00 2001
From: Cristian Duarte
Date: Mon, 9 Apr 2018 16:24:03 -0300
Subject: [PATCH 005/192] Use MyAvatar.goToLocation in radar teleport.
Use MyAvatar.goToLocation in radar teleport to prevent synchronization issues that make it fail occasionally when directly modifying its world position. It will work just as teleport.js does.
---
scripts/system/+android/radar.js | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js
index 120bec33c3..8100cc0887 100644
--- a/scripts/system/+android/radar.js
+++ b/scripts/system/+android/radar.js
@@ -46,11 +46,11 @@ var uniqueColor;
function moveTo(position) {
if (radar) {
- MyAvatar.position = position;
+ MyAvatar.goToLocation(position, false);
Camera.position = {
- x : MyAvatar.position.x,
+ x : position.x,
y : radarHeight,
- z : MyAvatar.position.z
+ z : position.z
};
}
}
From 63e17e87b0fa4e00db8b871e731c34684a1f4f08 Mon Sep 17 00:00:00 2001
From: Gabriel Calero
Date: Tue, 10 Apr 2018 19:21:23 -0300
Subject: [PATCH 006/192] Fix radar initial position. Clean radar.js
---
scripts/system/+android/radar.js | 232 ++-----------------------------
1 file changed, 8 insertions(+), 224 deletions(-)
diff --git a/scripts/system/+android/radar.js b/scripts/system/+android/radar.js
index 8100cc0887..67ba896207 100644
--- a/scripts/system/+android/radar.js
+++ b/scripts/system/+android/radar.js
@@ -90,46 +90,6 @@ function keyPressEvent(event) {
}
}
-function actionOnObjectFromEvent(event) {
- var rayIntersection = findRayIntersection(Camera.computePickRay(event.x,
- event.y));
- if (rayIntersection && rayIntersection.intersects
- && rayIntersection.overlayID) {
- printd("found overlayID touched " + rayIntersection.overlayID);
- if (entitiesByOverlayID[rayIntersection.overlayID]) {
- var entity = Entities.getEntityProperties(
- entitiesByOverlayID[rayIntersection.overlayID],
- [ "sourceUrl" ]);
- App.openUrl(entity.sourceUrl);
- return true;
- }
- }
- if (rayIntersection && rayIntersection.intersects
- && rayIntersection.entityID && rayIntersection.properties) {
- printd("found " + rayIntersection.entityID + " of type "
- + rayIntersection.properties.type);
- if (rayIntersection.properties.type == "Web") {
- printd("found web element to "
- + rayIntersection.properties.sourceUrl);
- App.openUrl(rayIntersection.properties.sourceUrl);
- return true;
- }
- }
- return false;
-}
-
-function mousePress(event) {
- mousePressOrTouchEnd(event);
-}
-
-function mousePressOrTouchEnd(event) {
- if (radar) {
- if (actionOnObjectFromEvent(event)) {
- return;
- }
- }
-}
-
function toggleRadarMode() {
if (radar) {
endRadar();
@@ -230,9 +190,6 @@ function touchEnd(event) {
if (analyzeDoubleTap(event))
return; // double tap detected, finish
- if (radar) {
- mousePressOrTouchEnd(event);
- }
}
/**
@@ -726,7 +683,7 @@ function Teleporter() {
return {
dragTeleportBegin : function(event) {
printd("[newTeleport] TELEPORT began");
- var overlayDimensions = entityIconModelDimensions(MyAvatar.position.y);
+ var overlayDimensions = teleportIconModelDimensions(MyAvatar.position.y);
// var destination = computeDestination(event, MyAvatar.position,
// Camera.position, radarHeight);
// Dimension teleport and cancel overlays (not show them yet)
@@ -1125,39 +1082,10 @@ function renderAllOthersAvatarIcons() {
}
}
-function entityAdded(entityID) {
- printd("Entity added " + entityID);
- var props = Entities.getEntityProperties(entityID, [ "type" ]);
- printd("Entity added " + entityID + " PROPS " + JSON.stringify(props));
- if (props && props.type == "Web") {
- printd("Entity Web added " + entityID);
- saveEntityData(entityID, true);
- }
-}
-
-function entityRemoved(entityID) {
- printd("Entity removed " + entityID);
- var props = Entities.getEntityProperties(entityID, [ "type" ]);
- if (props && props.type == "Web") {
- print("Entity Web removed " + entityID);
- removeEntityData(entityID);
- }
-}
-
/*******************************************************************************
* Entities (to remark) cache structure for showing entities markers
******************************************************************************/
-var entitiesData = {}; // by entityID
-var entitiesByOverlayID = {}; // by overlayID
-var entitiesIcons = []; // a parallel list of icons (overlays) to easily run
- // through
-
-var ICON_ENTITY_WEB_MODEL_URL = Script.resolvePath("../assets/images/web.svg");
-var ICON_ENTITY_IMG_MODEL_URL = Script
- .resolvePath("../assets/models/teleport-cancel.fbx"); // FIXME - use
- // correct
- // model&texture
var ICON_ENTITY_DEFAULT_DIMENSIONS = {
x : 0.10,
y : 0.00001,
@@ -1169,7 +1097,7 @@ var entityIconModelDimensionsVal = {
y : 0.00001,
z : 0
};
-function entityIconModelDimensions(y) {
+function teleportIconModelDimensions(y) {
// given the current height, give a size
// TODO: receive entity.position.y and substract to radarHeight
var xz = -0.002831 * (radarHeight - y) + 0.1;
@@ -1178,122 +1106,6 @@ function entityIconModelDimensions(y) {
// reuse object
return entityIconModelDimensionsVal;
}
-/*
- * entityIconPlaneDimensions: similar to entityIconModelDimensions but using xy
- * plane
- */
-function entityIconPlaneDimensions(y) {
- var dim = entityIconModelDimensions(y);
- var z = dim.z;
- dim.z = dim.y;
- dim.y = z;
- return dim;
-}
-
-function currentOverlayForEntity(QUuid) {
- if (entitiesData[QUuid] != undefined) {
- return entitiesData[QUuid].icon;
- } else {
- return null;
- }
-}
-
-function saveEntityData(QUuid, planar) {
- if (QUuid == null)
- return;
- var entity = Entities.getEntityProperties(QUuid, [ "position" ]);
- printd("entity added save entity " + QUuid);
- if (entitiesData[QUuid] != undefined) {
- entitiesData[QUuid].position = entity.position;
- } else {
- var entityIcon = Overlays.addOverlay("image3d", {
- subImage : {
- x : 0,
- y : 0,
- width : 150,
- height : 150
- },
- url : ICON_ENTITY_WEB_MODEL_URL,
- dimensions : ICON_ENTITY_DEFAULT_DIMENSIONS,
- visible : false,
- ignoreRayIntersection : false,
- orientation : Quat.fromPitchYawRollDegrees(-90, 0, 0)
- });
-
- entitiesIcons.push(entityIcon);
- entitiesData[QUuid] = {
- position : entity.position,
- icon : entityIcon
- };
- entitiesByOverlayID[entityIcon] = QUuid;
- }
-}
-
-function removeEntityData(QUuid) {
- if (QUuid == null)
- return;
-
- var itsOverlay = currentOverlayForEntity(QUuid);
- if (itsOverlay != null) {
- Overlays.deleteOverlay(itsOverlay);
- delete entitiesByOverlayID[itsOverlay];
- }
- var idx = entitiesIcons.indexOf(itsOverlay);
- entitiesIcons.splice(idx, 1);
-
- delete entitiesData[QUuid];
-}
-
-/*******************************************************************************
- * Entities to remark Icon/Markers rendering
- ******************************************************************************/
-
-function hideAllEntitiesIcons() {
- var len = entitiesIcons.length;
- for (var i = 0; i < len; i++) {
- Overlays.editOverlay(entitiesIcons[i], {
- visible : false
- });
- }
-}
-
-function renderAllEntitiesIcons() {
- var entityPos;
- var entityProps;
-
- for ( var QUuid in entitiesData) {
- if (entitiesData.hasOwnProperty(QUuid)) {
- entityProps = Entities.getEntityProperties(QUuid, [ "position",
- "visible" ]);
- if (entityProps != null) {
- entityPos = entityProps.position;
- var planeDimensions = entityIconPlaneDimensions(entityPos.y); // plane overlays uses
- // xy instead of xz
- if (entitiesData[QUuid].icon != undefined && entityPos) {
- var iconPos = findLineToHeightIntersectionCoords(
- entityPos.x,
- entityPos.y
- + RADAR_ICONS_APPARENT_DISTANCE_TO_AVATAR_BASE,
- entityPos.z, Camera.position.x, Camera.position.y,
- Camera.position.z, Camera.position.y
- - RADAR_CAMERA_DISTANCE_TO_ICONS);
- if (!iconPos) {
- printd("entity icon pos bad for " + QUuid);
- continue;
- }
- var iconDimensions = entityIconModelDimensions(entityPos.y);
- var dimensions = entitiesData[QUuid].planar ? planeDimensions
- : iconDimensions;
- Overlays.editOverlay(entitiesData[QUuid].icon, {
- visible : entityProps.visible,
- dimensions : dimensions,
- position : iconPos
- });
- }
- }
- }
- }
-}
/*******************************************************************************
*
@@ -1304,11 +1116,7 @@ function startRadar() {
saveAllOthersAvatarsData();
Camera.mode = "independent";
- Camera.position = {
- x : MyAvatar.position.x,
- y : radarHeight,
- z : MyAvatar.position.z
- };
+ initCameraOverMyAvatar();
Camera.orientation = Quat.fromPitchYawRollDegrees(-90, 0, 0);
radar = true;
@@ -1326,7 +1134,6 @@ function endRadar() {
Controller.setVPadEnabled(true);
disconnectRadarModeEvents();
- hideAllEntitiesIcons();
hideAllAvatarIcons();
}
@@ -1360,12 +1167,10 @@ function updateRadar() {
// Update avatar icons
if (startedDraggingCamera) {
hideAllAvatarIcons();
- hideAllEntitiesIcons();
startedDraggingCamera = false;
} else if (!draggingCamera) {
renderMyAvatarIcon();
renderAllOthersAvatarIcons();
- renderAllEntitiesIcons();
}
}
@@ -1373,36 +1178,15 @@ function valueIfDefined(value) {
return value !== undefined ? value : "";
}
-function entitiesAnalysis() {
- var ids = Entities.findEntitiesInFrustum(Camera.frustum);
- var entities = [];
- for (var i = 0; i < ids.length; i++) {
- var id = ids[i];
- var properties = Entities.getEntityProperties(id);
- entities.push({
- id : id,
- name : properties.name,
- type : properties.type,
- url : properties.type == "Model" ? properties.modelURL : "",
- sourceUrl : properties.sourceUrl,
- locked : properties.locked,
- visible : properties.visible,
- drawCalls : valueIfDefined(properties.renderInfo.drawCalls),
- hasScript : properties.script !== ""
- });
- }
-}
-
function connectRadarModeEvents() {
Script.update.connect(updateRadar); // 60Hz loop
Controller.keyPressEvent.connect(keyPressEvent);
- Controller.mousePressEvent.connect(mousePress); // single click/touch
Controller.touchUpdateEvent.connect(touchUpdate);
Window.domainChanged.connect(domainChanged);
MyAvatar.positionGoneTo.connect(positionGoneTo);
}
-function domainChanged() {
+function initCameraOverMyAvatar() {
radarHeight = MyAvatar.position.y + RADAR_HEIGHT_INIT_DELTA;
Camera.position = {
x : MyAvatar.position.x,
@@ -1411,6 +1195,10 @@ function domainChanged() {
};
}
+function domainChanged() {
+ initCameraOverMyAvatar();
+}
+
function positionGoneTo() {
Camera.position = {
x : MyAvatar.position.x,
@@ -1422,7 +1210,6 @@ function positionGoneTo() {
function disconnectRadarModeEvents() {
Script.update.disconnect(updateRadar);
Controller.keyPressEvent.disconnect(keyPressEvent);
- Controller.mousePressEvent.disconnect(mousePress);
Controller.touchUpdateEvent.disconnect(touchUpdate);
MyAvatar.positionGoneTo.disconnect(positionGoneTo);
Window.domainChanged.disconnect(domainChanged);
@@ -1436,7 +1223,4 @@ function init() {
AvatarList.avatarAddedEvent.connect(avatarAdded);
AvatarList.avatarRemovedEvent.connect(avatarRemoved);
-
- Entities.addingEntity.connect(entityAdded);
- Entities.deletingEntity.connect(entityRemoved);
}
From aceaa510c89a9226170de6c60dc7dd21505e50f7 Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Mon, 23 Apr 2018 14:47:17 -0700
Subject: [PATCH 007/192] Added referral to physical memory Test. Added menu
items - `close` and `about`
---
tools/auto-tester/src/Test.cpp | 78 +++++++++++++++++--------
tools/auto-tester/src/Test.h | 2 +
tools/auto-tester/src/ui/AutoTester.cpp | 10 ++++
tools/auto-tester/src/ui/AutoTester.h | 2 +
tools/auto-tester/src/ui/AutoTester.ui | 24 ++++++++
5 files changed, 91 insertions(+), 25 deletions(-)
diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp
index 99f9025fdd..57806e80f6 100644
--- a/tools/auto-tester/src/Test.cpp
+++ b/tools/auto-tester/src/Test.cpp
@@ -498,6 +498,12 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
const QString regexAssertGPU(ws + functionAssertGPU + ws + "\\(" + ws + quotedString + ".*");
const QRegularExpression lineAssertGPU = QRegularExpression(regexAssertGPU);
+ // Assert the correct amount of memory
+ const QString functionAssertPhysicalMemoryGB(ws + "autoTester" + ws + "\\." + ws + "assertPhysicalMemoryGB");
+ const QString regexAssertPhysicalMemoryGB(ws + functionAssertPhysicalMemoryGB + ws + "\\(" + ws + quotedString + ".*");
+ const QRegularExpression lineAssertPhysicalMemoryGB = QRegularExpression(regexAssertPhysicalMemoryGB);
+
+
// Each step is either of the following forms:
// autoTester.addStepSnapshot("Take snapshot"...
// autoTester.addStep("Clean up after test"...
@@ -514,18 +520,27 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
if (lineContainingTitle.match(line).hasMatch()) {
QStringList tokens = line.split('"');
relevantTextFromTest.title = tokens[1];
+
} else if (lineAssertPlatform.match(line).hasMatch()) {
QStringList platforms = line.split('"');
relevantTextFromTest.platform = platforms[1];
+
} else if (lineAssertDisplay.match(line).hasMatch()) {
QStringList displays = line.split('"');
relevantTextFromTest.display = displays[1];
+
} else if (lineAssertCPU.match(line).hasMatch()) {
QStringList cpus = line.split('"');
relevantTextFromTest.cpu = cpus[1];
+
} else if (lineAssertGPU.match(line).hasMatch()) {
QStringList gpus = line.split('"');
relevantTextFromTest.gpu = gpus[1];
+
+ } else if (lineAssertPhysicalMemoryGB.match(line).hasMatch()) {
+ QStringList physicalMemoryGB = line.split('"');
+ relevantTextFromTest.physicalMemoryGB = physicalMemoryGB[1];
+
} else if (lineStepSnapshot.match(line).hasMatch()) {
QStringList tokens = line.split('"');
QString nameOfStep = tokens[1];
@@ -534,6 +549,7 @@ ExtractedText Test::getTestScriptLines(QString testFileName) {
step->text = nameOfStep;
step->takeSnapshot = true;
relevantTextFromTest.stepList.emplace_back(step);
+
} else if (lineStep.match(line).hasMatch()) {
QStringList tokens = line.split('"');
QString nameOfStep = tokens[1];
@@ -630,62 +646,74 @@ void Test::createMDFile(QString testDirectory) {
// Platform
QStringList platforms = testScriptLines.platform.split(" ");;
- stream << "## Platforms\n";
- stream << "Run the test on each of the following platforms\n";
- for (int i = 0; i < platforms.size(); ++i) {
- // Note that the platforms parameter may include extra spaces, these appear as empty strings in the list
- if (platforms[i] != QString()) {
- stream << " - " << platforms[i] << "\n";
+ if (platforms.size() > 0) {
+ stream << "## Platforms\n";
+ stream << "Run the test on each of the following platforms\n";
+ for (int i = 0; i < platforms.size(); ++i) {
+ // Note that the platforms parameter may include extra spaces, these appear as empty strings in the list
+ if (platforms[i] != QString()) {
+ stream << " - " << platforms[i] << "\n";
+ }
}
}
// Display
QStringList displays = testScriptLines.display.split(" ");
- stream << "## Displays\n";
- stream << "Run the test on each of the following displays\n";
- for (int i = 0; i < displays.size(); ++i) {
- // Note that the displays parameter may include extra spaces, these appear as empty strings in the list
- if (displays[i] != QString()) {
- stream << " - " << displays[i] << "\n";
+ if (displays.size()) {
+ stream << "## Displays\n";
+ stream << "Run the test on each of the following displays\n";
+ for (int i = 0; i < displays.size(); ++i) {
+ // Note that the displays parameter may include extra spaces, these appear as empty strings in the list
+ if (displays[i] != QString()) {
+ stream << " - " << displays[i] << "\n";
+ }
}
}
// CPU
QStringList cpus = testScriptLines.cpu.split(" ");
- stream << "## Processors\n";
- stream << "Run the test on each of the following processors\n";
- for (int i = 0; i < cpus.size(); ++i) {
- // Note that the cpus parameter may include extra spaces, these appear as empty strings in the list
- if (cpus[i] != QString()) {
- stream << " - " << cpus[i] << "\n";
+ if (cpus.size() > 0) {
+ stream << "## Processors\n";
+ stream << "Run the test on each of the following processors\n";
+ for (int i = 0; i < cpus.size(); ++i) {
+ // Note that the cpus parameter may include extra spaces, these appear as empty strings in the list
+ if (cpus[i] != QString()) {
+ stream << " - " << cpus[i] << "\n";
+ }
}
}
// GPU
QStringList gpus = testScriptLines.gpu.split(" ");
- stream << "## Graphics Cards\n";
- stream << "Run the test on graphics cards from each of the following vendors\n";
- for (int i = 0; i < gpus.size(); ++i) {
- // Note that the gpus parameter may include extra spaces, these appear as empty strings in the list
- if (gpus[i] != QString()) {
- stream << " - " << gpus[i] << "\n";
+ if (gpus.size() > 0) {
+ stream << "## Graphics Cards\n";
+ stream << "Run the test on graphics cards from each of the following vendors\n";
+ for (int i = 0; i < gpus.size(); ++i) {
+ // Note that the gpus parameter may include extra spaces, these appear as empty strings in the list
+ if (gpus[i] != QString()) {
+ stream << " - " << gpus[i] << "\n";
+ }
}
}
stream << "## Steps\n";
stream << "Press space bar to advance step by step\n\n";
+ // Note that snapshots of step n are taken in step n+1
+ // (this implies that if the LAST step requests a snapshot then this will not work - caveat emptor)
int snapShotIndex { 0 };
for (size_t i = 0; i < testScriptLines.stepList.size(); ++i) {
stream << "### Step " << QString::number(i + 1) << "\n";
stream << "- " << testScriptLines.stepList[i]->text << "\n";
- if (testScriptLines.stepList[i]->takeSnapshot) {
+ if ((i + 1 < testScriptLines.stepList.size()) && testScriptLines.stepList[i + 1]->takeSnapshot) {
stream << "- .rightJustified(5, '0') << ".png)\n";
++snapShotIndex;
}
}
mdFile.close();
+
+ messageBox.information(0, "Success", "Test MD file " + mdFilename + " has been created");
}
void Test::createTestsOutline() {
diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h
index e69459fef2..7f5553f9e3 100644
--- a/tools/auto-tester/src/Test.h
+++ b/tools/auto-tester/src/Test.h
@@ -34,6 +34,8 @@ public:
QString display;
QString cpu;
QString gpu;
+ QString physicalMemoryGB;
+
StepList stepList;
};
diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp
index 21acfe9569..3f7d2cba28 100644
--- a/tools/auto-tester/src/ui/AutoTester.cpp
+++ b/tools/auto-tester/src/ui/AutoTester.cpp
@@ -10,6 +10,8 @@
//
#include "AutoTester.h"
+#include
+
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
ui.setupUi(this);
ui.checkBoxInteractiveMode->setChecked(true);
@@ -18,6 +20,9 @@ AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
test = new Test();
signalMapper = new QSignalMapper();
+
+ connect(ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked);
+ connect(ui.actionAbout, &QAction::triggered, this, &AutoTester::about);
}
void AutoTester::on_evaluateTestsButton_clicked() {
@@ -100,3 +105,8 @@ void AutoTester::saveImage(int index) {
ui.progressBar->setValue(_numberOfImagesDownloaded);
}
}
+
+void AutoTester::about() {
+ QMessageBox messageBox;
+ messageBox.information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__);
+}
\ No newline at end of file
diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/auto-tester/src/ui/AutoTester.h
index 1788e97177..03cb7fbcec 100644
--- a/tools/auto-tester/src/ui/AutoTester.h
+++ b/tools/auto-tester/src/ui/AutoTester.h
@@ -37,6 +37,8 @@ private slots:
void saveImage(int index);
+ void about();
+
private:
Ui::AutoTesterClass ui;
Test* test;
diff --git a/tools/auto-tester/src/ui/AutoTester.ui b/tools/auto-tester/src/ui/AutoTester.ui
index 2eb1314481..c5115d69b2 100644
--- a/tools/auto-tester/src/ui/AutoTester.ui
+++ b/tools/auto-tester/src/ui/AutoTester.ui
@@ -157,6 +157,20 @@
21
+
+
+
+
@@ -167,6 +181,16 @@
+
+
+ Close
+
+
+
+
+ About
+
+
From b2925c5843ef5da8b40eb59adb8c12c94903f1c2 Mon Sep 17 00:00:00 2001
From: Dante Ruiz
Date: Tue, 24 Apr 2018 13:32:30 -0700
Subject: [PATCH 008/192] fixing attachments not being visible when coming out
of first person
---
interface/src/avatar/MyAvatar.cpp | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index 249a765d92..e288528be9 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -2037,12 +2037,12 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
-
+ uint32_t renderTagBits = shouldDrawHead ? render::ItemKey::TAG_BITS_0 : render::ItemKey::TAG_BITS_NONE;
_attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
- render::ItemKey::TAG_BITS_NONE, true);
+ renderTagBits, false);
- _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(),
- render::ItemKey::TAG_BITS_NONE, true);
+ _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(),
+ renderTagBits, false);
}
}
}
From 29fce488e479460437db366fdc2f5edb22e800bf Mon Sep 17 00:00:00 2001
From: Dante Ruiz
Date: Tue, 24 Apr 2018 14:00:52 -0700
Subject: [PATCH 009/192] make some changes
---
interface/src/avatar/MyAvatar.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/interface/src/avatar/MyAvatar.cpp b/interface/src/avatar/MyAvatar.cpp
index e288528be9..367042bcf9 100755
--- a/interface/src/avatar/MyAvatar.cpp
+++ b/interface/src/avatar/MyAvatar.cpp
@@ -2037,12 +2037,14 @@ void MyAvatar::preDisplaySide(RenderArgs* renderArgs) {
_attachmentData[i].jointName.compare("RightEye", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("HeadTop_End", Qt::CaseInsensitive) == 0 ||
_attachmentData[i].jointName.compare("Face", Qt::CaseInsensitive) == 0) {
- uint32_t renderTagBits = shouldDrawHead ? render::ItemKey::TAG_BITS_0 : render::ItemKey::TAG_BITS_NONE;
- _attachmentModels[i]->setVisibleInScene(shouldDrawHead, qApp->getMain3DScene(),
- renderTagBits, false);
+ uint8_t modelRenderTagBits = shouldDrawHead ? render::ItemKey::TAG_BITS_0 : render::ItemKey::TAG_BITS_NONE;
+ modelRenderTagBits |= render::ItemKey::TAG_BITS_1;
+ _attachmentModels[i]->setVisibleInScene(true, qApp->getMain3DScene(),
+ modelRenderTagBits, false);
- _attachmentModels[i]->setCanCastShadow(shouldDrawHead, qApp->getMain3DScene(),
- renderTagBits, false);
+ uint8_t castShadowRenderTagBits = render::ItemKey::TAG_BITS_0 | render::ItemKey::TAG_BITS_1;
+ _attachmentModels[i]->setCanCastShadow(true, qApp->getMain3DScene(),
+ castShadowRenderTagBits, false);
}
}
}
From 99ad51189c66ed5e6c2f425c55d39be9953df464 Mon Sep 17 00:00:00 2001
From: Triplelexx
Date: Tue, 24 Apr 2018 23:03:56 +0100
Subject: [PATCH 010/192] fix browse asset filtering
added support for list of filters using formatting consistent with other implementations, e.g: (*.png *.wav)
---
.../assetDialog/AssetDialogContent.qml | 40 +++++++++++++++----
1 file changed, 33 insertions(+), 7 deletions(-)
diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
index 84f4c694ff..7c8a735ead 100644
--- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
+++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
@@ -19,14 +19,14 @@ import "../fileDialog"
Item {
// Set from OffscreenUi::assetDialog()
property alias dir: assetTableModel.folder
- property alias filter: selectionType.filtersString // FIXME: Currently only supports simple filters, "*.xxx".
+ property alias filter: selectionType.filtersString
property int options // Not used.
property bool selectDirectory: false
// Not implemented.
- //property bool saveDialog: false;
- //property bool multiSelect: false;
+ // property bool saveDialog: false;
+ // property bool multiSelect: false;
property bool singleClickNavigate: false
@@ -84,7 +84,7 @@ Item {
size: 28
width: height
enabled: destination !== ""
- //onClicked: d.navigateHome();
+ // onClicked: d.navigateHome();
onClicked: assetTableModel.folder = destination;
}
}
@@ -227,7 +227,9 @@ Item {
function onGetAllMappings(error, map) {
var mappings,
- fileTypeFilter,
+ fileTypeFilters = [],
+ filterListStart,
+ filterListEnd,
index,
path,
fileName,
@@ -248,7 +250,14 @@ Item {
if (error === "") {
mappings = Object.keys(map);
- fileTypeFilter = filter.replace("*", "").toLowerCase();
+ filter = filter.replace(/\s/g, '');
+ filterListStart = filter.indexOf("(");
+ filterListEnd = filter.indexOf(")");
+ if (filterListStart !== -1 && filterListEnd !== -1) {
+ fileTypeFilters = filter.substring(filterListStart + 2, filterListEnd).toLowerCase().split("*");
+ } else if (filter !== "") {
+ fileTypeFilters[0] = filter.replace("*", "").toLowerCase();
+ }
for (i = 0, length = mappings.length; i < length; i++) {
index = mappings[i].lastIndexOf("/");
@@ -259,7 +268,24 @@ Item {
fileIsDir = false;
isValid = false;
- if (fileType.toLowerCase() === fileTypeFilter) {
+ if (fileTypeFilters.length > 1) {
+ if (fileTypeFilters.indexOf(fileType.toLowerCase()) !== -1) {
+ if (path === folder) {
+ isValid = !selectDirectory;
+ } else if (path.length > folder.length) {
+ subDirectory = path.slice(folder.length);
+ index = subDirectory.indexOf("/");
+ if (index === subDirectory.lastIndexOf("/")) {
+ fileName = subDirectory.slice(0, index);
+ if (subDirectories.indexOf(fileName) === -1) {
+ fileIsDir = true;
+ isValid = true;
+ subDirectories.push(fileName);
+ }
+ }
+ }
+ }
+ } else if (fileType.toLowerCase() === fileTypeFilters[0] || fileTypeFilters.length === 0) {
if (path === folder) {
isValid = !selectDirectory;
} else if (path.length > folder.length) {
From 21a9525a737e3db268cabdd82c0c60504a54444f Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Tue, 24 Apr 2018 17:52:12 -0700
Subject: [PATCH 011/192] Version of HMACAuth with exposed lock
Client is responsible for locking between adding and
retrieving. Fixes a likely race.
---
libraries/networking/src/HMACAuth.cpp | 11 ++++++-----
libraries/networking/src/HMACAuth.h | 2 ++
libraries/networking/src/LimitedNodeList.cpp | 5 +++--
libraries/networking/src/NLPacket.cpp | 1 +
4 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp
index 42b5c48d93..e276e64b40 100644
--- a/libraries/networking/src/HMACAuth.cpp
+++ b/libraries/networking/src/HMACAuth.cpp
@@ -68,7 +68,7 @@ bool HMACAuth::setKey(const char* keyValue, int keyLen) {
return false;
}
- QMutexLocker lock(&_lock);
+ //QMutexLocker lock(&_lock);
return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
}
@@ -78,16 +78,17 @@ bool HMACAuth::setKey(const QUuid& uidKey) {
}
bool HMACAuth::addData(const char* data, int dataLen) {
- QMutexLocker lock(&_lock);
+ //QMutexLocker lock(&_lock);
return (bool) HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen);
}
HMACAuth::HMACHash HMACAuth::result() {
HMACHash hashValue(EVP_MAX_MD_SIZE);
unsigned int hashLen;
- QMutexLocker lock(&_lock);
- HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
- hashValue.resize((size_t) hashLen);
+ //QMutexLocker lock(&_lock);
+ if (HMAC_Final(_hmacContext, &hashValue[0], &hashLen)) {
+ hashValue.resize((size_t)hashLen);
+ }
// Clear state for possible reuse.
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
return hashValue;
diff --git a/libraries/networking/src/HMACAuth.h b/libraries/networking/src/HMACAuth.h
index 0bf7a86ec1..ba1ec78214 100644
--- a/libraries/networking/src/HMACAuth.h
+++ b/libraries/networking/src/HMACAuth.h
@@ -26,6 +26,8 @@ public:
explicit HMACAuth(AuthMethod authMethod = MD5);
~HMACAuth();
+ QMutex& getLock() { return _lock; }
+
bool setKey(const char* keyValue, int keyLen);
bool setKey(const QUuid& uidKey);
bool addData(const char* data, int dataLen);
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 8d177ca534..d66dd644d5 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -333,13 +333,14 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
QByteArray expectedHash = NLPacket::hashForPacketAndHMAC(packet, sourceNode->getAuthenticateHash());
- // check if the md5 hash in the header matches the hash we would expect
+ // check if the HMAC-md5 hash in the header matches the hash we would expect
if (packetHeaderHash != expectedHash) {
static QMultiMap hashDebugSuppressMap;
if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
- qCDebug(networking) << packetHeaderHash << expectedHash;
qCDebug(networking) << "Packet hash mismatch on" << headerType << "- Sender" << sourceID;
+ qCDebug(networking) << "Packet len:" << packet.getDataSize() << "Expected hash:" <<
+ expectedHash.toHex() << "Actual:" << packetHeaderHash.toHex();
hashDebugSuppressMap.insert(sourceID, headerType);
}
diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp
index 3355e1cd6b..09a7b78840 100644
--- a/libraries/networking/src/NLPacket.cpp
+++ b/libraries/networking/src/NLPacket.cpp
@@ -157,6 +157,7 @@ QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& h
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
// add the packet payload and the connection UUID
+ QMutexLocker hashLock(&hash.getLock());
hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
auto hashResult { hash.result() };
return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
From eab7dd60067ff18bab4a3c2f7d7b6c618bcf2945 Mon Sep 17 00:00:00 2001
From: Olivier Prat
Date: Wed, 25 Apr 2018 10:25:34 +0200
Subject: [PATCH 012/192] Fixed procedural shaders
---
libraries/render-utils/src/simple.slf | 10 ++++++++++
libraries/render-utils/src/simple_fade.slf | 10 ++++++++++
2 files changed, 20 insertions(+)
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index ed77777184..338f8607ee 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -16,7 +16,17 @@
// the interpolated normal
in vec3 _normalWS;
+in vec3 _normalMS;
in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
+in vec4 _positionES;
+
+// For retro-compatibility
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
//PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf
index 6e7aee2894..1cb4127e7b 100644
--- a/libraries/render-utils/src/simple_fade.slf
+++ b/libraries/render-utils/src/simple_fade.slf
@@ -19,9 +19,19 @@
// the interpolated normal
in vec3 _normalWS;
+in vec3 _normalMS;
in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
+in vec4 _positionES;
in vec4 _positionWS;
+// For retro-compatibility
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
+
//PROCEDURAL_COMMON_BLOCK
#line 1001
From bd72350287c31a12edc65094e05febcf2343f132 Mon Sep 17 00:00:00 2001
From: Triplelexx
Date: Wed, 25 Apr 2018 12:47:55 +0100
Subject: [PATCH 013/192] describe magic number
---
.../resources/qml/dialogs/assetDialog/AssetDialogContent.qml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
index 7c8a735ead..cd9ce226c0 100644
--- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
+++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
@@ -254,7 +254,8 @@ Item {
filterListStart = filter.indexOf("(");
filterListEnd = filter.indexOf(")");
if (filterListStart !== -1 && filterListEnd !== -1) {
- fileTypeFilters = filter.substring(filterListStart + 2, filterListEnd).toLowerCase().split("*");
+ var FIRST_EXTENSION_OFFSET = 2;
+ fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET, filterListEnd).toLowerCase().split("*");
} else if (filter !== "") {
fileTypeFilters[0] = filter.replace("*", "").toLowerCase();
}
From bd6b6c94232d7308842df7bddbff68a9b5e7b1b6 Mon Sep 17 00:00:00 2001
From: Triplelexx
Date: Wed, 25 Apr 2018 12:57:46 +0100
Subject: [PATCH 014/192] fix content within 128 columns
---
.../resources/qml/dialogs/assetDialog/AssetDialogContent.qml | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
index cd9ce226c0..ed442aa191 100644
--- a/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
+++ b/interface/resources/qml/dialogs/assetDialog/AssetDialogContent.qml
@@ -255,7 +255,8 @@ Item {
filterListEnd = filter.indexOf(")");
if (filterListStart !== -1 && filterListEnd !== -1) {
var FIRST_EXTENSION_OFFSET = 2;
- fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET, filterListEnd).toLowerCase().split("*");
+ fileTypeFilters = filter.substring(filterListStart + FIRST_EXTENSION_OFFSET
+ , filterListEnd).toLowerCase().split("*");
} else if (filter !== "") {
fileTypeFilters[0] = filter.replace("*", "").toLowerCase();
}
From 328f1dec9be27258b154130f51681ece09b76c97 Mon Sep 17 00:00:00 2001
From: Olivier Prat
Date: Wed, 25 Apr 2018 18:56:18 +0200
Subject: [PATCH 015/192] Extended to other shaders
---
libraries/render-utils/src/forward_simple.slf | 10 ++++++++++
.../render-utils/src/forward_simple_transparent.slf | 9 +++++++++
libraries/render-utils/src/simple.slf | 8 ++++----
libraries/render-utils/src/simple_fade.slf | 8 ++++----
libraries/render-utils/src/simple_transparent.slf | 10 ++++++++++
5 files changed, 37 insertions(+), 8 deletions(-)
diff --git a/libraries/render-utils/src/forward_simple.slf b/libraries/render-utils/src/forward_simple.slf
index 587fcbde73..1ac44750a7 100644
--- a/libraries/render-utils/src/forward_simple.slf
+++ b/libraries/render-utils/src/forward_simple.slf
@@ -16,11 +16,21 @@
<@include ForwardGlobalLight.slh@>
<$declareEvalSkyboxGlobalColor()$>
+
// the interpolated normal
in vec3 _normalWS;
+in vec3 _normalMS;
in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
in vec4 _positionES;
+// For retro-compatibility
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
+
layout(location = 0) out vec4 _fragColor0;
//PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/forward_simple_transparent.slf b/libraries/render-utils/src/forward_simple_transparent.slf
index f40ba2ed4f..8be2759571 100644
--- a/libraries/render-utils/src/forward_simple_transparent.slf
+++ b/libraries/render-utils/src/forward_simple_transparent.slf
@@ -18,9 +18,18 @@
// the interpolated normal
in vec3 _normalWS;
+in vec3 _normalMS;
in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
in vec4 _positionES;
+// For retro-compatibility
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
+
layout(location = 0) out vec4 _fragColor0;
//PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/simple.slf b/libraries/render-utils/src/simple.slf
index 338f8607ee..7591dc1882 100644
--- a/libraries/render-utils/src/simple.slf
+++ b/libraries/render-utils/src/simple.slf
@@ -23,10 +23,10 @@ in vec4 _positionMS;
in vec4 _positionES;
// For retro-compatibility
-#define _normal _normalWS
-#define _modelNormal _normalMS
-#define _position _positionMS
-#define _eyePosition _positionES
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
//PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/simple_fade.slf b/libraries/render-utils/src/simple_fade.slf
index 1cb4127e7b..0710c3e10b 100644
--- a/libraries/render-utils/src/simple_fade.slf
+++ b/libraries/render-utils/src/simple_fade.slf
@@ -27,10 +27,10 @@ in vec4 _positionES;
in vec4 _positionWS;
// For retro-compatibility
-#define _normal _normalWS
-#define _modelNormal _normalMS
-#define _position _positionMS
-#define _eyePosition _positionES
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
//PROCEDURAL_COMMON_BLOCK
diff --git a/libraries/render-utils/src/simple_transparent.slf b/libraries/render-utils/src/simple_transparent.slf
index c9815e8a80..ee79d2c0c4 100644
--- a/libraries/render-utils/src/simple_transparent.slf
+++ b/libraries/render-utils/src/simple_transparent.slf
@@ -16,7 +16,17 @@
// the interpolated normal
in vec3 _normalWS;
+in vec3 _normalMS;
in vec4 _color;
+in vec2 _texCoord0;
+in vec4 _positionMS;
+in vec4 _positionES;
+
+// For retro-compatibility
+#define _normal _normalWS
+#define _modelNormal _normalMS
+#define _position _positionMS
+#define _eyePosition _positionES
//PROCEDURAL_COMMON_BLOCK
From 3233cc43ac8d12a48e500130c6b7a91fbcff448c Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Wed, 25 Apr 2018 10:30:50 -0700
Subject: [PATCH 016/192] Revert "Merge pull request #12981 from
birarda/bug/revert-hmac"
This reverts commit 91b93db0c5f27a5b5c0282780ee56102078224be, reversing
changes made to 6062f21da114f34164ba80db7e31bf45b959626e.
---
libraries/networking/src/HMACAuth.cpp | 94 +++++++++++++++++++
libraries/networking/src/HMACAuth.h | 40 ++++++++
libraries/networking/src/LimitedNodeList.cpp | 36 +++----
libraries/networking/src/LimitedNodeList.h | 10 +-
libraries/networking/src/NLPacket.cpp | 16 ++--
libraries/networking/src/NLPacket.h | 8 +-
libraries/networking/src/Node.cpp | 14 ++-
libraries/networking/src/Node.h | 5 +-
.../networking/src/udt/PacketHeaders.cpp | 2 +-
9 files changed, 186 insertions(+), 39 deletions(-)
create mode 100644 libraries/networking/src/HMACAuth.cpp
create mode 100644 libraries/networking/src/HMACAuth.h
diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp
new file mode 100644
index 0000000000..42b5c48d93
--- /dev/null
+++ b/libraries/networking/src/HMACAuth.cpp
@@ -0,0 +1,94 @@
+//
+// HMACAuth.cpp
+// libraries/networking/src
+//
+// Created by Simon Walton on 3/19/2018.
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#include
+#include
+
+#include "HMACAuth.h"
+
+#include
+
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+HMACAuth::HMACAuth(AuthMethod authMethod)
+ : _hmacContext(HMAC_CTX_new())
+ , _authMethod(authMethod) { }
+
+HMACAuth::~HMACAuth()
+{
+ HMAC_CTX_free(_hmacContext);
+}
+
+#else
+
+HMACAuth::HMACAuth(AuthMethod authMethod)
+ : _hmacContext(new HMAC_CTX())
+ , _authMethod(authMethod) {
+ HMAC_CTX_init(_hmacContext);
+}
+
+HMACAuth::~HMACAuth() {
+ HMAC_CTX_cleanup(_hmacContext);
+ delete _hmacContext;
+}
+#endif
+
+bool HMACAuth::setKey(const char* keyValue, int keyLen) {
+ const EVP_MD* sslStruct = nullptr;
+
+ switch (_authMethod) {
+ case MD5:
+ sslStruct = EVP_md5();
+ break;
+
+ case SHA1:
+ sslStruct = EVP_sha1();
+ break;
+
+ case SHA224:
+ sslStruct = EVP_sha224();
+ break;
+
+ case SHA256:
+ sslStruct = EVP_sha256();
+ break;
+
+ case RIPEMD160:
+ sslStruct = EVP_ripemd160();
+ break;
+
+ default:
+ return false;
+ }
+
+ QMutexLocker lock(&_lock);
+ return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
+}
+
+bool HMACAuth::setKey(const QUuid& uidKey) {
+ const QByteArray rfcBytes(uidKey.toRfc4122());
+ return setKey(rfcBytes.constData(), rfcBytes.length());
+}
+
+bool HMACAuth::addData(const char* data, int dataLen) {
+ QMutexLocker lock(&_lock);
+ return (bool) HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen);
+}
+
+HMACAuth::HMACHash HMACAuth::result() {
+ HMACHash hashValue(EVP_MAX_MD_SIZE);
+ unsigned int hashLen;
+ QMutexLocker lock(&_lock);
+ HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
+ hashValue.resize((size_t) hashLen);
+ // Clear state for possible reuse.
+ HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
+ return hashValue;
+}
diff --git a/libraries/networking/src/HMACAuth.h b/libraries/networking/src/HMACAuth.h
new file mode 100644
index 0000000000..0bf7a86ec1
--- /dev/null
+++ b/libraries/networking/src/HMACAuth.h
@@ -0,0 +1,40 @@
+//
+// HMACAuth.h
+// libraries/networking/src
+//
+// Created by Simon Walton on 3/19/2018.
+// Copyright 2018 High Fidelity, Inc.
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
+
+#ifndef hifi_HMACAuth_h
+#define hifi_HMACAuth_h
+
+#include
+#include
+#include
+
+class QUuid;
+
+class HMACAuth {
+public:
+ enum AuthMethod { MD5, SHA1, SHA224, SHA256, RIPEMD160 };
+ using HMACHash = std::vector;
+
+ explicit HMACAuth(AuthMethod authMethod = MD5);
+ ~HMACAuth();
+
+ bool setKey(const char* keyValue, int keyLen);
+ bool setKey(const QUuid& uidKey);
+ bool addData(const char* data, int dataLen);
+ HMACHash result();
+
+private:
+ QMutex _lock;
+ struct hmac_ctx_st* _hmacContext;
+ AuthMethod _authMethod;
+};
+
+#endif // hifi_HMACAuth_h
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index 92385e99b0..8d177ca534 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -36,6 +36,7 @@
#include "HifiSockAddr.h"
#include "NetworkLogging.h"
#include "udt/Packet.h"
+#include "HMACAuth.h"
static Setting::Handle LIMITED_NODELIST_LOCAL_PORT("LimitedNodeList.LocalPort", 0);
@@ -330,7 +331,7 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
if (verifiedPacket && !ignoreVerification) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
- QByteArray expectedHash = NLPacket::hashForPacketAndSecret(packet, sourceNode->getConnectionSecret());
+ QByteArray expectedHash = NLPacket::hashForPacketAndHMAC(packet, sourceNode->getAuthenticateHash());
// check if the md5 hash in the header matches the hash we would expect
if (packetHeaderHash != expectedHash) {
@@ -370,15 +371,15 @@ void LimitedNodeList::collectPacketStats(const NLPacket& packet) {
_numCollectedBytes += packet.getDataSize();
}
-void LimitedNodeList::fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret) {
+void LimitedNodeList::fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth) {
if (!PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())) {
packet.writeSourceID(getSessionLocalID());
}
- if (!connectionSecret.isNull()
+ if (hmacAuth
&& !PacketTypeEnum::getNonSourcedPackets().contains(packet.getType())
&& !PacketTypeEnum::getNonVerifiedPackets().contains(packet.getType())) {
- packet.writeVerificationHashGivenSecret(connectionSecret);
+ packet.writeVerificationHash(*hmacAuth);
}
}
@@ -394,17 +395,17 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
emit dataSent(destinationNode.getType(), packet.getDataSize());
destinationNode.recordBytesSent(packet.getDataSize());
- return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getConnectionSecret());
+ return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), &destinationNode.getAuthenticateHash());
}
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret) {
+ HMACAuth* hmacAuth) {
Q_ASSERT(!packet.isPartOfMessage());
Q_ASSERT_X(!packet.isReliable(), "LimitedNodeList::sendUnreliablePacket",
"Trying to send a reliable packet unreliably.");
collectPacketStats(packet);
- fillPacketHeader(packet, connectionSecret);
+ fillPacketHeader(packet, hmacAuth);
return _nodeSocket.writePacket(packet, sockAddr);
}
@@ -417,7 +418,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
emit dataSent(destinationNode.getType(), packet->getDataSize());
destinationNode.recordBytesSent(packet->getDataSize());
- return sendPacket(std::move(packet), *activeSocket, destinationNode.getConnectionSecret());
+ return sendPacket(std::move(packet), *activeSocket, &destinationNode.getAuthenticateHash());
} else {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
return ERROR_SENDING_PACKET_BYTES;
@@ -425,18 +426,18 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
}
qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret) {
+ HMACAuth* hmacAuth) {
Q_ASSERT(!packet->isPartOfMessage());
if (packet->isReliable()) {
collectPacketStats(*packet);
- fillPacketHeader(*packet, connectionSecret);
+ fillPacketHeader(*packet, hmacAuth);
auto size = packet->getDataSize();
_nodeSocket.writePacket(std::move(packet), sockAddr);
return size;
} else {
- return sendUnreliablePacket(*packet, sockAddr, connectionSecret);
+ return sendUnreliablePacket(*packet, sockAddr, hmacAuth);
}
}
@@ -445,13 +446,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
if (activeSocket) {
qint64 bytesSent = 0;
- auto connectionSecret = destinationNode.getConnectionSecret();
+ auto& connectionHash = destinationNode.getAuthenticateHash();
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
- bytesSent += sendPacket(packetList.takeFront(), *activeSocket, connectionSecret);
+ bytesSent += sendPacket(packetList.takeFront(), *activeSocket,
+ &connectionHash);
}
emit dataSent(destinationNode.getType(), bytesSent);
@@ -464,14 +466,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
}
qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret) {
+ HMACAuth* hmacAuth) {
qint64 bytesSent = 0;
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
- bytesSent += sendPacket(packetList.takeFront(), sockAddr, connectionSecret);
+ bytesSent += sendPacket(packetList.takeFront(), sockAddr, hmacAuth);
}
return bytesSent;
@@ -499,7 +501,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList,
for (std::unique_ptr& packet : packetList->_packets) {
NLPacket* nlPacket = static_cast(packet.get());
collectPacketStats(*nlPacket);
- fillPacketHeader(*nlPacket, destinationNode.getConnectionSecret());
+ fillPacketHeader(*nlPacket, &destinationNode.getAuthenticateHash());
}
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
@@ -522,7 +524,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
: overridenSockAddr;
- return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getConnectionSecret());
+ return sendPacket(std::move(packet), destinationSockAddr, &destinationNode.getAuthenticateHash());
}
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer message, SharedNodePointer sendingNode) {
diff --git a/libraries/networking/src/LimitedNodeList.h b/libraries/networking/src/LimitedNodeList.h
index 3d6fd0cd91..05374bbfbb 100644
--- a/libraries/networking/src/LimitedNodeList.h
+++ b/libraries/networking/src/LimitedNodeList.h
@@ -138,19 +138,17 @@ public:
// use sendUnreliablePacket to send an unreliable packet (that you do not need to move)
// either to a node (via its active socket) or to a manual sockaddr
qint64 sendUnreliablePacket(const NLPacket& packet, const Node& destinationNode);
- qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret = QUuid());
+ qint64 sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
// use sendPacket to send a moved unreliable or reliable NL packet to a node's active socket or manual sockaddr
qint64 sendPacket(std::unique_ptr packet, const Node& destinationNode);
- qint64 sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret = QUuid());
+ qint64 sendPacket(std::unique_ptr packet, const HifiSockAddr& sockAddr, HMACAuth* hmacAuth = nullptr);
// use sendUnreliableUnorderedPacketList to unreliably send separate packets from the packet list
// either to a node's active socket or to a manual sockaddr
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const Node& destinationNode);
qint64 sendUnreliableUnorderedPacketList(NLPacketList& packetList, const HifiSockAddr& sockAddr,
- const QUuid& connectionSecret = QUuid());
+ HMACAuth* hmacAuth = nullptr);
// use sendPacketList to send reliable packet lists (ordered or unordered) to a node's active socket
// or to a manual sock addr
@@ -372,7 +370,7 @@ protected:
qint64 writePacket(const NLPacket& packet, const HifiSockAddr& destinationSockAddr,
const QUuid& connectionSecret = QUuid());
void collectPacketStats(const NLPacket& packet);
- void fillPacketHeader(const NLPacket& packet, const QUuid& connectionSecret = QUuid());
+ void fillPacketHeader(const NLPacket& packet, HMACAuth* hmacAuth = nullptr);
void setLocalSocket(const HifiSockAddr& sockAddr);
diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp
index ac3fbc966b..3355e1cd6b 100644
--- a/libraries/networking/src/NLPacket.cpp
+++ b/libraries/networking/src/NLPacket.cpp
@@ -11,6 +11,8 @@
#include "NLPacket.h"
+#include "HMACAuth.h"
+
int NLPacket::localHeaderSize(PacketType type) {
bool nonSourced = PacketTypeEnum::getNonSourcedPackets().contains(type);
bool nonVerified = PacketTypeEnum::getNonVerifiedPackets().contains(type);
@@ -150,18 +152,14 @@ QByteArray NLPacket::verificationHashInHeader(const udt::Packet& packet) {
return QByteArray(packet.getData() + offset, NUM_BYTES_MD5_HASH);
}
-QByteArray NLPacket::hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret) {
- QCryptographicHash hash(QCryptographicHash::Md5);
-
+QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash) {
int offset = Packet::totalHeaderSize(packet.isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
// add the packet payload and the connection UUID
hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
- hash.addData(connectionSecret.toRfc4122());
-
- // return the hash
- return hash.result();
+ auto hashResult { hash.result() };
+ return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
}
void NLPacket::writeTypeAndVersion() {
@@ -214,14 +212,14 @@ void NLPacket::writeSourceID(LocalID sourceID) const {
_sourceID = sourceID;
}
-void NLPacket::writeVerificationHashGivenSecret(const QUuid& connectionSecret) const {
+void NLPacket::writeVerificationHash(HMACAuth& hmacAuth) const {
Q_ASSERT(!PacketTypeEnum::getNonSourcedPackets().contains(_type) &&
!PacketTypeEnum::getNonVerifiedPackets().contains(_type));
auto offset = Packet::totalHeaderSize(isPartOfMessage()) + sizeof(PacketType) + sizeof(PacketVersion)
+ NUM_BYTES_LOCALID;
- QByteArray verificationHash = hashForPacketAndSecret(*this, connectionSecret);
+ QByteArray verificationHash = hashForPacketAndHMAC(*this, hmacAuth);
memcpy(_packet.get() + offset, verificationHash.data(), verificationHash.size());
}
diff --git a/libraries/networking/src/NLPacket.h b/libraries/networking/src/NLPacket.h
index 9b07bc6581..4103f2068e 100644
--- a/libraries/networking/src/NLPacket.h
+++ b/libraries/networking/src/NLPacket.h
@@ -18,6 +18,8 @@
#include "udt/Packet.h"
+class HMACAuth;
+
class NLPacket : public udt::Packet {
Q_OBJECT
public:
@@ -69,7 +71,7 @@ public:
static LocalID sourceIDInHeader(const udt::Packet& packet);
static QByteArray verificationHashInHeader(const udt::Packet& packet);
- static QByteArray hashForPacketAndSecret(const udt::Packet& packet, const QUuid& connectionSecret);
+ static QByteArray hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& hash);
PacketType getType() const { return _type; }
void setType(PacketType type);
@@ -78,9 +80,9 @@ public:
void setVersion(PacketVersion version);
LocalID getSourceID() const { return _sourceID; }
-
+
void writeSourceID(LocalID sourceID) const;
- void writeVerificationHashGivenSecret(const QUuid& connectionSecret) const;
+ void writeVerificationHash(HMACAuth& hmacAuth) const;
protected:
diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp
index 73b7c44e7e..7979b36e30 100644
--- a/libraries/networking/src/Node.cpp
+++ b/libraries/networking/src/Node.cpp
@@ -86,10 +86,10 @@ NodeType_t NodeType::fromString(QString type) {
Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
- const HifiSockAddr& localSocket, QObject* parent) :
+ const HifiSockAddr& localSocket, QObject* parent) :
NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type),
- _pingMs(-1), // "Uninitialized"
+ _authenticateHash(new HMACAuth), _pingMs(-1), // "Uninitialized"
_clockSkewUsec(0),
_mutex(),
_clockSkewMovingPercentile(30, 0.8f) // moving 80th percentile of 30 samples
@@ -108,6 +108,7 @@ void Node::setType(char type) {
_symmetricSocket.setObjectName(typeString);
}
+
void Node::updateClockSkewUsec(qint64 clockSkewSample) {
_clockSkewMovingPercentile.updatePercentile(clockSkewSample);
_clockSkewUsec = (quint64)_clockSkewMovingPercentile.getValueAtPercentile();
@@ -194,3 +195,12 @@ QDebug operator<<(QDebug debug, const Node& node) {
debug.nospace() << node.getPublicSocket() << "/" << node.getLocalSocket();
return debug.nospace();
}
+
+void Node::setConnectionSecret(const QUuid& connectionSecret) {
+ if (_connectionSecret == connectionSecret) {
+ return;
+ }
+
+ _connectionSecret = connectionSecret;
+ _authenticateHash->setKey(_connectionSecret);
+}
diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h
index 93b6a649d4..5b3b559582 100644
--- a/libraries/networking/src/Node.h
+++ b/libraries/networking/src/Node.h
@@ -33,6 +33,7 @@
#include "SimpleMovingAverage.h"
#include "MovingPercentile.h"
#include "NodePermissions.h"
+#include "HMACAuth.h"
class Node : public NetworkPeer {
Q_OBJECT
@@ -55,7 +56,8 @@ public:
void setIsUpstream(bool isUpstream) { _isUpstream = isUpstream; }
const QUuid& getConnectionSecret() const { return _connectionSecret; }
- void setConnectionSecret(const QUuid& connectionSecret) { _connectionSecret = connectionSecret; }
+ void setConnectionSecret(const QUuid& connectionSecret);
+ HMACAuth& getAuthenticateHash() const { return *_authenticateHash; }
NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(std::unique_ptr linkedData) { _linkedData = std::move(linkedData); }
@@ -97,6 +99,7 @@ private:
NodeType_t _type;
QUuid _connectionSecret;
+ std::unique_ptr _authenticateHash;
std::unique_ptr _linkedData;
bool _isReplicated { false };
int _pingMs;
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 98b0e1d892..db466a0403 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -91,7 +91,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::Ping:
return static_cast(PingVersion::IncludeConnectionID);
default:
- return 20;
+ return 19;
}
}
From 44816941fce7644083171ca8c5b98f0005de3061 Mon Sep 17 00:00:00 2001
From: Brad Davis
Date: Wed, 25 Apr 2018 10:57:57 -0700
Subject: [PATCH 017/192] Fix debug assert in manipulating thread local data to
store GL surfaces
---
libraries/gl/src/gl/OffscreenGLCanvas.cpp | 45 +++++++++++++++++++----
libraries/gl/src/gl/OffscreenGLCanvas.h | 2 +
2 files changed, 40 insertions(+), 7 deletions(-)
diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.cpp b/libraries/gl/src/gl/OffscreenGLCanvas.cpp
index 4a2c5fd7f7..91f7954943 100644
--- a/libraries/gl/src/gl/OffscreenGLCanvas.cpp
+++ b/libraries/gl/src/gl/OffscreenGLCanvas.cpp
@@ -17,15 +17,18 @@
#include
#include
#include
+#include
+#include
#include
#include
#include
+#include
+
#include "Context.h"
#include "GLHelpers.h"
#include "GLLogging.h"
-
OffscreenGLCanvas::OffscreenGLCanvas() :
_context(new QOpenGLContext),
_offscreenSurface(new QOffscreenSurface)
@@ -33,6 +36,8 @@ OffscreenGLCanvas::OffscreenGLCanvas() :
}
OffscreenGLCanvas::~OffscreenGLCanvas() {
+ clearThreadContext();
+
// A context with logging enabled needs to be current when it's destroyed
_context->makeCurrent(_offscreenSurface);
delete _context;
@@ -117,25 +122,51 @@ QObject* OffscreenGLCanvas::getContextObject() {
}
void OffscreenGLCanvas::moveToThreadWithContext(QThread* thread) {
+ clearThreadContext();
moveToThread(thread);
_context->moveToThread(thread);
}
-static const char* THREAD_CONTEXT_PROPERTY = "offscreenGlCanvas";
+struct ThreadContextStorage : public Dependency {
+ QThreadStorage> threadContext;
+};
void OffscreenGLCanvas::setThreadContext() {
- QThread::currentThread()->setProperty(THREAD_CONTEXT_PROPERTY, QVariant::fromValue(this));
+ if (!DependencyManager::isSet()) {
+ DependencyManager::set();
+ }
+ auto threadContextStorage = DependencyManager::get();
+ QPointer p(this);
+ threadContextStorage->threadContext.setLocalData(p);
+}
+
+void OffscreenGLCanvas::clearThreadContext() {
+ if (!DependencyManager::isSet()) {
+ return;
+ }
+ auto threadContextStorage = DependencyManager::get();
+ if (!threadContextStorage->threadContext.hasLocalData()) {
+ return;
+ }
+ auto& threadContext = threadContextStorage->threadContext.localData();
+ if (this != threadContext.operator OffscreenGLCanvas *()) {
+ return;
+ }
+ threadContextStorage->threadContext.setLocalData(nullptr);
}
bool OffscreenGLCanvas::restoreThreadContext() {
// Restore the rendering context for this thread
- auto threadCanvasVariant = QThread::currentThread()->property(THREAD_CONTEXT_PROPERTY);
- if (!threadCanvasVariant.isValid()) {
+ if (!DependencyManager::isSet()) {
return false;
}
- auto threadCanvasObject = qvariant_cast(threadCanvasVariant);
- auto threadCanvas = static_cast(threadCanvasObject);
+ auto threadContextStorage = DependencyManager::get();
+ if (!threadContextStorage->threadContext.hasLocalData()) {
+ return false;
+ }
+
+ auto threadCanvas = threadContextStorage->threadContext.localData();
if (!threadCanvas) {
return false;
}
diff --git a/libraries/gl/src/gl/OffscreenGLCanvas.h b/libraries/gl/src/gl/OffscreenGLCanvas.h
index ed644b98fb..a4960ae234 100644
--- a/libraries/gl/src/gl/OffscreenGLCanvas.h
+++ b/libraries/gl/src/gl/OffscreenGLCanvas.h
@@ -39,6 +39,8 @@ private slots:
void onMessageLogged(const QOpenGLDebugMessage &debugMessage);
protected:
+ void clearThreadContext();
+
std::once_flag _reportOnce;
QOpenGLContext* _context{ nullptr };
QOffscreenSurface* _offscreenSurface{ nullptr };
From 4fa11f116fb17847c5bdcee167301f16d94cc2ce Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Wed, 25 Apr 2018 14:41:16 -0700
Subject: [PATCH 018/192] Add & use HMACAuth method that calculates hash
atomically
To avoid issues with race conditions involving separate
addData() and result() calls use a method that does
both in the lock.
---
libraries/networking/src/HMACAuth.cpp | 25 +++++++++++++++++++++----
libraries/networking/src/HMACAuth.h | 5 +++--
libraries/networking/src/NLPacket.cpp | 7 ++++---
3 files changed, 28 insertions(+), 9 deletions(-)
diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp
index e276e64b40..8aa2fea6a9 100644
--- a/libraries/networking/src/HMACAuth.cpp
+++ b/libraries/networking/src/HMACAuth.cpp
@@ -68,7 +68,7 @@ bool HMACAuth::setKey(const char* keyValue, int keyLen) {
return false;
}
- //QMutexLocker lock(&_lock);
+ QMutexLocker lock(&_lock);
return (bool) HMAC_Init_ex(_hmacContext, keyValue, keyLen, sslStruct, nullptr);
}
@@ -78,14 +78,14 @@ bool HMACAuth::setKey(const QUuid& uidKey) {
}
bool HMACAuth::addData(const char* data, int dataLen) {
- //QMutexLocker lock(&_lock);
+ QMutexLocker lock(&_lock);
return (bool) HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen);
}
HMACAuth::HMACHash HMACAuth::result() {
HMACHash hashValue(EVP_MAX_MD_SIZE);
- unsigned int hashLen;
- //QMutexLocker lock(&_lock);
+ unsigned int hashLen;
+ QMutexLocker lock(&_lock);
if (HMAC_Final(_hmacContext, &hashValue[0], &hashLen)) {
hashValue.resize((size_t)hashLen);
}
@@ -93,3 +93,20 @@ HMACAuth::HMACHash HMACAuth::result() {
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
return hashValue;
}
+
+bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) {
+ QMutexLocker lock(&_lock);
+ if (!HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen)) {
+ return false;
+ }
+
+ hashResult.resize(EVP_MAX_MD_SIZE);
+ unsigned int hashLen;
+ if (HMAC_Final(_hmacContext, &hashResult[0], &hashLen)) {
+ hashResult.resize((size_t)hashLen);
+ // Clear state for possible reuse.
+ HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
+ return true;
+ }
+ return false;
+}
diff --git a/libraries/networking/src/HMACAuth.h b/libraries/networking/src/HMACAuth.h
index ba1ec78214..ec06bfbda2 100644
--- a/libraries/networking/src/HMACAuth.h
+++ b/libraries/networking/src/HMACAuth.h
@@ -26,10 +26,11 @@ public:
explicit HMACAuth(AuthMethod authMethod = MD5);
~HMACAuth();
- QMutex& getLock() { return _lock; }
-
bool setKey(const char* keyValue, int keyLen);
bool setKey(const QUuid& uidKey);
+ // Calculate complete hash in one.
+ bool calculateHash(HMACHash& hashResult, const char* data, int dataLen);
+ // Append data to be hashed.
bool addData(const char* data, int dataLen);
HMACHash result();
diff --git a/libraries/networking/src/NLPacket.cpp b/libraries/networking/src/NLPacket.cpp
index 09a7b78840..f946e97bf4 100644
--- a/libraries/networking/src/NLPacket.cpp
+++ b/libraries/networking/src/NLPacket.cpp
@@ -157,9 +157,10 @@ QByteArray NLPacket::hashForPacketAndHMAC(const udt::Packet& packet, HMACAuth& h
+ NUM_BYTES_LOCALID + NUM_BYTES_MD5_HASH;
// add the packet payload and the connection UUID
- QMutexLocker hashLock(&hash.getLock());
- hash.addData(packet.getData() + offset, packet.getDataSize() - offset);
- auto hashResult { hash.result() };
+ HMACAuth::HMACHash hashResult;
+ if (!hash.calculateHash(hashResult, packet.getData() + offset, packet.getDataSize() - offset)) {
+ return QByteArray();
+ }
return QByteArray((const char*) hashResult.data(), (int) hashResult.size());
}
From 66bd424ae4002a75cd3003f4e97df1322dd2143f Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Wed, 25 Apr 2018 14:51:11 -0700
Subject: [PATCH 019/192] Bump default packet version for new HMAC
---
libraries/networking/src/udt/PacketHeaders.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index db466a0403..98b0e1d892 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -91,7 +91,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::Ping:
return static_cast(PingVersion::IncludeConnectionID);
default:
- return 19;
+ return 20;
}
}
From 9ef56c44a3da38161741cf4dc62f49e786c8e5b1 Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Wed, 25 Apr 2018 17:08:08 -0700
Subject: [PATCH 020/192] Merge PR12964 to not use verification if not keyed
https://github.com/highfidelity/hifi/pull/12964
---
libraries/networking/src/HMACAuth.cpp | 20 +++++++++++++++++---
libraries/networking/src/LimitedNodeList.cpp | 20 ++++++++++++--------
libraries/networking/src/Node.cpp | 6 +++++-
libraries/networking/src/Node.h | 4 ++--
4 files changed, 36 insertions(+), 14 deletions(-)
diff --git a/libraries/networking/src/HMACAuth.cpp b/libraries/networking/src/HMACAuth.cpp
index 8aa2fea6a9..8e57978b5a 100644
--- a/libraries/networking/src/HMACAuth.cpp
+++ b/libraries/networking/src/HMACAuth.cpp
@@ -9,12 +9,14 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "HMACAuth.h"
+
#include
#include
-#include "HMACAuth.h"
-
#include
+#include "NetworkLogging.h"
+#include
#if OPENSSL_VERSION_NUMBER >= 0x10100000
HMACAuth::HMACAuth(AuthMethod authMethod)
@@ -86,9 +88,17 @@ HMACAuth::HMACHash HMACAuth::result() {
HMACHash hashValue(EVP_MAX_MD_SIZE);
unsigned int hashLen;
QMutexLocker lock(&_lock);
- if (HMAC_Final(_hmacContext, &hashValue[0], &hashLen)) {
+
+ auto hmacResult = HMAC_Final(_hmacContext, &hashValue[0], &hashLen);
+
+ if (hmacResult) {
hashValue.resize((size_t)hashLen);
+ } else {
+ // the HMAC_FINAL call failed - should not be possible to get into this state
+ qCWarning(networking) << "Error occured calling HMAC_Final";
+ assert(hmacResult);
}
+
// Clear state for possible reuse.
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
return hashValue;
@@ -97,6 +107,8 @@ HMACAuth::HMACHash HMACAuth::result() {
bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen) {
QMutexLocker lock(&_lock);
if (!HMAC_Update(_hmacContext, reinterpret_cast(data), dataLen)) {
+ qCWarning(networking) << "Error occured calling HMAC_Update";
+ assert(false);
return false;
}
@@ -108,5 +120,7 @@ bool HMACAuth::calculateHash(HMACHash& hashResult, const char* data, int dataLen
HMAC_Init_ex(_hmacContext, nullptr, 0, nullptr, nullptr);
return true;
}
+ qCWarning(networking) << "Error occured calling HMAC_Final";
+ assert(false);
return false;
}
diff --git a/libraries/networking/src/LimitedNodeList.cpp b/libraries/networking/src/LimitedNodeList.cpp
index d66dd644d5..5457e206f5 100644
--- a/libraries/networking/src/LimitedNodeList.cpp
+++ b/libraries/networking/src/LimitedNodeList.cpp
@@ -331,10 +331,14 @@ bool LimitedNodeList::packetSourceAndHashMatchAndTrackBandwidth(const udt::Packe
if (verifiedPacket && !ignoreVerification) {
QByteArray packetHeaderHash = NLPacket::verificationHashInHeader(packet);
- QByteArray expectedHash = NLPacket::hashForPacketAndHMAC(packet, sourceNode->getAuthenticateHash());
+ QByteArray expectedHash;
+ auto sourceNodeHMACAuth = sourceNode->getAuthenticateHash();
+ if (sourceNode->getAuthenticateHash()) {
+ expectedHash = NLPacket::hashForPacketAndHMAC(packet, *sourceNodeHMACAuth);
+ }
// check if the HMAC-md5 hash in the header matches the hash we would expect
- if (packetHeaderHash != expectedHash) {
+ if (!sourceNodeHMACAuth || packetHeaderHash != expectedHash) {
static QMultiMap hashDebugSuppressMap;
if (!hashDebugSuppressMap.contains(sourceID, headerType)) {
@@ -396,7 +400,7 @@ qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const Node&
emit dataSent(destinationNode.getType(), packet.getDataSize());
destinationNode.recordBytesSent(packet.getDataSize());
- return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), &destinationNode.getAuthenticateHash());
+ return sendUnreliablePacket(packet, *destinationNode.getActiveSocket(), destinationNode.getAuthenticateHash());
}
qint64 LimitedNodeList::sendUnreliablePacket(const NLPacket& packet, const HifiSockAddr& sockAddr,
@@ -419,7 +423,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
emit dataSent(destinationNode.getType(), packet->getDataSize());
destinationNode.recordBytesSent(packet->getDataSize());
- return sendPacket(std::move(packet), *activeSocket, &destinationNode.getAuthenticateHash());
+ return sendPacket(std::move(packet), *activeSocket, destinationNode.getAuthenticateHash());
} else {
qCDebug(networking) << "LimitedNodeList::sendPacket called without active socket for node" << destinationNode << "- not sending";
return ERROR_SENDING_PACKET_BYTES;
@@ -447,14 +451,14 @@ qint64 LimitedNodeList::sendUnreliableUnorderedPacketList(NLPacketList& packetLi
if (activeSocket) {
qint64 bytesSent = 0;
- auto& connectionHash = destinationNode.getAuthenticateHash();
+ auto connectionHash = destinationNode.getAuthenticateHash();
// close the last packet in the list
packetList.closeCurrentPacket();
while (!packetList._packets.empty()) {
bytesSent += sendPacket(packetList.takeFront(), *activeSocket,
- &connectionHash);
+ connectionHash);
}
emit dataSent(destinationNode.getType(), bytesSent);
@@ -502,7 +506,7 @@ qint64 LimitedNodeList::sendPacketList(std::unique_ptr packetList,
for (std::unique_ptr& packet : packetList->_packets) {
NLPacket* nlPacket = static_cast(packet.get());
collectPacketStats(*nlPacket);
- fillPacketHeader(*nlPacket, &destinationNode.getAuthenticateHash());
+ fillPacketHeader(*nlPacket, destinationNode.getAuthenticateHash());
}
return _nodeSocket.writePacketList(std::move(packetList), *activeSocket);
@@ -525,7 +529,7 @@ qint64 LimitedNodeList::sendPacket(std::unique_ptr packet, const Node&
auto& destinationSockAddr = (overridenSockAddr.isNull()) ? *destinationNode.getActiveSocket()
: overridenSockAddr;
- return sendPacket(std::move(packet), destinationSockAddr, &destinationNode.getAuthenticateHash());
+ return sendPacket(std::move(packet), destinationSockAddr, destinationNode.getAuthenticateHash());
}
int LimitedNodeList::updateNodeWithDataFromPacket(QSharedPointer message, SharedNodePointer sendingNode) {
diff --git a/libraries/networking/src/Node.cpp b/libraries/networking/src/Node.cpp
index 7979b36e30..8fd8131b10 100644
--- a/libraries/networking/src/Node.cpp
+++ b/libraries/networking/src/Node.cpp
@@ -89,7 +89,7 @@ Node::Node(const QUuid& uuid, NodeType_t type, const HifiSockAddr& publicSocket,
const HifiSockAddr& localSocket, QObject* parent) :
NetworkPeer(uuid, publicSocket, localSocket, parent),
_type(type),
- _authenticateHash(new HMACAuth), _pingMs(-1), // "Uninitialized"
+ _pingMs(-1), // "Uninitialized"
_clockSkewUsec(0),
_mutex(),
_clockSkewMovingPercentile(30, 0.8f) // moving 80th percentile of 30 samples
@@ -201,6 +201,10 @@ void Node::setConnectionSecret(const QUuid& connectionSecret) {
return;
}
+ if (!_authenticateHash) {
+ _authenticateHash.reset(new HMACAuth());
+ }
+
_connectionSecret = connectionSecret;
_authenticateHash->setKey(_connectionSecret);
}
diff --git a/libraries/networking/src/Node.h b/libraries/networking/src/Node.h
index 5b3b559582..bcfe525aa8 100644
--- a/libraries/networking/src/Node.h
+++ b/libraries/networking/src/Node.h
@@ -57,7 +57,7 @@ public:
const QUuid& getConnectionSecret() const { return _connectionSecret; }
void setConnectionSecret(const QUuid& connectionSecret);
- HMACAuth& getAuthenticateHash() const { return *_authenticateHash; }
+ HMACAuth* getAuthenticateHash() const { return _authenticateHash.get(); }
NodeData* getLinkedData() const { return _linkedData.get(); }
void setLinkedData(std::unique_ptr linkedData) { _linkedData = std::move(linkedData); }
@@ -99,7 +99,7 @@ private:
NodeType_t _type;
QUuid _connectionSecret;
- std::unique_ptr _authenticateHash;
+ std::unique_ptr _authenticateHash { nullptr };
std::unique_ptr _linkedData;
bool _isReplicated { false };
int _pingMs;
From 496b638a885ef34cf66fe541e925aa61cef37b59 Mon Sep 17 00:00:00 2001
From: Ryan Huffman
Date: Thu, 26 Apr 2018 14:31:31 -0700
Subject: [PATCH 021/192] Fix log file directory not being capped at 50MB
---
libraries/shared/src/shared/FileLogger.cpp | 35 +++++++++++-----------
1 file changed, 17 insertions(+), 18 deletions(-)
diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp
index 8ceb378574..4f4a9ab6dd 100644
--- a/libraries/shared/src/shared/FileLogger.cpp
+++ b/libraries/shared/src/shared/FileLogger.cpp
@@ -36,17 +36,15 @@ protected:
private:
const FileLogger& _logger;
QMutex _fileMutex;
- uint64_t _lastRollTime;
+ std::chrono::system_clock::time_point _lastRollTime;
};
-
-
static const QString FILENAME_FORMAT = "hifi-log_%1%2.txt";
static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
static const QString LOGS_DIRECTORY = "Logs";
-static const QString IPADDR_WILDCARD = "[0-9]*.[0-9]*.[0-9]*.[0-9]*";
-static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[0,1][0-9]-[0-3][0-9]_[0-2][0-9].[0-6][0-9].[0-6][0-9]";
-static const QString FILENAME_WILDCARD = "hifi-log_" + IPADDR_WILDCARD + "_" + DATETIME_WILDCARD + ".txt";
+static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[01][0-9]-[0-3][0-9]_[0-2][0-9]\\.[0-6][0-9]\\.[0-6][0-9]";
+static const QString SESSION_WILDCARD = "[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}";
+static QRegExp LOG_FILENAME_REGEX { "hifi-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\.txt" };
static QUuid SESSION_ID;
// Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively
@@ -104,20 +102,21 @@ void FilePersistThread::rollFileIfNecessary(QFile& file, bool notifyListenersIfR
_lastRollTime = now;
}
- QStringList nameFilters;
- nameFilters << FILENAME_WILDCARD;
- QDir logQDir(FileUtils::standardPath(LOGS_DIRECTORY));
- logQDir.setNameFilters(nameFilters);
- logQDir.setSorting(QDir::Time);
- QFileInfoList filesInDir = logQDir.entryInfoList();
+ QDir logDir(FileUtils::standardPath(LOGS_DIRECTORY));
+ logDir.setSorting(QDir::Time);
+ logDir.setFilter(QDir::Files);
qint64 totalSizeOfDir = 0;
- foreach(QFileInfo dirItm, filesInDir){
- if (totalSizeOfDir < MAX_LOG_DIR_SIZE){
- totalSizeOfDir += dirItm.size();
- } else {
- QFile file(dirItm.filePath());
- file.remove();
+ QFileInfoList filesInDir = logDir.entryInfoList();
+ for (auto& fileInfo : filesInDir) {
+ if (!LOG_FILENAME_REGEX.exactMatch(fileInfo.fileName())) {
+ continue;
+ }
+ totalSizeOfDir += fileInfo.size();
+ if (totalSizeOfDir > MAX_LOG_DIR_SIZE){
+ qDebug() << "Removing log file: " << fileInfo.fileName();
+ QFile oldLogFile(fileInfo.filePath());
+ oldLogFile.remove();
}
}
}
From 1448f3959ebe541fe9457e70b2aae0e0598e6b04 Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Thu, 26 Apr 2018 17:11:17 -0700
Subject: [PATCH 022/192] Add a utility function to dump ConnectionStats
---
.../networking/src/udt/ConnectionStats.cpp | 29 +++++++++++++++++++
.../networking/src/udt/ConnectionStats.h | 3 ++
2 files changed, 32 insertions(+)
diff --git a/libraries/networking/src/udt/ConnectionStats.cpp b/libraries/networking/src/udt/ConnectionStats.cpp
index e7efe3d5af..46d88e680f 100644
--- a/libraries/networking/src/udt/ConnectionStats.cpp
+++ b/libraries/networking/src/udt/ConnectionStats.cpp
@@ -9,6 +9,7 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include
#include "ConnectionStats.h"
using namespace udt;
@@ -112,3 +113,31 @@ void ConnectionStats::recordPacketSendPeriod(int sample) {
_currentSample.packetSendPeriod = sample;
_total.packetSendPeriod = (int)((_total.packetSendPeriod * EWMA_PREVIOUS_SAMPLES_WEIGHT) + (sample * EWMA_CURRENT_SAMPLE_WEIGHT));
}
+
+QDebug& operator<<(QDebug&& debug, const udt::ConnectionStats::Stats& stats) {
+ debug << "Connection stats:\n";
+#define HIFI_LOG_EVENT(x) << " " #x " events: " << stats.events[ConnectionStats::Stats::Event::x] << "\n"
+ debug
+ HIFI_LOG_EVENT(SentACK)
+ HIFI_LOG_EVENT(ReceivedACK)
+ HIFI_LOG_EVENT(ProcessedACK)
+ HIFI_LOG_EVENT(SentLightACK)
+ HIFI_LOG_EVENT(ReceivedLightACK)
+ HIFI_LOG_EVENT(SentACK2)
+ HIFI_LOG_EVENT(ReceivedACK2)
+ HIFI_LOG_EVENT(SentNAK)
+ HIFI_LOG_EVENT(ReceivedNAK)
+ HIFI_LOG_EVENT(SentTimeoutNAK)
+ HIFI_LOG_EVENT(ReceivedTimeoutNAK)
+ HIFI_LOG_EVENT(Retransmission)
+ HIFI_LOG_EVENT(Duplicate)
+ ;
+#undef HIFI_LOG_EVENT
+
+ debug << " Sent packets: " << stats.sentPackets;
+ debug << "\n Received packets: " << stats.receivedPackets;
+ debug << "\n Sent util bytes: " << stats.sentUtilBytes;
+ debug << "\n Sent bytes: " << stats.sentBytes;
+ debug << "\n Received bytes: " << stats.receivedBytes << "\n";
+ return debug;
+}
diff --git a/libraries/networking/src/udt/ConnectionStats.h b/libraries/networking/src/udt/ConnectionStats.h
index 84cd6b2486..7ec7b163ee 100644
--- a/libraries/networking/src/udt/ConnectionStats.h
+++ b/libraries/networking/src/udt/ConnectionStats.h
@@ -101,4 +101,7 @@ private:
}
+class QDebug;
+QDebug& operator<<(QDebug&& debug, const udt::ConnectionStats::Stats& stats);
+
#endif // hifi_ConnectionStats_h
From 290c31d9162a6e81adbf57530feeff9393d2c732 Mon Sep 17 00:00:00 2001
From: Dante Ruiz
Date: Fri, 27 Apr 2018 11:33:45 -0700
Subject: [PATCH 023/192] don't spam resetSensors
---
interface/src/Application.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index c38caca090..18ba4958aa 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3554,7 +3554,7 @@ void Application::keyPressEvent(QKeyEvent* event) {
} else {
showCursor(Cursor::Icon::DEFAULT);
}
- } else {
+ } else if (!event->isAutoRepeat()){
resetSensors(true);
}
break;
From bfbded7beca85e58c1b87801f715d3cee678f027 Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Fri, 27 Apr 2018 16:57:24 -0700
Subject: [PATCH 024/192] Reset pending domain checkins in more locations
---
libraries/networking/src/DomainHandler.cpp | 3 +++
libraries/networking/src/DomainHandler.h | 2 +-
libraries/networking/src/NodeList.cpp | 6 +++++-
3 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp
index cd8064c4c0..84af21004d 100644
--- a/libraries/networking/src/DomainHandler.cpp
+++ b/libraries/networking/src/DomainHandler.cpp
@@ -239,6 +239,7 @@ void DomainHandler::activateICELocalSocket() {
_domainURL.setHost(_sockAddr.getAddress().toString());
emit domainURLChanged(_domainURL);
emit completedSocketDiscovery();
+ clearPendingCheckins(); // Clear outstanding count.
}
void DomainHandler::activateICEPublicSocket() {
@@ -248,6 +249,7 @@ void DomainHandler::activateICEPublicSocket() {
_domainURL.setHost(_sockAddr.getAddress().toString());
emit domainURLChanged(_domainURL);
emit completedSocketDiscovery();
+ clearPendingCheckins(); // Clear outstanding count.
}
QString DomainHandler::getViewPointFromNamedPath(QString namedPath) {
@@ -339,6 +341,7 @@ void DomainHandler::processSettingsPacketList(QSharedPointer pa
qCDebug(networking) << "Received domain settings: \n" << _settingsObject;
}
+ clearPendingCheckins(); // Reset outstanding check-ins.
emit settingsReceived(_settingsObject);
}
diff --git a/libraries/networking/src/DomainHandler.h b/libraries/networking/src/DomainHandler.h
index 2d4712209d..08908dbaf6 100644
--- a/libraries/networking/src/DomainHandler.h
+++ b/libraries/networking/src/DomainHandler.h
@@ -97,7 +97,7 @@ public:
int getCheckInPacketsSinceLastReply() const { return _checkInPacketsSinceLastReply; }
void sentCheckInPacket();
- void domainListReceived() { _checkInPacketsSinceLastReply = 0; }
+ void clearPendingCheckins() { _checkInPacketsSinceLastReply = 0; }
/**jsdoc
* The reasons that you may be refused connection to a domain are defined by numeric values:
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 04e32f50cb..9d3e5f0e91 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -534,6 +534,8 @@ void NodeList::processDomainServerPathResponse(QSharedPointer m
qCDebug(networking) << "Could not go to viewpoint" << viewpoint
<< "which was the lookup result for path" << pathQuery;
}
+
+ _domainHandler.clearPendingCheckins();
}
void NodeList::handleICEConnectionToDomainServer() {
@@ -594,6 +596,8 @@ void NodeList::processDomainServerConnectionTokenPacket(QSharedPointerreadWithoutCopy(NUM_BYTES_RFC4122_UUID)));
+
+ _domainHandler.clearPendingCheckins();
sendDomainServerCheckIn();
}
@@ -605,7 +609,7 @@ void NodeList::processDomainServerList(QSharedPointer message)
}
// this is a packet from the domain server, reset the count of un-replied check-ins
- _domainHandler.domainListReceived();
+ _domainHandler.clearPendingCheckins();
// emit our signal so listeners know we just heard from the DS
emit receivedDomainServerList();
From cf9b089a3c523c26fbe0a60ee142702ee5969971 Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Fri, 27 Apr 2018 17:18:16 -0700
Subject: [PATCH 025/192] Added option to set snapshot location (for this
execution only) from the command line.
---
interface/src/Application.cpp | 15 ++++++++++++---
interface/src/Application.h | 2 ++
interface/src/ui/Snapshot.cpp | 12 +++++++++---
interface/src/ui/Snapshot.h | 4 ++--
4 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index c38caca090..c0fe84894d 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -986,13 +986,22 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
setProperty(hifi::properties::STEAM, (steamClient && steamClient->isRunning()));
setProperty(hifi::properties::CRASHED, _previousSessionCrashed);
{
- const QString TEST_SCRIPT = "--testScript";
+ const QString TEST_SCRIPT { "--testScript" };
+ const QString TEST_SNAPSHOT_LOCATION { "--testSnapshotLocation" };
+
const QStringList args = arguments();
for (int i = 0; i < args.size() - 1; ++i) {
if (args.at(i) == TEST_SCRIPT) {
QString testScriptPath = args.at(i + 1);
if (QFileInfo(testScriptPath).exists()) {
setProperty(hifi::properties::TEST, QUrl::fromLocalFile(testScriptPath));
+ }
+ } else if (args.at(i) == TEST_SNAPSHOT_LOCATION) {
+ // Set test snapshot location only if it is a writeable directory
+ QString pathname(args.at(i + 1));
+ QFileInfo fileInfo(pathname);
+ if (fileInfo.isDir() && fileInfo.isWritable()) {
+ testSnapshotLocation = pathname;
}
}
}
@@ -7259,7 +7268,7 @@ void Application::loadAvatarBrowser() const {
void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRatio, const QString& filename) {
postLambdaEvent([notify, includeAnimated, aspectRatio, filename, this] {
// Get a screenshot and save it
- QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename);
+ QString path = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getScreenshot(aspectRatio), filename, QString());
// If we're not doing an animated snapshot as well...
if (!includeAnimated) {
// Tell the dependency manager that the capture of the still snapshot has taken place.
@@ -7273,7 +7282,7 @@ void Application::takeSnapshot(bool notify, bool includeAnimated, float aspectRa
void Application::takeSecondaryCameraSnapshot(const QString& filename) {
postLambdaEvent([filename, this] {
- QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename);
+ QString snapshotPath = Snapshot::saveSnapshot(getActiveDisplayPlugin()->getSecondaryCameraScreenshot(), filename, testSnapshotLocation);
emit DependencyManager::get()->stillSnapshotTaken(snapshotPath, true);
});
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 74b0e5a110..341560bc5b 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -719,5 +719,7 @@ private:
std::atomic _pendingIdleEvent { true };
std::atomic _pendingRenderEvent { true };
+
+ QString testSnapshotLocation { QString() };
};
#endif // hifi_Application_h
diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp
index 69103a40b5..9eb64dcb14 100644
--- a/interface/src/ui/Snapshot.cpp
+++ b/interface/src/ui/Snapshot.cpp
@@ -89,10 +89,10 @@ QString Snapshot::saveSnapshot(QImage image, const QString& filename) {
QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
// return whatever we get back from saved file for snapshot
- return static_cast(savedFileForSnapshot(image, true));
+ return static_cast(savedFileForSnapshot(image, true, QString(), QString()));
}
-QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename) {
+QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, QString userSelectedPathname) {
// adding URL to snapshot
QUrl currentURL = DependencyManager::get()->currentPublicAddress();
@@ -117,7 +117,13 @@ QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QSt
const int IMAGE_QUALITY = 100;
if (!isTemporary) {
- QString snapshotFullPath = snapshotsLocation.get();
+ // If user has requested specific path then use it, else use the application value
+ QString snapshotFullPath;
+ if (!userSelectedPathname.isNull()) {
+ snapshotFullPath = userSelectedPathname;
+ } else {
+ snapshotFullPath = snapshotsLocation.get();
+ }
if (snapshotFullPath.isEmpty()) {
snapshotFullPath = OffscreenUi::getExistingDirectory(nullptr, "Choose Snapshots Directory", QStandardPaths::writableLocation(QStandardPaths::DesktopLocation));
diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h
index 62d3ed3db8..20e9e8339f 100644
--- a/interface/src/ui/Snapshot.h
+++ b/interface/src/ui/Snapshot.h
@@ -37,7 +37,7 @@ class Snapshot : public QObject, public Dependency {
Q_OBJECT
SINGLETON_DEPENDENCY
public:
- static QString saveSnapshot(QImage image, const QString& filename);
+ static QString saveSnapshot(QImage image, const QString& filename, const QString& pathname);
static QTemporaryFile* saveTempSnapshot(QImage image);
static SnapshotMetaData* parseSnapshotData(QString snapshotPath);
@@ -51,7 +51,7 @@ public slots:
Q_INVOKABLE QString getSnapshotsLocation();
Q_INVOKABLE void setSnapshotsLocation(const QString& location);
private:
- static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename = QString());
+ static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename, QString userSelectedPathname);
};
#endif // hifi_Snapshot_h
From 2cc3ed6287afbd5df4cb65a6f42f6389671eeabe Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Sat, 28 Apr 2018 20:00:28 +1200
Subject: [PATCH 026/192] List which contexts each namespace and object is
available in
---
interface/src/AvatarBookmarks.h | 4 ++++
interface/src/LODManager.h | 5 ++++-
interface/src/SpeechRecognizer.h | 3 +++
interface/src/audio/AudioScope.h | 4 ++++
interface/src/avatar/AvatarManager.h | 3 +++
interface/src/devices/DdeFaceTracker.h | 3 +++
interface/src/raypick/PickScriptingInterface.h | 4 ++++
interface/src/raypick/PointerScriptingInterface.h | 3 +++
.../src/scripting/AccountServicesScriptingInterface.h | 3 +++
interface/src/scripting/Audio.h | 8 +++++++-
interface/src/scripting/ClipboardScriptingInterface.h | 3 +++
.../src/scripting/ControllerScriptingInterface.h | 5 ++++-
.../src/scripting/GooglePolyScriptingInterface.h | 3 +++
interface/src/scripting/HMDScriptingInterface.h | 6 +++++-
interface/src/scripting/MenuScriptingInterface.h | 3 +++
interface/src/scripting/SelectionScriptingInterface.h | 3 +++
interface/src/scripting/SettingsScriptingInterface.h | 3 +++
interface/src/scripting/WindowScriptingInterface.h | 6 +++++-
interface/src/ui/AvatarInputs.h | 4 ++++
interface/src/ui/overlays/ContextOverlayInterface.h | 3 ---
interface/src/ui/overlays/Overlays.h | 4 ++++
libraries/animation/src/AnimationCache.h | 4 ++++
libraries/audio-client/src/AudioIOStats.h | 8 ++++++++
libraries/audio/src/SoundCache.h | 5 +++++
.../src/controllers/impl/MappingBuilderProxy.h | 5 ++++-
.../src/controllers/impl/RouteBuilderProxy.h | 3 +++
.../src/display-plugins/CompositorHelper.h | 4 ++++
libraries/entities/src/EntityScriptingInterface.h | 6 ++++++
.../graphics-scripting/GraphicsScriptingInterface.h | 3 +++
.../src/model-networking/ModelCache.h | 3 +++
.../src/model-networking/TextureCache.h | 3 +++
libraries/networking/src/AddressManager.h | 5 +++++
libraries/networking/src/MessagesClient.h | 5 +++++
libraries/networking/src/ResourceCache.h | 6 ++++++
libraries/script-engine/src/AssetScriptingInterface.h | 5 +++++
libraries/script-engine/src/Quat.h | 6 ++++++
.../script-engine/src/RecordingScriptingInterface.h | 4 ++++
libraries/script-engine/src/SceneScriptingInterface.h | 8 ++++++++
libraries/script-engine/src/ScriptEngine.h | 6 ++++++
libraries/script-engine/src/ScriptEngines.h | 4 ++++
libraries/script-engine/src/ScriptUUID.h | 6 ++++++
libraries/script-engine/src/ScriptsModel.h | 3 +++
libraries/script-engine/src/ScriptsModelFilter.h | 3 +++
libraries/script-engine/src/UsersScriptingInterface.h | 7 ++++++-
libraries/script-engine/src/Vec3.h | 6 ++++++
libraries/shared/src/DebugDraw.h | 5 +++++
libraries/shared/src/PathUtils.h | 4 ++++
libraries/shared/src/RegisteredMetaTypes.h | 6 ++++++
libraries/shared/src/shared/Camera.h | 4 ++++
libraries/ui/src/ui/TabletScriptingInterface.h | 11 +++++++++++
50 files changed, 221 insertions(+), 10 deletions(-)
diff --git a/interface/src/AvatarBookmarks.h b/interface/src/AvatarBookmarks.h
index 177e6e493e..7b47ea8af7 100644
--- a/interface/src/AvatarBookmarks.h
+++ b/interface/src/AvatarBookmarks.h
@@ -18,6 +18,10 @@
/**jsdoc
* This API helps manage adding and deleting avatar bookmarks.
* @namespace AvatarBookmarks
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
*/
class AvatarBookmarks: public Bookmarks, public Dependency {
diff --git a/interface/src/LODManager.h b/interface/src/LODManager.h
index e8737d92ae..889fff3153 100644
--- a/interface/src/LODManager.h
+++ b/interface/src/LODManager.h
@@ -10,8 +10,11 @@
//
/**jsdoc
- * The LOD class manages your Level of Detail functions within interface
+ * The LODManager API manages your Level of Detail functions within interface.
* @namespace LODManager
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
#ifndef hifi_LODManager_h
diff --git a/interface/src/SpeechRecognizer.h b/interface/src/SpeechRecognizer.h
index d5f9031cfc..b22ab73837 100644
--- a/interface/src/SpeechRecognizer.h
+++ b/interface/src/SpeechRecognizer.h
@@ -24,6 +24,9 @@
/**jsdoc
* @namespace SpeechRecognizer
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class SpeechRecognizer : public QObject, public Dependency {
Q_OBJECT
diff --git a/interface/src/audio/AudioScope.h b/interface/src/audio/AudioScope.h
index ff8bfda6dd..41cee8d17d 100644
--- a/interface/src/audio/AudioScope.h
+++ b/interface/src/audio/AudioScope.h
@@ -28,6 +28,10 @@ class AudioScope : public QObject, public Dependency {
/**jsdoc
* The AudioScope API helps control the Audio Scope features in Interface
* @namespace AudioScope
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {number} scopeInput Read-only.
* @property {number} scopeOutputLeft Read-only.
* @property {number} scopeOutputRight Read-only.
diff --git a/interface/src/avatar/AvatarManager.h b/interface/src/avatar/AvatarManager.h
index d2655914d2..7f5aa00466 100644
--- a/interface/src/avatar/AvatarManager.h
+++ b/interface/src/avatar/AvatarManager.h
@@ -30,6 +30,9 @@
/**jsdoc
* The AvatarManager API has properties and methods which manage Avatars within the same domain.
* @namespace AvatarManager
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class AvatarManager : public AvatarHashMap {
diff --git a/interface/src/devices/DdeFaceTracker.h b/interface/src/devices/DdeFaceTracker.h
index d4af0bbd37..4fe36b582e 100644
--- a/interface/src/devices/DdeFaceTracker.h
+++ b/interface/src/devices/DdeFaceTracker.h
@@ -29,6 +29,9 @@
/**jsdoc
* The FaceTracker API helps manage facial tracking hardware.
* @namespace FaceTracker
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class DdeFaceTracker : public FaceTracker, public Dependency {
diff --git a/interface/src/raypick/PickScriptingInterface.h b/interface/src/raypick/PickScriptingInterface.h
index f2cd9287a5..2568dd8457 100644
--- a/interface/src/raypick/PickScriptingInterface.h
+++ b/interface/src/raypick/PickScriptingInterface.h
@@ -18,6 +18,10 @@
* The Picks API lets you create and manage objects for repeatedly calculating intersections in different ways.
*
* @namespace Picks
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property PICK_NOTHING {number} A filter flag. Don't intersect with anything.
* @property PICK_ENTITIES {number} A filter flag. Include entities when intersecting.
* @property PICK_OVERLAYS {number} A filter flag. Include overlays when intersecting.
diff --git a/interface/src/raypick/PointerScriptingInterface.h b/interface/src/raypick/PointerScriptingInterface.h
index 1cc7b56503..e7acfd4037 100644
--- a/interface/src/raypick/PointerScriptingInterface.h
+++ b/interface/src/raypick/PointerScriptingInterface.h
@@ -19,6 +19,9 @@
* Pointers can also be configured to automatically generate PointerEvents.
*
* @namespace Pointers
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class PointerScriptingInterface : public QObject, public Dependency {
diff --git a/interface/src/scripting/AccountServicesScriptingInterface.h b/interface/src/scripting/AccountServicesScriptingInterface.h
index d38a84d8fa..5774ee1da5 100644
--- a/interface/src/scripting/AccountServicesScriptingInterface.h
+++ b/interface/src/scripting/AccountServicesScriptingInterface.h
@@ -38,6 +38,9 @@ class AccountServicesScriptingInterface : public QObject {
/**jsdoc
* The AccountServices API contains helper functions related to user connectivity
*
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @namespace AccountServices
* @property {string} username Read-only.
* @property {boolean} loggedIn Read-only.
diff --git a/interface/src/scripting/Audio.h b/interface/src/scripting/Audio.h
index c77d1522b5..f0a4328c2f 100644
--- a/interface/src/scripting/Audio.h
+++ b/interface/src/scripting/Audio.h
@@ -27,8 +27,14 @@ class Audio : public AudioScriptingInterface, protected ReadWriteLockable {
/**jsdoc
* The Audio API features tools to help control audio contexts and settings.
- *
+ *
* @namespace Audio
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property {boolean} muted
* @property {boolean} noiseReduction
* @property {number} inputVolume
diff --git a/interface/src/scripting/ClipboardScriptingInterface.h b/interface/src/scripting/ClipboardScriptingInterface.h
index cce300e831..32b8c64a7d 100644
--- a/interface/src/scripting/ClipboardScriptingInterface.h
+++ b/interface/src/scripting/ClipboardScriptingInterface.h
@@ -21,6 +21,9 @@
* The Clipboard API enables you to export and import entities to and from JSON files.
*
* @namespace Clipboard
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class ClipboardScriptingInterface : public QObject {
Q_OBJECT
diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h
index f19caa8478..42bb648abf 100644
--- a/interface/src/scripting/ControllerScriptingInterface.h
+++ b/interface/src/scripting/ControllerScriptingInterface.h
@@ -145,7 +145,10 @@ class ScriptEngine;
*
* @namespace Controller
*
- * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
+ * @hifi-interface
+ * @hifi-client-entity
+ *
+ * @property {Controller.Actions} Actions - Predefined actions on Interface and the user's avatar. These can be used as end
* points in a {@link RouteObject} mapping. A synonym for Controller.Hardware.Actions
.
* Read-only.
* Default mappings are provided from the Controller.Hardware.Keyboard
and Controller.Standard
to
diff --git a/interface/src/scripting/GooglePolyScriptingInterface.h b/interface/src/scripting/GooglePolyScriptingInterface.h
index 5c37b394fa..fb5aed9759 100644
--- a/interface/src/scripting/GooglePolyScriptingInterface.h
+++ b/interface/src/scripting/GooglePolyScriptingInterface.h
@@ -18,6 +18,9 @@
/**jsdoc
* The GooglePoly API allows you to interact with Google Poly models direct from inside High Fidelity.
* @namespace GooglePoly
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class GooglePolyScriptingInterface : public QObject, public Dependency {
diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h
index 9b2482e73a..d2a272851f 100644
--- a/interface/src/scripting/HMDScriptingInterface.h
+++ b/interface/src/scripting/HMDScriptingInterface.h
@@ -28,7 +28,11 @@ class QScriptEngine;
* The HMD API provides access to the HMD used in VR display mode.
*
* @namespace HMD
- * @property {Vec3} position - The position of the HMD if currently in VR display mode, otherwise
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
+ * @property {Vec3} position - The position of the HMD if currently in VR display mode, otherwise
* {@link Vec3(0)|Vec3.ZERO}. Read-only.
* @property {Quat} orientation - The orientation of the HMD if currently in VR display mode, otherwise
* {@link Quat(0)|Quat.IDENTITY}. Read-only.
diff --git a/interface/src/scripting/MenuScriptingInterface.h b/interface/src/scripting/MenuScriptingInterface.h
index 649c444eaf..81cf775de8 100644
--- a/interface/src/scripting/MenuScriptingInterface.h
+++ b/interface/src/scripting/MenuScriptingInterface.h
@@ -32,6 +32,9 @@ class MenuItemProperties;
* If a menu item doesn't belong to a group it is always displayed.
*
* @namespace Menu
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
/**
diff --git a/interface/src/scripting/SelectionScriptingInterface.h b/interface/src/scripting/SelectionScriptingInterface.h
index 71ff41248a..df92250c28 100644
--- a/interface/src/scripting/SelectionScriptingInterface.h
+++ b/interface/src/scripting/SelectionScriptingInterface.h
@@ -86,6 +86,9 @@ protected:
* The Selection
API provides a means of grouping together avatars, entities, and overlays in named lists.
* @namespace Selection
*
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @example Outline an entity when it is grabbed by a controller.
* // Create a box and copy the following text into the entity's "Script URL" field.
* (function () {
diff --git a/interface/src/scripting/SettingsScriptingInterface.h b/interface/src/scripting/SettingsScriptingInterface.h
index 9e0271601b..32d868bb24 100644
--- a/interface/src/scripting/SettingsScriptingInterface.h
+++ b/interface/src/scripting/SettingsScriptingInterface.h
@@ -18,6 +18,9 @@
/**jsdoc
* The Settings API provides a facility to store and retrieve values that persist between Interface runs.
* @namespace Settings
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class SettingsScriptingInterface : public QObject {
diff --git a/interface/src/scripting/WindowScriptingInterface.h b/interface/src/scripting/WindowScriptingInterface.h
index 0b766d2097..348882e0f8 100644
--- a/interface/src/scripting/WindowScriptingInterface.h
+++ b/interface/src/scripting/WindowScriptingInterface.h
@@ -28,7 +28,11 @@
* physics.
*
* @namespace Window
- * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
+ * @property {number} innerWidth - The width of the drawable area of the Interface window (i.e., without borders or other
* chrome), in pixels. Read-only.
* @property {number} innerHeight - The height of the drawable area of the Interface window (i.e., without borders or other
* chrome), in pixels. Read-only.
diff --git a/interface/src/ui/AvatarInputs.h b/interface/src/ui/AvatarInputs.h
index a9d1509770..e67d35e59f 100644
--- a/interface/src/ui/AvatarInputs.h
+++ b/interface/src/ui/AvatarInputs.h
@@ -26,6 +26,10 @@ class AvatarInputs : public QObject {
/**jsdoc
* API to help manage your Avatar's input
* @namespace AvatarInputs
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {boolean} cameraEnabled Read-only.
* @property {boolean} cameraMuted Read-only.
* @property {boolean} isHMD Read-only.
diff --git a/interface/src/ui/overlays/ContextOverlayInterface.h b/interface/src/ui/overlays/ContextOverlayInterface.h
index b80a3a70fb..808c3a4ee3 100644
--- a/interface/src/ui/overlays/ContextOverlayInterface.h
+++ b/interface/src/ui/overlays/ContextOverlayInterface.h
@@ -31,9 +31,6 @@
#include "EntityTree.h"
#include "ContextOverlayLogging.h"
-/**jsdoc
-* @namespace ContextOverlay
-*/
class ContextOverlayInterface : public QObject, public Dependency {
Q_OBJECT
diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h
index c2f6e3e693..cf1151b46a 100644
--- a/interface/src/ui/overlays/Overlays.h
+++ b/interface/src/ui/overlays/Overlays.h
@@ -76,6 +76,10 @@ void RayToOverlayIntersectionResultFromScriptValue(const QScriptValue& object, R
* The Overlays API provides facilities to create and interact with overlays. Overlays are 2D and 3D objects visible only to
* yourself and that aren't persisted to the domain. They are used for UI.
* @namespace Overlays
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {Uuid} keyboardFocusOverlay - Get or set the {@link Overlays.OverlayType|web3d} overlay that has keyboard focus.
* If no overlay has keyboard focus, get returns null
; set to null
or {@link Uuid|Uuid.NULL} to
* clear keyboard focus.
diff --git a/libraries/animation/src/AnimationCache.h b/libraries/animation/src/AnimationCache.h
index 03b37aef2f..4db009f592 100644
--- a/libraries/animation/src/AnimationCache.h
+++ b/libraries/animation/src/AnimationCache.h
@@ -37,6 +37,10 @@ public:
* API to manage animation cache resources.
* @namespace AnimationCache
*
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
+ *
* @property {number} numTotal - Total number of total resources. Read-only.
* @property {number} numCached - Total number of cached resource. Read-only.
* @property {number} sizeTotal - Size in bytes of all resources. Read-only.
diff --git a/libraries/audio-client/src/AudioIOStats.h b/libraries/audio-client/src/AudioIOStats.h
index 89db4942ec..45fcf365da 100644
--- a/libraries/audio-client/src/AudioIOStats.h
+++ b/libraries/audio-client/src/AudioIOStats.h
@@ -41,6 +41,10 @@ class AudioStreamStatsInterface : public QObject {
/**jsdoc
* @class AudioStats.AudioStreamStats
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {number} lossRate Read-only.
* @property {number} lossCount Read-only.
* @property {number} lossRateWindow Read-only.
@@ -185,6 +189,10 @@ class AudioStatsInterface : public QObject {
/**jsdoc
* Audio stats from the client.
* @namespace AudioStats
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {number} pingMs Read-only.
* @property {number} inputReadMsMax Read-only.
* @property {number} inputUnplayedMsMax Read-only.
diff --git a/libraries/audio/src/SoundCache.h b/libraries/audio/src/SoundCache.h
index d8c52635e0..0874cef90e 100644
--- a/libraries/audio/src/SoundCache.h
+++ b/libraries/audio/src/SoundCache.h
@@ -29,6 +29,11 @@ public:
* API to manage sound cache resources.
* @namespace SoundCache
*
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property {number} numTotal - Total number of total resources. Read-only.
* @property {number} numCached - Total number of cached resource. Read-only.
* @property {number} sizeTotal - Size in bytes of all resources. Read-only.
diff --git a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
index 86a43c0c13..4521c89afd 100644
--- a/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/MappingBuilderProxy.h
@@ -49,9 +49,12 @@ class UserInputMapper;
* output that already has a route the new route is ignored.
* New mappings override previous mappings: each output is processed using the route in the most recently enabled
* mapping that contains that output.
- *
+ *
*
* @class MappingObject
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
/**jsdoc
diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
index 0336638068..3204e0502f 100644
--- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
+++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h
@@ -35,6 +35,9 @@ class ScriptingInterface;
* types.
*
* @class RouteObject
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
// TODO migrate functionality to a RouteBuilder class and make the proxy defer to that
diff --git a/libraries/display-plugins/src/display-plugins/CompositorHelper.h b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
index bc6ed63363..fb712c26fa 100644
--- a/libraries/display-plugins/src/display-plugins/CompositorHelper.h
+++ b/libraries/display-plugins/src/display-plugins/CompositorHelper.h
@@ -174,6 +174,10 @@ private:
/**jsdoc
* @namespace Reticle
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {boolean} allowMouseCapture
* @property {number} depth
* @property {Vec2} maximumPosition
diff --git a/libraries/entities/src/EntityScriptingInterface.h b/libraries/entities/src/EntityScriptingInterface.h
index d4a8b11453..2491d4bbbd 100644
--- a/libraries/entities/src/EntityScriptingInterface.h
+++ b/libraries/entities/src/EntityScriptingInterface.h
@@ -94,6 +94,12 @@ void RayToEntityIntersectionResultFromScriptValue(const QScriptValue& object, Ra
* Interface has displayed and so knows about.
*
* @namespace Entities
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property {Uuid} keyboardFocusEntity - Get or set the {@link Entities.EntityType|Web} entity that has keyboard focus.
* If no entity has keyboard focus, get returns null
; set to null
or {@link Uuid|Uuid.NULL} to
* clear keyboard focus.
diff --git a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
index 526352804b..8b76491229 100644
--- a/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
+++ b/libraries/graphics-scripting/src/graphics-scripting/GraphicsScriptingInterface.h
@@ -24,6 +24,9 @@
/**jsdoc
* The experimental Graphics API (experimental) lets you query and manage certain graphics-related structures (like underlying meshes and textures) from scripting.
* @namespace Graphics
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class GraphicsScriptingInterface : public QObject, public QScriptable, public Dependency {
diff --git a/libraries/model-networking/src/model-networking/ModelCache.h b/libraries/model-networking/src/model-networking/ModelCache.h
index 9532f39ce0..cda825e5fb 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.h
+++ b/libraries/model-networking/src/model-networking/ModelCache.h
@@ -144,6 +144,9 @@ public:
* API to manage model cache resources.
* @namespace ModelCache
*
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {number} numTotal - Total number of total resources. Read-only.
* @property {number} numCached - Total number of cached resource. Read-only.
* @property {number} sizeTotal - Size in bytes of all resources. Read-only.
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 3f46dc3074..ef911103c3 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -151,6 +151,9 @@ public:
* API to manage texture cache resources.
* @namespace TextureCache
*
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {number} numTotal - Total number of total resources. Read-only.
* @property {number} numCached - Total number of cached resource. Read-only.
* @property {number} sizeTotal - Size in bytes of all resources. Read-only.
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 94eff46bda..94fb25812f 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -33,6 +33,11 @@ const QString GET_PLACE = "/api/v1/places/%1";
* The location API provides facilities related to your current location in the metaverse.
*
* @namespace location
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
+ *
* @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
* connected to the domain or are in a serverless domain.
* Read-only.
diff --git a/libraries/networking/src/MessagesClient.h b/libraries/networking/src/MessagesClient.h
index 6ef3777d8c..f2ccfe33f4 100644
--- a/libraries/networking/src/MessagesClient.h
+++ b/libraries/networking/src/MessagesClient.h
@@ -37,6 +37,11 @@
*
*
* @namespace Messages
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
*/
class MessagesClient : public QObject, public Dependency {
Q_OBJECT
diff --git a/libraries/networking/src/ResourceCache.h b/libraries/networking/src/ResourceCache.h
index 609483bc56..799d2c7f59 100644
--- a/libraries/networking/src/ResourceCache.h
+++ b/libraries/networking/src/ResourceCache.h
@@ -89,6 +89,12 @@ class ScriptableResource : public QObject {
/**jsdoc
* @constructor Resource
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property {string} url - URL of this resource.
* @property {Resource.State} state - Current loading state.
*/
diff --git a/libraries/script-engine/src/AssetScriptingInterface.h b/libraries/script-engine/src/AssetScriptingInterface.h
index 5cb1136b74..eb9a628ae3 100644
--- a/libraries/script-engine/src/AssetScriptingInterface.h
+++ b/libraries/script-engine/src/AssetScriptingInterface.h
@@ -27,6 +27,11 @@
/**jsdoc
* The Assets API allows you to communicate with the Asset Browser.
* @namespace Assets
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
*/
class AssetScriptingInterface : public BaseAssetScriptingInterface, QScriptable {
Q_OBJECT
diff --git a/libraries/script-engine/src/Quat.h b/libraries/script-engine/src/Quat.h
index e6e395d9bf..254757dece 100644
--- a/libraries/script-engine/src/Quat.h
+++ b/libraries/script-engine/src/Quat.h
@@ -35,6 +35,12 @@
* of gimbal lock.
* @namespace Quat
* @variation 0
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property IDENTITY {Quat} { x: 0, y: 0, z: 0, w: 1 }
: The identity rotation, i.e., no rotation.
* Read-only.
* @example
Print the IDENTITY
value.
diff --git a/libraries/script-engine/src/RecordingScriptingInterface.h b/libraries/script-engine/src/RecordingScriptingInterface.h
index 0e4f90b928..29d9b31049 100644
--- a/libraries/script-engine/src/RecordingScriptingInterface.h
+++ b/libraries/script-engine/src/RecordingScriptingInterface.h
@@ -25,6 +25,10 @@ class QScriptValue;
/**jsdoc
* @namespace Recording
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
*/
class RecordingScriptingInterface : public QObject, public Dependency {
Q_OBJECT
diff --git a/libraries/script-engine/src/SceneScriptingInterface.h b/libraries/script-engine/src/SceneScriptingInterface.h
index c69cd7090d..fdfbc6f6c0 100644
--- a/libraries/script-engine/src/SceneScriptingInterface.h
+++ b/libraries/script-engine/src/SceneScriptingInterface.h
@@ -112,6 +112,10 @@ namespace SceneScripting {
/**jsdoc
* @class Scene.Stage
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {string} backgroundMode
* @property {Scene.Stage.KeyLight} keyLight
* @property {Scene.Stage.Location} location
@@ -171,6 +175,10 @@ namespace SceneScripting {
/**jsdoc
* @namespace Scene
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {boolean} shouldRenderAvatars
* @property {boolean} shouldRenderEntities
* @property {Scene.Stage} stage
diff --git a/libraries/script-engine/src/ScriptEngine.h b/libraries/script-engine/src/ScriptEngine.h
index 63a4ba4f90..af4d04a706 100644
--- a/libraries/script-engine/src/ScriptEngine.h
+++ b/libraries/script-engine/src/ScriptEngine.h
@@ -89,6 +89,12 @@ public:
/**jsdoc
* @namespace Script
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property {string} context
*/
class ScriptEngine : public BaseScriptEngine, public EntitiesScriptEngineProvider {
diff --git a/libraries/script-engine/src/ScriptEngines.h b/libraries/script-engine/src/ScriptEngines.h
index 1200168420..da6fe521c9 100644
--- a/libraries/script-engine/src/ScriptEngines.h
+++ b/libraries/script-engine/src/ScriptEngines.h
@@ -29,6 +29,10 @@ class ScriptEngine;
/**jsdoc
* @namespace ScriptDiscoveryService
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {string} debugScriptUrl
* @property {string} defaultScriptsPath
* @property {ScriptsModel} scriptsModel
diff --git a/libraries/script-engine/src/ScriptUUID.h b/libraries/script-engine/src/ScriptUUID.h
index 303a871d1d..9b61f451c5 100644
--- a/libraries/script-engine/src/ScriptUUID.h
+++ b/libraries/script-engine/src/ScriptUUID.h
@@ -23,6 +23,12 @@
* hexadecimal digits.
*
* @namespace Uuid
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property NULL {Uuid} The null UUID, {00000000-0000-0000-0000-000000000000}
.
*/
diff --git a/libraries/script-engine/src/ScriptsModel.h b/libraries/script-engine/src/ScriptsModel.h
index a4ffc192f9..2466347baa 100644
--- a/libraries/script-engine/src/ScriptsModel.h
+++ b/libraries/script-engine/src/ScriptsModel.h
@@ -68,6 +68,9 @@ public:
* Has properties and functions below in addition to those of
* http://doc.qt.io/qt-5/qabstractitemmodel.html.
* @class ScriptsModel
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class ScriptsModel : public QAbstractItemModel {
Q_OBJECT
diff --git a/libraries/script-engine/src/ScriptsModelFilter.h b/libraries/script-engine/src/ScriptsModelFilter.h
index 26efde02e8..05a76334bb 100644
--- a/libraries/script-engine/src/ScriptsModelFilter.h
+++ b/libraries/script-engine/src/ScriptsModelFilter.h
@@ -20,6 +20,9 @@
* Has properties and functions per
* http://doc.qt.io/qt-5/qsortfilterproxymodel.html.
* @class ScriptsModelFilter
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class ScriptsModelFilter : public QSortFilterProxyModel {
Q_OBJECT
diff --git a/libraries/script-engine/src/UsersScriptingInterface.h b/libraries/script-engine/src/UsersScriptingInterface.h
index 6728c471f6..f214c3f11c 100644
--- a/libraries/script-engine/src/UsersScriptingInterface.h
+++ b/libraries/script-engine/src/UsersScriptingInterface.h
@@ -18,7 +18,12 @@
/**jsdoc
* @namespace Users
- * @property {boolean} canKick - true
if the domain server allows the node or avatar to kick (ban) avatars,
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-assignment-client
+ *
+ * @property {boolean} canKick - true
if the domain server allows the node or avatar to kick (ban) avatars,
* otherwise false
. Read-only.
* @property {boolean} requestsDomainListData - true
if the avatar requests extra data from the mixers (such as
* positional data of an avatar you've ignored). Read-only.
diff --git a/libraries/script-engine/src/Vec3.h b/libraries/script-engine/src/Vec3.h
index 635f2a530c..eb9438c5c2 100644
--- a/libraries/script-engine/src/Vec3.h
+++ b/libraries/script-engine/src/Vec3.h
@@ -47,6 +47,12 @@
*
* @namespace Vec3
* @variation 0
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @property {Vec3} UNIT_X - { x: 1, y: 0, z: 0 }
: Unit vector in the x-axis direction. Read-only.
* @property {Vec3} UNIT_Y - { x: 0, y: 1, z: 0 }
: Unit vector in the y-axis direction. Read-only.
* @property {Vec3} UNIT_Z - { x: 0, y: 0, z: 1 }
: Unit vector in the z-axis direction. Read-only.
diff --git a/libraries/shared/src/DebugDraw.h b/libraries/shared/src/DebugDraw.h
index 64327585fb..7dd19415c9 100644
--- a/libraries/shared/src/DebugDraw.h
+++ b/libraries/shared/src/DebugDraw.h
@@ -25,6 +25,11 @@
* Helper functions to render ephemeral debug markers and lines.
* DebugDraw markers and lines are only visible locally, they are not visible by other users.
* @namespace DebugDraw
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
*/
class DebugDraw : public QObject {
Q_OBJECT
diff --git a/libraries/shared/src/PathUtils.h b/libraries/shared/src/PathUtils.h
index d879ac968d..fc933b6b8c 100644
--- a/libraries/shared/src/PathUtils.h
+++ b/libraries/shared/src/PathUtils.h
@@ -22,6 +22,10 @@
* The Paths API provides absolute paths to the scripts and resources directories.
*
* @namespace Paths
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @deprecated The Paths API is deprecated. Use {@link Script.resolvePath} and {@link Script.resourcesPath} instead.
* @readonly
* @property {string} defaultScripts - The path to the scripts directory. Read-only.
diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h
index 689d1a3f42..467d6374a5 100644
--- a/libraries/shared/src/RegisteredMetaTypes.h
+++ b/libraries/shared/src/RegisteredMetaTypes.h
@@ -361,6 +361,12 @@ using MeshPointer = std::shared_ptr;
/**jsdoc
* A handle for a mesh in an entity, such as returned by {@link Entities.getMeshes}.
* @class MeshProxy
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ * @hifi-server-entity
+ * @hifi-assignment-client
+ *
* @deprecated Use the {@link Graphics} API instead.
*/
class MeshProxy : public QObject {
diff --git a/libraries/shared/src/shared/Camera.h b/libraries/shared/src/shared/Camera.h
index ea2e9cddab..32e753d0f9 100644
--- a/libraries/shared/src/shared/Camera.h
+++ b/libraries/shared/src/shared/Camera.h
@@ -40,6 +40,10 @@ class Camera : public QObject {
* The Camera API provides access to the "camera" that defines your view in desktop and HMD display modes.
*
* @namespace Camera
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property position {Vec3} The position of the camera. You can set this value only when the camera is in independent mode.
* @property orientation {Quat} The orientation of the camera. You can set this value only when the camera is in independent
* mode.
diff --git a/libraries/ui/src/ui/TabletScriptingInterface.h b/libraries/ui/src/ui/TabletScriptingInterface.h
index bab15fc7b6..e74b846f02 100644
--- a/libraries/ui/src/ui/TabletScriptingInterface.h
+++ b/libraries/ui/src/ui/TabletScriptingInterface.h
@@ -40,6 +40,9 @@ class OffscreenQmlSurface;
/**jsdoc
* @namespace Tablet
+ *
+ * @hifi-interface
+ * @hifi-client-entity
*/
class TabletScriptingInterface : public QObject, public Dependency {
Q_OBJECT
@@ -176,6 +179,10 @@ Q_DECLARE_METATYPE(TabletButtonsProxyModel*);
/**jsdoc
* @class TabletProxy
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {string} name - Name of this tablet. Read-only.
* @property {boolean} toolbarMode - Used to transition this tablet into and out of toolbar mode.
* When tablet is in toolbar mode, all its buttons will appear in a floating toolbar.
@@ -410,6 +417,10 @@ Q_DECLARE_METATYPE(TabletProxy*);
/**jsdoc
* @class TabletButtonProxy
+ *
+ * @hifi-interface
+ * @hifi-client-entity
+ *
* @property {Uuid} uuid - Uniquely identifies this button. Read-only.
* @property {TabletButtonProxy.ButtonProperties} properties
*/
From b994776ebd98314025ecdc86fdffc91ebb42eba1 Mon Sep 17 00:00:00 2001
From: Liv Erickson
Date: Mon, 30 Apr 2018 12:01:37 -0700
Subject: [PATCH 027/192] do not open help on first run
---
interface/src/Application.cpp | 3 ---
1 file changed, 3 deletions(-)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index cd4562da54..6c11286d03 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -3166,9 +3166,6 @@ void Application::handleSandboxStatus(QNetworkReply* reply) {
// If this is a first run we short-circuit the address passed in
if (firstRun.get()) {
-#if !defined(Q_OS_ANDROID)
- showHelp();
-#endif
DependencyManager::get()->goToEntry();
sentTo = SENT_TO_ENTRY;
firstRun.set(false);
From 5ab16302cac5a63f6ac3aa084850a145b43f4785 Mon Sep 17 00:00:00 2001
From: Atlante45
Date: Tue, 24 Apr 2018 15:09:30 -0700
Subject: [PATCH 028/192] Keep CrashpadClient around
---
interface/src/Crashpad.cpp | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/interface/src/Crashpad.cpp b/interface/src/Crashpad.cpp
index e39cd42d81..27b6f4b937 100644
--- a/interface/src/Crashpad.cpp
+++ b/interface/src/Crashpad.cpp
@@ -11,6 +11,8 @@
#include "Crashpad.h"
+#include
+
#include
#if HAS_CRASHPAD
@@ -20,7 +22,7 @@
#include
#include
-#include
+#include
#include
#include
@@ -28,28 +30,26 @@
#include
#include
+#include
+
using namespace crashpad;
static const std::string BACKTRACE_URL { CMAKE_BACKTRACE_URL };
static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
-static std::wstring gIPCPipe;
-
extern QString qAppFileName();
std::mutex annotationMutex;
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
-#include
+
+std::unique_ptr client;
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) {
- CrashpadClient client;
- if (gIPCPipe.length()) {
- client.SetHandlerIPCPipe(gIPCPipe);
- }
- client.DumpAndCrash(pExceptionInfo);
+ assert(client);
+ client->DumpAndCrash(pExceptionInfo);
}
return EXCEPTION_CONTINUE_SEARCH;
@@ -60,7 +60,7 @@ bool startCrashHandler() {
return false;
}
- CrashpadClient client;
+ client.reset(new CrashpadClient());
std::vector arguments;
std::map annotations;
@@ -96,12 +96,9 @@ bool startCrashHandler() {
// Enable automated uploads.
database->GetSettings()->SetUploadsEnabled(true);
- bool result = client.StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
- gIPCPipe = client.GetHandlerIPCPipe();
-
AddVectoredExceptionHandler(0, vectoredExceptionHandler);
- return result;
+ return client->StartHandler(handler, db, db, BACKTRACE_URL, annotations, arguments, true, true);
}
void setCrashAnnotation(std::string name, std::string value) {
From 250806252e526889820549e6ac13ce259a340ebd Mon Sep 17 00:00:00 2001
From: Atlante45
Date: Tue, 24 Apr 2018 15:09:30 -0700
Subject: [PATCH 029/192] Don't use unique_ptr to store CrashpadClient
---
interface/src/Crashpad.cpp | 12 +++++++-----
1 file changed, 7 insertions(+), 5 deletions(-)
diff --git a/interface/src/Crashpad.cpp b/interface/src/Crashpad.cpp
index 27b6f4b937..45f1d0778f 100644
--- a/interface/src/Crashpad.cpp
+++ b/interface/src/Crashpad.cpp
@@ -39,16 +39,17 @@ static const std::string BACKTRACE_TOKEN { CMAKE_BACKTRACE_TOKEN };
extern QString qAppFileName();
+CrashpadClient* client { nullptr };
std::mutex annotationMutex;
crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr };
-
-std::unique_ptr client;
-
LONG WINAPI vectoredExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) {
+ if (!client) {
+ return EXCEPTION_CONTINUE_SEARCH;
+ }
+
if (pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_HEAP_CORRUPTION ||
pExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_STACK_BUFFER_OVERRUN) {
- assert(client);
client->DumpAndCrash(pExceptionInfo);
}
@@ -60,7 +61,8 @@ bool startCrashHandler() {
return false;
}
- client.reset(new CrashpadClient());
+ assert(!client);
+ client = new CrashpadClient();
std::vector arguments;
std::map annotations;
From cfc5892d63f342949bf886dc45e99e07809aa0db Mon Sep 17 00:00:00 2001
From: Simon Walton
Date: Mon, 30 Apr 2018 13:41:56 -0700
Subject: [PATCH 030/192] Only clear check-in count on Domain Query related
responses
---
libraries/networking/src/DomainHandler.cpp | 3 ---
libraries/networking/src/NodeList.cpp | 2 --
2 files changed, 5 deletions(-)
diff --git a/libraries/networking/src/DomainHandler.cpp b/libraries/networking/src/DomainHandler.cpp
index 84af21004d..cd8064c4c0 100644
--- a/libraries/networking/src/DomainHandler.cpp
+++ b/libraries/networking/src/DomainHandler.cpp
@@ -239,7 +239,6 @@ void DomainHandler::activateICELocalSocket() {
_domainURL.setHost(_sockAddr.getAddress().toString());
emit domainURLChanged(_domainURL);
emit completedSocketDiscovery();
- clearPendingCheckins(); // Clear outstanding count.
}
void DomainHandler::activateICEPublicSocket() {
@@ -249,7 +248,6 @@ void DomainHandler::activateICEPublicSocket() {
_domainURL.setHost(_sockAddr.getAddress().toString());
emit domainURLChanged(_domainURL);
emit completedSocketDiscovery();
- clearPendingCheckins(); // Clear outstanding count.
}
QString DomainHandler::getViewPointFromNamedPath(QString namedPath) {
@@ -341,7 +339,6 @@ void DomainHandler::processSettingsPacketList(QSharedPointer pa
qCDebug(networking) << "Received domain settings: \n" << _settingsObject;
}
- clearPendingCheckins(); // Reset outstanding check-ins.
emit settingsReceived(_settingsObject);
}
diff --git a/libraries/networking/src/NodeList.cpp b/libraries/networking/src/NodeList.cpp
index 9d3e5f0e91..4920ea97c7 100644
--- a/libraries/networking/src/NodeList.cpp
+++ b/libraries/networking/src/NodeList.cpp
@@ -534,8 +534,6 @@ void NodeList::processDomainServerPathResponse(QSharedPointer m
qCDebug(networking) << "Could not go to viewpoint" << viewpoint
<< "which was the lookup result for path" << pathQuery;
}
-
- _domainHandler.clearPendingCheckins();
}
void NodeList::handleICEConnectionToDomainServer() {
From 73cea112eefa7541a05394426f74e0652cb07efb Mon Sep 17 00:00:00 2001
From: Ryan Huffman
Date: Thu, 26 Apr 2018 15:49:34 -0700
Subject: [PATCH 031/192] Update FilePersistThread to use std::chrono instead
of usecTimestampNow
---
libraries/shared/src/shared/FileLogger.cpp | 16 +++++++---------
1 file changed, 7 insertions(+), 9 deletions(-)
diff --git a/libraries/shared/src/shared/FileLogger.cpp b/libraries/shared/src/shared/FileLogger.cpp
index 4f4a9ab6dd..1e17ee9e7c 100644
--- a/libraries/shared/src/shared/FileLogger.cpp
+++ b/libraries/shared/src/shared/FileLogger.cpp
@@ -44,7 +44,7 @@ static const QString DATETIME_FORMAT = "yyyy-MM-dd_hh.mm.ss";
static const QString LOGS_DIRECTORY = "Logs";
static const QString DATETIME_WILDCARD = "20[0-9][0-9]-[01][0-9]-[0-3][0-9]_[0-2][0-9]\\.[0-6][0-9]\\.[0-6][0-9]";
static const QString SESSION_WILDCARD = "[0-9a-z]{8}(-[0-9a-z]{4}){3}-[0-9a-z]{12}";
-static QRegExp LOG_FILENAME_REGEX { "hifi-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\.txt" };
+static QRegExp LOG_FILENAME_REGEX { "hifi-log_" + DATETIME_WILDCARD + "(_" + SESSION_WILDCARD + ")?\\.txt" };
static QUuid SESSION_ID;
// Max log size is 512 KB. We send log files to our crash reporter, so we want to keep this relatively
@@ -52,8 +52,7 @@ static QUuid SESSION_ID;
static const qint64 MAX_LOG_SIZE = 512 * 1024;
// Max log files found in the log directory is 100.
static const qint64 MAX_LOG_DIR_SIZE = 512 * 1024 * 100;
-// Max log age is 1 hour
-static const uint64_t MAX_LOG_AGE_USECS = USECS_PER_SECOND * 3600;
+static const std::chrono::minutes MAX_LOG_AGE { 60 };
static FilePersistThread* _persistThreadInstance;
@@ -84,23 +83,22 @@ FilePersistThread::FilePersistThread(const FileLogger& logger) : _logger(logger)
if (file.exists()) {
rollFileIfNecessary(file, false);
}
- _lastRollTime = usecTimestampNow();
+ _lastRollTime = std::chrono::system_clock::now();
}
void FilePersistThread::rollFileIfNecessary(QFile& file, bool notifyListenersIfRolled) {
- uint64_t now = usecTimestampNow();
- if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE_USECS) {
+ auto now = std::chrono::system_clock::now();
+ if ((file.size() > MAX_LOG_SIZE) || (now - _lastRollTime) > MAX_LOG_AGE) {
QString newFileName = getLogRollerFilename();
if (file.copy(newFileName)) {
file.open(QIODevice::WriteOnly | QIODevice::Truncate);
file.close();
- qCDebug(shared) << "Rolled log file:" << newFileName;
if (notifyListenersIfRolled) {
emit rollingLogFile(newFileName);
}
- _lastRollTime = now;
+ _lastRollTime = std::chrono::system_clock::now();
}
QDir logDir(FileUtils::standardPath(LOGS_DIRECTORY));
@@ -128,7 +126,7 @@ bool FilePersistThread::processQueueItems(const Queue& messages) {
rollFileIfNecessary(file);
if (file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text)) {
QTextStream out(&file);
- foreach(const QString& message, messages) {
+ for (const QString& message : messages) {
out << message;
}
}
From f3fe1f3e58386363ddb2e2b5a48e5a08bd73f4c3 Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 30 Apr 2018 13:51:53 -0700
Subject: [PATCH 032/192] Force crash helpers not inlined
---
libraries/shared/src/CrashHelpers.cpp | 77 +++++++++++++++++++++++++++
libraries/shared/src/CrashHelpers.h | 66 +++--------------------
2 files changed, 83 insertions(+), 60 deletions(-)
create mode 100644 libraries/shared/src/CrashHelpers.cpp
diff --git a/libraries/shared/src/CrashHelpers.cpp b/libraries/shared/src/CrashHelpers.cpp
new file mode 100644
index 0000000000..f8ca90bc4c
--- /dev/null
+++ b/libraries/shared/src/CrashHelpers.cpp
@@ -0,0 +1,77 @@
+//
+// CrashHelpers.cpp
+// libraries/shared/src
+//
+// Created by Clement Brisset on 4/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 "CrashHelpers.h"
+
+namespace crash {
+
+class B;
+class A {
+public:
+ A(B* b) : _b(b) { }
+ ~A();
+ virtual void virtualFunction() = 0;
+
+private:
+ B* _b;
+};
+
+class B : public A {
+public:
+ B() : A(this) { }
+ virtual void virtualFunction() override { }
+};
+
+A::~A() {
+ _b->virtualFunction();
+}
+
+void pureVirtualCall() {
+ qCDebug(shared) << "About to make a pure virtual call";
+ B b;
+}
+
+void doubleFree() {
+ qCDebug(shared) << "About to double delete memory";
+ int* blah = new int(200);
+ delete blah;
+ delete blah;
+}
+
+void nullDeref() {
+ qCDebug(shared) << "About to dereference a null pointer";
+ int* p = nullptr;
+ *p = 1;
+}
+
+void doAbort() {
+ qCDebug(shared) << "About to abort";
+ abort();
+}
+
+void outOfBoundsVectorCrash() {
+ qCDebug(shared) << "std::vector out of bounds crash!";
+ std::vector v;
+ v[0] = 42;
+}
+
+void newFault() {
+ qCDebug(shared) << "About to crash inside new fault";
+
+ // Force crash with multiple large allocations
+ while (true) {
+ const size_t GIGABYTE = 1024 * 1024 * 1024;
+ new char[GIGABYTE];
+ }
+
+}
+
+}
diff --git a/libraries/shared/src/CrashHelpers.h b/libraries/shared/src/CrashHelpers.h
index 1cc6749182..ad988c8906 100644
--- a/libraries/shared/src/CrashHelpers.h
+++ b/libraries/shared/src/CrashHelpers.h
@@ -18,66 +18,12 @@
namespace crash {
-class B;
-class A {
-public:
- A(B* b) : _b(b) { }
- ~A();
- virtual void virtualFunction() = 0;
-
-private:
- B* _b;
-};
-
-class B : public A {
-public:
- B() : A(this) { }
- virtual void virtualFunction() override { }
-};
-
-A::~A() {
- _b->virtualFunction();
-}
-
-void pureVirtualCall() {
- qCDebug(shared) << "About to make a pure virtual call";
- B b;
-}
-
-void doubleFree() {
- qCDebug(shared) << "About to double delete memory";
- int* blah = new int(200);
- delete blah;
- delete blah;
-}
-
-void nullDeref() {
- qCDebug(shared) << "About to dereference a null pointer";
- int* p = nullptr;
- *p = 1;
-}
-
-void doAbort() {
- qCDebug(shared) << "About to abort";
- abort();
-}
-
-void outOfBoundsVectorCrash() {
- qCDebug(shared) << "std::vector out of bounds crash!";
- std::vector v;
- v[0] = 42;
-}
-
-void newFault() {
- qCDebug(shared) << "About to crash inside new fault";
-
- // Force crash with multiple large allocations
- while (true) {
- const size_t GIGABYTE = 1024 * 1024 * 1024;
- new char[GIGABYTE];
- }
-
-}
+void pureVirtualCall();
+void doubleFree();
+void nullDeref();
+void doAbort();
+void outOfBoundsVectorCrash();
+void newFault();
}
From c01fd02de2c5f427e56d75b29b57fcd4122e6d0c Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Mon, 30 Apr 2018 17:23:49 -0700
Subject: [PATCH 033/192] Corrected parameter type.
---
interface/src/ui/Snapshot.cpp | 2 +-
interface/src/ui/Snapshot.h | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp
index 9eb64dcb14..1af7086933 100644
--- a/interface/src/ui/Snapshot.cpp
+++ b/interface/src/ui/Snapshot.cpp
@@ -92,7 +92,7 @@ QTemporaryFile* Snapshot::saveTempSnapshot(QImage image) {
return static_cast(savedFileForSnapshot(image, true, QString(), QString()));
}
-QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, QString userSelectedPathname) {
+QFile* Snapshot::savedFileForSnapshot(QImage & shot, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname) {
// adding URL to snapshot
QUrl currentURL = DependencyManager::get()->currentPublicAddress();
diff --git a/interface/src/ui/Snapshot.h b/interface/src/ui/Snapshot.h
index 20e9e8339f..86c860cfcb 100644
--- a/interface/src/ui/Snapshot.h
+++ b/interface/src/ui/Snapshot.h
@@ -51,7 +51,7 @@ public slots:
Q_INVOKABLE QString getSnapshotsLocation();
Q_INVOKABLE void setSnapshotsLocation(const QString& location);
private:
- static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename, QString userSelectedPathname);
+ static QFile* savedFileForSnapshot(QImage & image, bool isTemporary, const QString& userSelectedFilename, const QString& userSelectedPathname);
};
#endif // hifi_Snapshot_h
From d5610a2f37885a0748129bc3de9dd77261ad12b0 Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Mon, 30 Apr 2018 17:52:44 -0700
Subject: [PATCH 034/192] WTF...
---
interface/src/ui/Snapshot.cpp | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/interface/src/ui/Snapshot.cpp b/interface/src/ui/Snapshot.cpp
index 1af7086933..d52f01223c 100644
--- a/interface/src/ui/Snapshot.cpp
+++ b/interface/src/ui/Snapshot.cpp
@@ -73,9 +73,9 @@ SnapshotMetaData* Snapshot::parseSnapshotData(QString snapshotPath) {
return data;
}
-QString Snapshot::saveSnapshot(QImage image, const QString& filename) {
+QString Snapshot::saveSnapshot(QImage image, const QString& filename, const QString& pathname) {
- QFile* snapshotFile = savedFileForSnapshot(image, false, filename);
+ QFile* snapshotFile = savedFileForSnapshot(image, false, filename, pathname);
// we don't need the snapshot file, so close it, grab its filename and delete it
snapshotFile->close();
From 24ac342c6bc3cc5feeed186a2f55c4631d1b5128 Mon Sep 17 00:00:00 2001
From: Ryan Huffman
Date: Tue, 10 Apr 2018 11:41:59 -0700
Subject: [PATCH 035/192] Add support for client texture selection
---
interface/src/Application.cpp | 1 +
libraries/baking/src/FBXBaker.cpp | 87 +++++++-----
libraries/baking/src/FBXBaker.h | 4 +-
libraries/baking/src/ModelBaker.cpp | 126 +++++++++++++++---
libraries/baking/src/ModelBaker.h | 8 +-
libraries/baking/src/OBJBaker.cpp | 57 +++-----
libraries/baking/src/OBJBaker.h | 5 +-
libraries/baking/src/TextureBaker.cpp | 63 +++++++--
libraries/baking/src/TextureBaker.h | 14 +-
libraries/fbx/src/FBXReader.cpp | 1 +
libraries/fbx/src/FBXReader_Material.cpp | 4 +
libraries/gpu-gl/src/gpu/gl41/GL41Backend.h | 4 +-
libraries/gpu-gl/src/gpu/gl45/GL45Backend.h | 2 +
.../src/gpu/gl45/GL45BackendTexture.cpp | 18 +++
libraries/gpu-gles/src/gpu/gles/GLESBackend.h | 3 +-
libraries/gpu/src/gpu/Context.h | 2 +
libraries/gpu/src/gpu/Texture.h | 5 +
libraries/gpu/src/gpu/Texture_ktx.cpp | 77 ++++++-----
libraries/ktx/src/TextureMeta.cpp | 58 ++++++++
libraries/ktx/src/TextureMeta.h | 42 ++++++
libraries/ktx/src/khronos/KHR.h | 59 ++++++++
.../src/model-networking/ModelCache.cpp | 5 +-
.../src/model-networking/TextureCache.cpp | 114 +++++++++++++---
.../src/model-networking/TextureCache.h | 22 ++-
libraries/networking/src/ResourceCache.cpp | 6 +-
tools/oven/src/DomainBaker.cpp | 2 +-
26 files changed, 614 insertions(+), 175 deletions(-)
create mode 100644 libraries/ktx/src/TextureMeta.cpp
create mode 100644 libraries/ktx/src/TextureMeta.h
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index c38caca090..6756fbe255 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -1296,6 +1296,7 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
// Create the main thread context, the GPU backend, and the display plugins
initializeGL();
+ DependencyManager::get()->setGPUContext(_gpuContext);
qCDebug(interfaceapp, "Initialized Display.");
// Create the rendering engine. This can be slow on some machines due to lots of
// GPU pipeline creation.
diff --git a/libraries/baking/src/FBXBaker.cpp b/libraries/baking/src/FBXBaker.cpp
index 0407c8508c..175698eeea 100644
--- a/libraries/baking/src/FBXBaker.cpp
+++ b/libraries/baking/src/FBXBaker.cpp
@@ -70,17 +70,66 @@ void FBXBaker::bakeSourceCopy() {
return;
}
- // export the FBX with re-written texture references
- exportScene();
-
- if (shouldStop()) {
- return;
- }
-
// check if we're already done with textures (in case we had none to re-write)
checkIfTexturesFinished();
}
+void FBXBaker::embedTextureMetaData() {
+ std::vector embeddedTextureNodes;
+
+ for (FBXNode& rootChild : _rootNode.children) {
+ if (rootChild.name == "Objects") {
+ qlonglong maxId = 0;
+ for (auto &child : rootChild.children) {
+ if (child.properties.length() == 3) {
+ maxId = std::max(maxId, child.properties[0].toLongLong());
+ }
+ }
+
+ for (auto& object : rootChild.children) {
+ if (object.name == "Texture") {
+ QVariant relativeFilename;
+ for (auto& child : object.children) {
+ if (child.name == "RelativeFilename") {
+ relativeFilename = child.properties[0];
+ break;
+ }
+ }
+
+ if (relativeFilename.isNull() || !relativeFilename.toString().endsWith(BAKED_META_TEXTURE_SUFFIX)) {
+ continue;
+ }
+
+ FBXNode videoNode;
+ videoNode.name = "Video";
+ videoNode.properties.append(++maxId);
+ videoNode.properties.append(object.properties[1]);
+ videoNode.properties.append("Clip");
+
+ QString bakedTextureFilePath {
+ _bakedOutputDir + "/" + relativeFilename.toString()
+ };
+ qDebug() << "Location of texture: " << bakedTextureFilePath;
+
+ QFile textureFile { bakedTextureFilePath };
+ if (!textureFile.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to open: " << bakedTextureFilePath;
+ continue;
+ }
+
+ videoNode.children.append({ "RelativeFilename", { relativeFilename }, { } });
+ videoNode.children.append({ "Content", { textureFile.readAll() }, { } });
+
+ rootChild.children.append(videoNode);
+
+ textureFile.close();
+ }
+ }
+ }
+ }
+
+}
+
void FBXBaker::setupOutputFolder() {
// make sure there isn't already an output directory using the same name
if (QDir(_bakedOutputDir).exists()) {
@@ -352,27 +401,3 @@ void FBXBaker::rewriteAndBakeSceneTextures() {
}
}
}
-
-void FBXBaker::exportScene() {
- // save the relative path to this FBX inside our passed output folder
- auto fileName = _modelURL.fileName();
- auto baseName = fileName.left(fileName.lastIndexOf('.'));
- auto bakedFilename = baseName + BAKED_FBX_EXTENSION;
-
- _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename;
-
- auto fbxData = FBXWriter::encodeFBX(_rootNode);
-
- QFile bakedFile(_bakedModelFilePath);
-
- if (!bakedFile.open(QIODevice::WriteOnly)) {
- handleError("Error opening " + _bakedModelFilePath + " for writing");
- return;
- }
-
- bakedFile.write(fbxData);
-
- _outputFiles.push_back(_bakedModelFilePath);
-
- qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath;
-}
diff --git a/libraries/baking/src/FBXBaker.h b/libraries/baking/src/FBXBaker.h
index 2888a60f73..58a7bffa18 100644
--- a/libraries/baking/src/FBXBaker.h
+++ b/libraries/baking/src/FBXBaker.h
@@ -26,8 +26,6 @@
#include
-static const QString BAKED_FBX_EXTENSION = ".baked.fbx";
-
using TextureBakerThreadGetter = std::function;
class FBXBaker : public ModelBaker {
@@ -51,11 +49,11 @@ private:
void loadSourceFBX();
void importScene();
+ void embedTextureMetaData();
void rewriteAndBakeSceneModels();
void rewriteAndBakeSceneTextures();
void exportScene();
- FBXNode _rootNode;
FBXGeometry* _geometry;
QHash _textureNameMatchCount;
QHash _remappedTexturePaths;
diff --git a/libraries/baking/src/ModelBaker.cpp b/libraries/baking/src/ModelBaker.cpp
index 16a0c89c7f..020a0dbfc2 100644
--- a/libraries/baking/src/ModelBaker.cpp
+++ b/libraries/baking/src/ModelBaker.cpp
@@ -248,7 +248,7 @@ QString ModelBaker::compressTexture(QString modelTextureFileName, image::Texture
QFileInfo modelTextureFileInfo{ modelTextureFileName.replace("\\", "/") };
- if (modelTextureFileInfo.suffix() == BAKED_TEXTURE_EXT.mid(1)) {
+ if (modelTextureFileInfo.suffix() == BAKED_TEXTURE_KTX_EXT.mid(1)) {
// re-baking a model that already references baked textures
// this is an error - return from here
handleError("Cannot re-bake a file that already references compressed textures");
@@ -273,31 +273,31 @@ QString ModelBaker::compressTexture(QString modelTextureFileName, image::Texture
}
auto urlToTexture = getTextureURL(modelTextureFileInfo, modelTextureFileName, !textureContent.isNull());
- QString bakedTextureFileName;
+ QString baseTextureFileName;
if (_remappedTexturePaths.contains(urlToTexture)) {
- bakedTextureFileName = _remappedTexturePaths[urlToTexture];
+ baseTextureFileName = _remappedTexturePaths[urlToTexture];
} else {
// construct the new baked texture file name and file path
// ensuring that the baked texture will have a unique name
// even if there was another texture with the same name at a different path
- bakedTextureFileName = createBakedTextureFileName(modelTextureFileInfo);
- _remappedTexturePaths[urlToTexture] = bakedTextureFileName;
+ baseTextureFileName = createBaseTextureFileName(modelTextureFileInfo);
+ _remappedTexturePaths[urlToTexture] = baseTextureFileName;
}
qCDebug(model_baking).noquote() << "Re-mapping" << modelTextureFileName
- << "to" << bakedTextureFileName;
+ << "to" << baseTextureFileName;
- QString bakedTextureFilePath{
- _bakedOutputDir + "/" + bakedTextureFileName
+ QString bakedTextureFilePath {
+ _bakedOutputDir + "/" + baseTextureFileName + BAKED_META_TEXTURE_SUFFIX
};
- textureChild = bakedTextureFileName;
+ textureChild = baseTextureFileName + BAKED_META_TEXTURE_SUFFIX;
if (!_bakingTextures.contains(urlToTexture)) {
_outputFiles.push_back(bakedTextureFilePath);
// bake this texture asynchronously
- bakeTexture(urlToTexture, textureType, _bakedOutputDir, bakedTextureFileName, textureContent);
+ bakeTexture(urlToTexture, textureType, _bakedOutputDir, baseTextureFileName, textureContent);
}
}
@@ -309,7 +309,7 @@ void ModelBaker::bakeTexture(const QUrl& textureURL, image::TextureUsage::Type t
// start a bake for this texture and add it to our list to keep track of
QSharedPointer bakingTexture{
- new TextureBaker(textureURL, textureType, outputDir, bakedFilename, textureContent),
+ new TextureBaker(textureURL, textureType, outputDir, "../", bakedFilename, textureContent),
&TextureBaker::deleteLater
};
@@ -484,30 +484,30 @@ void ModelBaker::checkIfTexturesFinished() {
} else {
qCDebug(model_baking) << "Finished baking, emitting finished" << _modelURL;
+ texturesFinished();
+
setIsFinished(true);
}
}
}
-QString ModelBaker::createBakedTextureFileName(const QFileInfo& textureFileInfo) {
+QString ModelBaker::createBaseTextureFileName(const QFileInfo& textureFileInfo) {
// first make sure we have a unique base name for this texture
// in case another texture referenced by this model has the same base name
auto& nameMatches = _textureNameMatchCount[textureFileInfo.baseName()];
- QString bakedTextureFileName{ textureFileInfo.completeBaseName() };
+ QString baseTextureFileName{ textureFileInfo.completeBaseName() };
if (nameMatches > 0) {
// there are already nameMatches texture with this name
// append - and that number to our baked texture file name so that it is unique
- bakedTextureFileName += "-" + QString::number(nameMatches);
+ baseTextureFileName += "-" + QString::number(nameMatches);
}
- bakedTextureFileName += BAKED_TEXTURE_EXT;
-
// increment the number of name matches
++nameMatches;
- return bakedTextureFileName;
+ return baseTextureFileName;
}
void ModelBaker::setWasAborted(bool wasAborted) {
@@ -519,3 +519,95 @@ void ModelBaker::setWasAborted(bool wasAborted) {
}
}
}
+
+void ModelBaker::texturesFinished() {
+ embedTextureMetaData();
+ exportScene();
+}
+
+void ModelBaker::embedTextureMetaData() {
+ std::vector embeddedTextureNodes;
+
+ for (FBXNode& rootChild : _rootNode.children) {
+ if (rootChild.name == "Objects") {
+ qlonglong maxId = 0;
+ for (auto &child : rootChild.children) {
+ if (child.properties.length() == 3) {
+ maxId = std::max(maxId, child.properties[0].toLongLong());
+ }
+ }
+
+ qDebug() << "Max id found was: " << maxId;
+
+ for (auto& object : rootChild.children) {
+ if (object.name == "Texture") {
+ QVariant relativeFilename;
+ for (auto& child : object.children) {
+ if (child.name == "RelativeFilename") {
+ relativeFilename = child.properties[0];
+ break;
+ }
+ }
+
+ if (relativeFilename.isNull()
+ || !relativeFilename.toString().endsWith(BAKED_META_TEXTURE_SUFFIX)) {
+ continue;
+ }
+ if (object.properties.length() < 2) {
+ qWarning() << "Found texture with unexpected number of properties: " << object.name;
+ continue;
+ }
+
+ FBXNode videoNode;
+ videoNode.name = "Video";
+ videoNode.properties.append(++maxId);
+ videoNode.properties.append(object.properties[1]);
+ videoNode.properties.append("Clip");
+
+ QString bakedTextureFilePath {
+ _bakedOutputDir + "/" + relativeFilename.toString()
+ };
+ qDebug() << "Location of texture: " << bakedTextureFilePath;
+
+ QFile textureFile { bakedTextureFilePath };
+ if (!textureFile.open(QIODevice::ReadOnly)) {
+ qWarning() << "Failed to open: " << bakedTextureFilePath;
+ continue;
+ }
+
+ videoNode.children.append({ "RelativeFilename", { relativeFilename }, { } });
+ videoNode.children.append({ "Content", { textureFile.readAll() }, { } });
+
+ rootChild.children.append(videoNode);
+
+ textureFile.close();
+ }
+ }
+ }
+ }
+
+}
+
+void ModelBaker::exportScene() {
+ // save the relative path to this FBX inside our passed output folder
+ auto fileName = _modelURL.fileName();
+ auto baseName = fileName.left(fileName.lastIndexOf('.'));
+ auto bakedFilename = baseName + BAKED_FBX_EXTENSION;
+
+ _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename;
+
+ auto fbxData = FBXWriter::encodeFBX(_rootNode);
+
+ QFile bakedFile(_bakedModelFilePath);
+
+ if (!bakedFile.open(QIODevice::WriteOnly)) {
+ handleError("Error opening " + _bakedModelFilePath + " for writing");
+ return;
+ }
+
+ bakedFile.write(fbxData);
+
+ _outputFiles.push_back(_bakedModelFilePath);
+
+ qCDebug(model_baking) << "Exported" << _modelURL << "with re-written paths to" << _bakedModelFilePath;
+}
diff --git a/libraries/baking/src/ModelBaker.h b/libraries/baking/src/ModelBaker.h
index 6fd529af92..1fd77ab761 100644
--- a/libraries/baking/src/ModelBaker.h
+++ b/libraries/baking/src/ModelBaker.h
@@ -29,6 +29,8 @@
using TextureBakerThreadGetter = std::function;
using GetMaterialIDCallback = std::function ;
+static const QString BAKED_FBX_EXTENSION = ".baked.fbx";
+
class ModelBaker : public Baker {
Q_OBJECT
@@ -49,7 +51,11 @@ public slots:
protected:
void checkIfTexturesFinished();
+ void texturesFinished();
+ void embedTextureMetaData();
+ void exportScene();
+ FBXNode _rootNode;
QHash _textureContentMap;
QUrl _modelURL;
QString _bakedOutputDir;
@@ -63,7 +69,7 @@ private slots:
void handleAbortedTexture();
private:
- QString createBakedTextureFileName(const QFileInfo & textureFileInfo);
+ QString createBaseTextureFileName(const QFileInfo & textureFileInfo);
QUrl getTextureURL(const QFileInfo& textureFileInfo, QString relativeFileName, bool isEmbedded = false);
void bakeTexture(const QUrl & textureURL, image::TextureUsage::Type textureType, const QDir & outputDir,
const QString & bakedFilename, const QByteArray & textureContent);
diff --git a/libraries/baking/src/OBJBaker.cpp b/libraries/baking/src/OBJBaker.cpp
index 85771ff2e3..1fe53f26eb 100644
--- a/libraries/baking/src/OBJBaker.cpp
+++ b/libraries/baking/src/OBJBaker.cpp
@@ -147,31 +147,7 @@ void OBJBaker::bakeOBJ() {
auto geometry = reader.readOBJ(objData, QVariantHash(), combineParts, _modelURL);
// Write OBJ Data as FBX tree nodes
- FBXNode rootNode;
- createFBXNodeTree(rootNode, *geometry);
-
- // Serialize the resultant FBX tree
- auto encodedFBX = FBXWriter::encodeFBX(rootNode);
-
- // Export as baked FBX
- auto fileName = _modelURL.fileName();
- auto baseName = fileName.left(fileName.lastIndexOf('.'));
- auto bakedFilename = baseName + ".baked.fbx";
-
- _bakedModelFilePath = _bakedOutputDir + "/" + bakedFilename;
-
- QFile bakedFile;
- bakedFile.setFileName(_bakedModelFilePath);
- if (!bakedFile.open(QIODevice::WriteOnly)) {
- handleError("Error opening " + _bakedModelFilePath + " for writing");
- return;
- }
-
- bakedFile.write(encodedFBX);
-
- // Export successful
- _outputFiles.push_back(_bakedModelFilePath);
- qCDebug(model_baking) << "Exported" << _modelURL << "to" << _bakedModelFilePath;
+ createFBXNodeTree(_rootNode, *geometry);
checkIfTexturesFinished();
}
@@ -203,15 +179,17 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
globalSettingsNode.children = { properties70Node };
// Generating Object node
- _objectNode.name = OBJECTS_NODE_NAME;
+ FBXNode objectNode;
+ objectNode.name = OBJECTS_NODE_NAME;
// Generating Object node's child - Geometry node
FBXNode geometryNode;
geometryNode.name = GEOMETRY_NODE_NAME;
+ NodeID geometryID;
{
- _geometryID = nextNodeID();
+ geometryID = nextNodeID();
geometryNode.properties = {
- _geometryID,
+ geometryID,
GEOMETRY_NODE_NAME,
MESH
};
@@ -226,12 +204,13 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
// Generating Object node's child - Model node
FBXNode modelNode;
modelNode.name = MODEL_NODE_NAME;
+ NodeID modelID;
{
- _modelID = nextNodeID();
- modelNode.properties = { _modelID, MODEL_NODE_NAME, MESH };
+ modelID = nextNodeID();
+ modelNode.properties = { modelID, MODEL_NODE_NAME, MESH };
}
- _objectNode.children = { geometryNode, modelNode };
+ objectNode.children = { geometryNode, modelNode };
// Generating Objects node's child - Material node
auto& meshParts = geometry.meshes[0].parts;
@@ -247,7 +226,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
setMaterialNodeProperties(materialNode, meshPart.materialID, geometry);
}
- _objectNode.children.append(materialNode);
+ objectNode.children.append(materialNode);
}
// Generating Texture Node
@@ -257,13 +236,13 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
QString material = meshParts[i].materialID;
FBXMaterial currentMaterial = geometry.materials[material];
if (!currentMaterial.albedoTexture.filename.isEmpty() || !currentMaterial.specularTexture.filename.isEmpty()) {
- _textureID = nextNodeID();
- _mapTextureMaterial.emplace_back(_textureID, i);
+ auto textureID = nextNodeID();
+ _mapTextureMaterial.emplace_back(textureID, i);
FBXNode textureNode;
{
textureNode.name = TEXTURE_NODE_NAME;
- textureNode.properties = { _textureID };
+ textureNode.properties = { textureID, "texture" + QString::number(textureID) };
}
// Texture node child - TextureName node
@@ -295,7 +274,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
textureNode.children = { textureNameNode, relativeFilenameNode };
- _objectNode.children.append(textureNode);
+ objectNode.children.append(textureNode);
}
}
@@ -306,14 +285,14 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
// connect Geometry to Model
FBXNode cNode;
cNode.name = C_NODE_NAME;
- cNode.properties = { CONNECTIONS_NODE_PROPERTY, _geometryID, _modelID };
+ cNode.properties = { CONNECTIONS_NODE_PROPERTY, geometryID, modelID };
connectionsNode.children = { cNode };
// connect all materials to model
for (auto& materialID : _materialIDs) {
FBXNode cNode;
cNode.name = C_NODE_NAME;
- cNode.properties = { CONNECTIONS_NODE_PROPERTY, materialID, _modelID };
+ cNode.properties = { CONNECTIONS_NODE_PROPERTY, materialID, modelID };
connectionsNode.children.append(cNode);
}
@@ -341,7 +320,7 @@ void OBJBaker::createFBXNodeTree(FBXNode& rootNode, FBXGeometry& geometry) {
}
// Make all generated nodes children of rootNode
- rootNode.children = { globalSettingsNode, _objectNode, connectionsNode };
+ rootNode.children = { globalSettingsNode, objectNode, connectionsNode };
}
// Set properties for material nodes
diff --git a/libraries/baking/src/OBJBaker.h b/libraries/baking/src/OBJBaker.h
index e888c7b1d8..8e49692d35 100644
--- a/libraries/baking/src/OBJBaker.h
+++ b/libraries/baking/src/OBJBaker.h
@@ -43,12 +43,9 @@ private:
void setMaterialNodeProperties(FBXNode& materialNode, QString material, FBXGeometry& geometry);
NodeID nextNodeID() { return _nodeID++; }
+
NodeID _nodeID { 0 };
- NodeID _geometryID;
- NodeID _modelID;
std::vector _materialIDs;
- NodeID _textureID;
std::vector> _mapTextureMaterial;
- FBXNode _objectNode;
};
#endif // hifi_OBJBaker_h
diff --git a/libraries/baking/src/TextureBaker.cpp b/libraries/baking/src/TextureBaker.cpp
index b6edd07965..7a5dc85a61 100644
--- a/libraries/baking/src/TextureBaker.cpp
+++ b/libraries/baking/src/TextureBaker.cpp
@@ -18,26 +18,30 @@
#include
#include
#include
+#include
#include "ModelBakingLoggingCategory.h"
#include "TextureBaker.h"
-const QString BAKED_TEXTURE_EXT = ".ktx";
+const QString BAKED_TEXTURE_KTX_EXT = ".ktx";
+const QString BAKED_TEXTURE_BCN_SUFFIX = "_bcn.ktx";
+const QString BAKED_META_TEXTURE_SUFFIX = ".texmeta.json";
TextureBaker::TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
- const QDir& outputDirectory, const QString& bakedFilename,
- const QByteArray& textureContent) :
+ const QDir& outputDirectory, const QString& metaTexturePathPrefix,
+ const QString& baseFilename, const QByteArray& textureContent) :
_textureURL(textureURL),
_originalTexture(textureContent),
_textureType(textureType),
+ _baseFilename(baseFilename),
_outputDirectory(outputDirectory),
- _bakedTextureFileName(bakedFilename)
+ _metaTexturePathPrefix(metaTexturePathPrefix)
{
- if (bakedFilename.isEmpty()) {
+ if (baseFilename.isEmpty()) {
// figure out the baked texture filename
auto originalFilename = textureURL.fileName();
- _bakedTextureFileName = originalFilename.left(originalFilename.lastIndexOf('.')) + BAKED_TEXTURE_EXT;
+ _baseFilename = originalFilename.left(originalFilename.lastIndexOf('.'));
}
}
@@ -118,6 +122,19 @@ void TextureBaker::processTexture() {
auto hashData = QCryptographicHash::hash(_originalTexture, QCryptographicHash::Md5);
std::string hash = hashData.toHex().toStdString();
+ TextureMeta meta;
+
+ {
+ auto filePath = _outputDirectory.absoluteFilePath(_textureURL.fileName());
+ QFile file { filePath };
+ if (!file.open(QIODevice::WriteOnly) || file.write(_originalTexture) == -1) {
+ handleError("Could not write meta texture for " + _textureURL.toString());
+ return;
+ }
+ _outputFiles.push_back(filePath);
+ meta.original =_metaTexturePathPrefix +_textureURL.fileName();
+ }
+
// IMPORTANT: _originalTexture is empty past this point
auto processedTexture = image::processImage(std::move(_originalTexture), _textureURL.toString().toStdString(),
ABSOLUTE_MAX_TEXTURE_NUM_PIXELS, _textureType, _abortProcessing);
@@ -142,15 +159,37 @@ void TextureBaker::processTexture() {
const char* data = reinterpret_cast(memKTX->_storage->data());
const size_t length = memKTX->_storage->size();
+ const char* name = khronos::gl::texture::toString(memKTX->_header.getGLInternaFormat());
+ if (name == nullptr) {
+ handleError("Could not determine internal format for compressed KTX: " + _textureURL.toString());
+ return;
+ }
+
+ qDebug() << "Found type: " << name;
// attempt to write the baked texture to the destination file path
- auto filePath = _outputDirectory.absoluteFilePath(_bakedTextureFileName);
- QFile bakedTextureFile { filePath };
-
- if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
- handleError("Could not write baked texture for " + _textureURL.toString());
- } else {
+ {
+ auto fileName = _baseFilename + BAKED_TEXTURE_BCN_SUFFIX;
+ auto filePath = _outputDirectory.absoluteFilePath(fileName);
+ QFile bakedTextureFile { filePath };
+ if (!bakedTextureFile.open(QIODevice::WriteOnly) || bakedTextureFile.write(data, length) == -1) {
+ handleError("Could not write baked texture for " + _textureURL.toString());
+ return;
+ }
_outputFiles.push_back(filePath);
+ meta.availableTextureTypes[memKTX->_header.getGLInternaFormat()] = _metaTexturePathPrefix + fileName;
+ }
+
+
+ {
+ auto data = meta.serialize();
+ _metaTextureFileName = _outputDirectory.absoluteFilePath(_baseFilename + BAKED_META_TEXTURE_SUFFIX);
+ QFile file { _metaTextureFileName };
+ if (!file.open(QIODevice::WriteOnly) || file.write(data) == -1) {
+ handleError("Could not write meta texture for " + _textureURL.toString());
+ } else {
+ _outputFiles.push_back(_metaTextureFileName);
+ }
}
qCDebug(model_baking) << "Baked texture" << _textureURL;
diff --git a/libraries/baking/src/TextureBaker.h b/libraries/baking/src/TextureBaker.h
index 90ecfe52f7..93b01080cd 100644
--- a/libraries/baking/src/TextureBaker.h
+++ b/libraries/baking/src/TextureBaker.h
@@ -21,22 +21,22 @@
#include "Baker.h"
-extern const QString BAKED_TEXTURE_EXT;
+extern const QString BAKED_TEXTURE_KTX_EXT;
+extern const QString BAKED_META_TEXTURE_SUFFIX;
class TextureBaker : public Baker {
Q_OBJECT
public:
TextureBaker(const QUrl& textureURL, image::TextureUsage::Type textureType,
- const QDir& outputDirectory, const QString& bakedFilename = QString(),
- const QByteArray& textureContent = QByteArray());
+ const QDir& outputDirectory, const QString& metaTexturePathPrefix = "",
+ const QString& baseFilename = QString(), const QByteArray& textureContent = QByteArray());
const QByteArray& getOriginalTexture() const { return _originalTexture; }
QUrl getTextureURL() const { return _textureURL; }
- QString getDestinationFilePath() const { return _outputDirectory.absoluteFilePath(_bakedTextureFileName); }
- QString getBakedTextureFileName() const { return _bakedTextureFileName; }
+ QString getMetaTextureFileName() const { return _metaTextureFileName; }
virtual void setWasAborted(bool wasAborted) override;
@@ -58,8 +58,10 @@ private:
QByteArray _originalTexture;
image::TextureUsage::Type _textureType;
+ QString _baseFilename;
QDir _outputDirectory;
- QString _bakedTextureFileName;
+ QString _metaTextureFileName;
+ QString _metaTexturePathPrefix;
std::atomic _abortProcessing { false };
};
diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp
index 1e59646795..1f237edfb0 100644
--- a/libraries/fbx/src/FBXReader.cpp
+++ b/libraries/fbx/src/FBXReader.cpp
@@ -1101,6 +1101,7 @@ FBXGeometry* FBXReader::extractFBXGeometry(const QVariantHash& mapping, const QS
}
if (!content.isEmpty()) {
_textureContent.insert(filepath, content);
+ qDebug() << "Adding content: " << filepath << content.length();
}
} else if (object.name == "Material") {
FBXMaterial material;
diff --git a/libraries/fbx/src/FBXReader_Material.cpp b/libraries/fbx/src/FBXReader_Material.cpp
index 4aa3044934..88dc7f599d 100644
--- a/libraries/fbx/src/FBXReader_Material.cpp
+++ b/libraries/fbx/src/FBXReader_Material.cpp
@@ -85,12 +85,16 @@ FBXTexture FBXReader::getTexture(const QString& textureID) {
FBXTexture texture;
const QByteArray& filepath = _textureFilepaths.value(textureID);
texture.content = _textureContent.value(filepath);
+ qDebug() << "Getting texture: " << textureID << filepath << texture.content.length();
if (texture.content.isEmpty()) { // the content is not inlined
+ qDebug() << "Texture is not inlined";
texture.filename = _textureFilenames.value(textureID);
} else { // use supplied filepath for inlined content
+ qDebug() << "Texture is inlined";
texture.filename = filepath;
}
+ qDebug() << "Path: " << texture.filename;
texture.id = textureID;
texture.name = _textureNames.value(textureID);
diff --git a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h
index 9479321747..f3b452b1f9 100644
--- a/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h
+++ b/libraries/gpu-gl/src/gpu/gl41/GL41Backend.h
@@ -52,6 +52,8 @@ public:
static const std::string GL41_VERSION;
const std::string& getVersion() const override { return GL41_VERSION; }
+ bool supportedTextureFormat(const gpu::Element& format) override;
+
class GL41Texture : public GLTexture {
using Parent = GLTexture;
friend class GL41Backend;
@@ -173,8 +175,6 @@ protected:
void makeProgramBindings(ShaderObject& shaderObject) override;
int makeResourceBufferSlots(GLuint glprogram, const Shader::BindingSet& slotBindings,Shader::SlotSet& resourceBuffers) override;
- static bool supportedTextureFormat(const gpu::Element& format);
-
};
} }
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
index c23a83eaf9..616b6d1075 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45Backend.h
@@ -54,6 +54,8 @@ public:
static const std::string GL45_VERSION;
const std::string& getVersion() const override { return GL45_VERSION; }
+ bool supportedTextureFormat(const gpu::Element& format) override;
+
class GL45Texture : public GLTexture {
using Parent = GLTexture;
friend class GL45Backend;
diff --git a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
index 4d5ffefa67..6b3c99ccc3 100644
--- a/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
+++ b/libraries/gpu-gl/src/gpu/gl45/GL45BackendTexture.cpp
@@ -32,6 +32,24 @@ using namespace gpu::gl45;
#define FORCE_STRICT_TEXTURE 0
#define ENABLE_SPARSE_TEXTURE 0
+bool GL45Backend::supportedTextureFormat(const gpu::Element& format) {
+ switch (format.getSemantic()) {
+ case gpu::Semantic::COMPRESSED_ETC2_RGB:
+ case gpu::Semantic::COMPRESSED_ETC2_SRGB:
+ case gpu::Semantic::COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA:
+ case gpu::Semantic::COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA:
+ case gpu::Semantic::COMPRESSED_ETC2_RGBA:
+ case gpu::Semantic::COMPRESSED_ETC2_SRGBA:
+ case gpu::Semantic::COMPRESSED_EAC_RED:
+ case gpu::Semantic::COMPRESSED_EAC_RED_SIGNED:
+ case gpu::Semantic::COMPRESSED_EAC_XY:
+ case gpu::Semantic::COMPRESSED_EAC_XY_SIGNED:
+ return false;
+ default:
+ return true;
+ }
+}
+
GLTexture* GL45Backend::syncGPUObject(const TexturePointer& texturePointer) {
if (!texturePointer) {
return nullptr;
diff --git a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h
index 38e28e630a..47a123718a 100644
--- a/libraries/gpu-gles/src/gpu/gles/GLESBackend.h
+++ b/libraries/gpu-gles/src/gpu/gles/GLESBackend.h
@@ -32,7 +32,6 @@ public:
static const GLint RESOURCE_TRANSFER_EXTRA_TEX_UNIT { 33 };
static const GLint RESOURCE_BUFFER_TEXBUF_TEX_UNIT { 34 };
static const GLint RESOURCE_BUFFER_SLOT0_TEX_UNIT { 35 };
- static bool supportedTextureFormat(const gpu::Element& format);
explicit GLESBackend(bool syncCache) : Parent(syncCache) {}
GLESBackend() : Parent() {}
virtual ~GLESBackend() {
@@ -40,6 +39,8 @@ public:
// which is pure virtual from GLBackend's dtor.
resetStages();
}
+
+ bool supportedTextureFormat(const gpu::Element& format) override;
static const std::string GLES_VERSION;
const std::string& getVersion() const override { return GLES_VERSION; }
diff --git a/libraries/gpu/src/gpu/Context.h b/libraries/gpu/src/gpu/Context.h
index eda8fee596..8c5a4d493e 100644
--- a/libraries/gpu/src/gpu/Context.h
+++ b/libraries/gpu/src/gpu/Context.h
@@ -64,6 +64,8 @@ public:
virtual void recycle() const = 0;
virtual void downloadFramebuffer(const FramebufferPointer& srcFramebuffer, const Vec4i& region, QImage& destImage) = 0;
+ virtual bool supportedTextureFormat(const gpu::Element& format) = 0;
+
// Shared header between C++ and GLSL
#include "TransformCamera_shared.slh"
diff --git a/libraries/gpu/src/gpu/Texture.h b/libraries/gpu/src/gpu/Texture.h
index 4d82aba595..09b2bc9475 100755
--- a/libraries/gpu/src/gpu/Texture.h
+++ b/libraries/gpu/src/gpu/Texture.h
@@ -37,6 +37,10 @@ namespace ktx {
using KeyValues = std::list;
}
+namespace khronos { namespace gl { namespace texture {
+ enum class InternalFormat: uint32_t;
+}}}
+
namespace gpu {
@@ -565,6 +569,7 @@ public:
static bool evalKTXFormat(const Element& mipFormat, const Element& texelFormat, ktx::Header& header);
static bool evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat);
+ static bool getCompressedFormat(khronos::gl::texture::InternalFormat format, Element& elFormat);
protected:
const TextureUsageType _usageType;
diff --git a/libraries/gpu/src/gpu/Texture_ktx.cpp b/libraries/gpu/src/gpu/Texture_ktx.cpp
index 5e354c0a5c..0822af3cfb 100644
--- a/libraries/gpu/src/gpu/Texture_ktx.cpp
+++ b/libraries/gpu/src/gpu/Texture_ktx.cpp
@@ -619,6 +619,47 @@ bool Texture::evalKTXFormat(const Element& mipFormat, const Element& texelFormat
return true;
}
+bool Texture::getCompressedFormat(ktx::GLInternalFormat format, Element& elFormat) {
+ if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_SRGB;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_MASK;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_SRGBA;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RED_RGTC1) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_RED;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RG_RGTC2) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_XY;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_HIGH;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) {
+ elFormat = Format::COLOR_COMPRESSED_BCX_HDR_RGB;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RGB8_ETC2) {
+ elFormat = Format::COLOR_COMPRESSED_ETC2_RGB;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB8_ETC2) {
+ elFormat = Format::COLOR_COMPRESSED_ETC2_SRGB;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
+ elFormat = Format::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
+ elFormat = Format::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RGBA8_ETC2_EAC) {
+ elFormat = Format::COLOR_COMPRESSED_ETC2_RGBA;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) {
+ elFormat = Format::COLOR_COMPRESSED_ETC2_SRGBA;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_R11_EAC) {
+ elFormat = Format::COLOR_COMPRESSED_EAC_RED;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SIGNED_R11_EAC) {
+ elFormat = Format::COLOR_COMPRESSED_EAC_RED_SIGNED;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_RG11_EAC) {
+ elFormat = Format::COLOR_COMPRESSED_EAC_XY;
+ } else if (format == ktx::GLInternalFormat::COMPRESSED_SIGNED_RG11_EAC) {
+ elFormat = Format::COLOR_COMPRESSED_EAC_XY_SIGNED;
+ } else {
+ return false;
+ }
+ return true;
+}
+
bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, Element& texelFormat) {
if (header.getGLFormat() == ktx::GLFormat::BGRA && header.getGLType() == ktx::GLType::UNSIGNED_BYTE && header.getTypeSize() == 1) {
if (header.getGLInternaFormat() == ktx::GLInternalFormat::RGBA8) {
@@ -661,41 +702,7 @@ bool Texture::evalTextureFormat(const ktx::Header& header, Element& mipFormat, E
mipFormat = Format::COLOR_RGB9E5;
texelFormat = Format::COLOR_RGB9E5;
} else if (header.isCompressed()) {
- if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_SRGB;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_MASK;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_SRGBA;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RED_RGTC1) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_RED;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RG_RGTC2) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_XY;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_SRGBA_HIGH;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT) {
- texelFormat = Format::COLOR_COMPRESSED_BCX_HDR_RGB;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB8_ETC2) {
- texelFormat = Format::COLOR_COMPRESSED_ETC2_RGB;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB8_ETC2) {
- texelFormat = Format::COLOR_COMPRESSED_ETC2_SRGB;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
- texelFormat = Format::COLOR_COMPRESSED_ETC2_RGB_PUNCHTHROUGH_ALPHA;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2) {
- texelFormat = Format::COLOR_COMPRESSED_ETC2_SRGB_PUNCHTHROUGH_ALPHA;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RGBA8_ETC2_EAC) {
- texelFormat = Format::COLOR_COMPRESSED_ETC2_RGBA;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC) {
- texelFormat = Format::COLOR_COMPRESSED_ETC2_SRGBA;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_R11_EAC) {
- texelFormat = Format::COLOR_COMPRESSED_EAC_RED;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SIGNED_R11_EAC) {
- texelFormat = Format::COLOR_COMPRESSED_EAC_RED_SIGNED;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_RG11_EAC) {
- texelFormat = Format::COLOR_COMPRESSED_EAC_XY;
- } else if (header.getGLInternaFormat() == ktx::GLInternalFormat::COMPRESSED_SIGNED_RG11_EAC) {
- texelFormat = Format::COLOR_COMPRESSED_EAC_XY_SIGNED;
- } else {
+ if (!getCompressedFormat(header.getGLInternaFormat(), texelFormat)) {
return false;
}
mipFormat = texelFormat;
diff --git a/libraries/ktx/src/TextureMeta.cpp b/libraries/ktx/src/TextureMeta.cpp
new file mode 100644
index 0000000000..88235d8a4b
--- /dev/null
+++ b/libraries/ktx/src/TextureMeta.cpp
@@ -0,0 +1,58 @@
+//
+// TextureMeta.cpp
+// libraries/shared/src
+//
+// Created by Ryan Huffman on 04/10/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 "TextureMeta.h"
+
+#include
+#include
+
+const QString TEXTURE_META_EXTENSION = ".texmeta.json";
+
+bool TextureMeta::deserialize(const QByteArray& data, TextureMeta* meta) {
+ QJsonParseError error;
+ auto doc = QJsonDocument::fromJson(data, &error);
+ if (!doc.isObject()) {
+ return false;
+ }
+
+ auto root = doc.object();
+ if (root.contains("original")) {
+ meta->original = root["original"].toString();
+ }
+ if (root.contains("compressed")) {
+ auto compressed = root["compressed"].toObject();
+ for (auto it = compressed.constBegin(); it != compressed.constEnd(); it++) {
+ khronos::gl::texture::InternalFormat format;
+ auto formatName = it.key().toLatin1();
+ if (khronos::gl::texture::fromString(formatName.constData(), &format)) {
+ meta->availableTextureTypes[format] = it.value().toString();
+ }
+ }
+ }
+
+ return true;
+}
+
+QByteArray TextureMeta::serialize() {
+ QJsonDocument doc;
+ QJsonObject root;
+ QJsonObject compressed;
+
+ for (auto kv : availableTextureTypes) {
+ const char* name = khronos::gl::texture::toString(kv.first);
+ compressed[name] = kv.second.toString();
+ }
+ root["original"] = original.toString();
+ root["compressed"] = compressed;
+ doc.setObject(root);
+
+ return doc.toJson();
+}
diff --git a/libraries/ktx/src/TextureMeta.h b/libraries/ktx/src/TextureMeta.h
new file mode 100644
index 0000000000..6582c29e70
--- /dev/null
+++ b/libraries/ktx/src/TextureMeta.h
@@ -0,0 +1,42 @@
+//
+// TextureMeta.h
+// libraries/shared/src
+//
+// Created by Ryan Huffman on 04/10/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_TextureMeta_h
+#define hifi_TextureMeta_h
+
+#include
+#include
+#include
+
+#include "khronos/KHR.h"
+
+extern const QString TEXTURE_META_EXTENSION;
+
+namespace std {
+ template<> struct hash {
+ using enum_type = std::underlying_type::type;
+ typedef std::size_t result_type;
+ result_type operator()(khronos::gl::texture::InternalFormat const& v) const noexcept {
+ return std::hash()(static_cast(v));
+ }
+ };
+}
+
+struct TextureMeta {
+ static bool deserialize(const QByteArray& data, TextureMeta* meta);
+ QByteArray serialize();
+
+ QUrl original;
+ std::unordered_map availableTextureTypes;
+};
+
+
+#endif // hifi_TextureMeta_h
diff --git a/libraries/ktx/src/khronos/KHR.h b/libraries/ktx/src/khronos/KHR.h
index 4ee893e4fc..617e40ce06 100644
--- a/libraries/ktx/src/khronos/KHR.h
+++ b/libraries/ktx/src/khronos/KHR.h
@@ -10,6 +10,8 @@
#ifndef khronos_khr_hpp
#define khronos_khr_hpp
+#include
+
namespace khronos {
namespace gl {
@@ -209,6 +211,63 @@ namespace khronos {
COMPRESSED_SIGNED_RG11_EAC = 0x9273,
};
+ static std::unordered_map nameToFormat {
+ { "COMPRESSED_RED", InternalFormat::COMPRESSED_RED },
+ { "COMPRESSED_RG", InternalFormat::COMPRESSED_RG },
+ { "COMPRESSED_RGB", InternalFormat::COMPRESSED_RGB },
+ { "COMPRESSED_RGBA", InternalFormat::COMPRESSED_RGBA },
+
+ { "COMPRESSED_SRGB", InternalFormat::COMPRESSED_SRGB },
+ { "COMPRESSED_SRGB_ALPHA", InternalFormat::COMPRESSED_SRGB_ALPHA },
+
+ { "COMPRESSED_ETC1_RGB8_OES", InternalFormat::COMPRESSED_ETC1_RGB8_OES },
+
+ { "COMPRESSED_SRGB_S3TC_DXT1_EXT", InternalFormat::COMPRESSED_SRGB_S3TC_DXT1_EXT },
+ { "COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT", InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT },
+ { "COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT", InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT },
+ { "COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT", InternalFormat::COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT },
+
+ { "COMPRESSED_RED_RGTC1", InternalFormat::COMPRESSED_RED_RGTC1 },
+ { "COMPRESSED_SIGNED_RED_RGTC1", InternalFormat::COMPRESSED_SIGNED_RED_RGTC1 },
+ { "COMPRESSED_RG_RGTC2", InternalFormat::COMPRESSED_RG_RGTC2 },
+ { "COMPRESSED_SIGNED_RG_RGTC2", InternalFormat::COMPRESSED_SIGNED_RG_RGTC2 },
+
+ { "COMPRESSED_RGBA_BPTC_UNORM", InternalFormat::COMPRESSED_RGBA_BPTC_UNORM },
+ { "COMPRESSED_SRGB_ALPHA_BPTC_UNORM", InternalFormat::COMPRESSED_SRGB_ALPHA_BPTC_UNORM },
+ { "COMPRESSED_RGB_BPTC_SIGNED_FLOAT", InternalFormat::COMPRESSED_RGB_BPTC_SIGNED_FLOAT },
+ { "COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT", InternalFormat::COMPRESSED_RGB_BPTC_UNSIGNED_FLOAT },
+
+ { "COMPRESSED_RGB8_ETC2", InternalFormat::COMPRESSED_RGB8_ETC2 },
+ { "COMPRESSED_SRGB8_ETC2", InternalFormat::COMPRESSED_SRGB8_ETC2 },
+ { "COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2", InternalFormat::COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2 },
+ { "COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2", InternalFormat::COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2 },
+ { "COMPRESSED_RGBA8_ETC2_EAC", InternalFormat::COMPRESSED_RGBA8_ETC2_EAC },
+ { "COMPRESSED_SRGB8_ALPHA8_ETC2_EAC", InternalFormat::COMPRESSED_SRGB8_ALPHA8_ETC2_EAC },
+
+ { "COMPRESSED_R11_EAC", InternalFormat::COMPRESSED_R11_EAC },
+ { "COMPRESSED_SIGNED_R11_EAC", InternalFormat::COMPRESSED_SIGNED_R11_EAC },
+ { "COMPRESSED_RG11_EAC", InternalFormat::COMPRESSED_RG11_EAC },
+ { "COMPRESSED_SIGNED_RG11_EAC", InternalFormat::COMPRESSED_SIGNED_RG11_EAC }
+ };
+
+ inline const char* toString(InternalFormat format) {
+ for (auto& pair : nameToFormat) {
+ if (pair.second == format) {
+ return pair.first.data();
+ }
+ }
+ return nullptr;
+ }
+
+ inline bool fromString(const char* name, InternalFormat* format) {
+ auto it = nameToFormat.find(name);
+ if (it == nameToFormat.end()) {
+ return false;
+ }
+ *format = it->second;
+ return true;
+ }
+
inline uint8_t evalUncompressedBlockBitSize(InternalFormat format) {
switch (format) {
case InternalFormat::R8:
diff --git a/libraries/model-networking/src/model-networking/ModelCache.cpp b/libraries/model-networking/src/model-networking/ModelCache.cpp
index f17cdbb7e8..6756941520 100644
--- a/libraries/model-networking/src/model-networking/ModelCache.cpp
+++ b/libraries/model-networking/src/model-networking/ModelCache.cpp
@@ -517,10 +517,11 @@ QUrl NetworkMaterial::getTextureUrl(const QUrl& baseUrl, const FBXTexture& textu
// Inlined file: cache under the fbx file to avoid namespace clashes
// NOTE: We cannot resolve the path because filename may be an absolute path
assert(texture.filename.size() > 0);
+ auto baseUrlStripped = baseUrl.toDisplayString(QUrl::RemoveFragment | QUrl::RemoveQuery | QUrl::RemoveUserInfo);
if (texture.filename.at(0) == '/') {
- return baseUrl.toString() + texture.filename;
+ return baseUrlStripped + texture.filename;
} else {
- return baseUrl.toString() + '/' + texture.filename;
+ return baseUrlStripped + '/' + texture.filename;
}
}
}
diff --git a/libraries/model-networking/src/model-networking/TextureCache.cpp b/libraries/model-networking/src/model-networking/TextureCache.cpp
index 04696cea1a..54b654c56b 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.cpp
+++ b/libraries/model-networking/src/model-networking/TextureCache.cpp
@@ -48,6 +48,8 @@
#include
#include
+#include
+
Q_LOGGING_CATEGORY(trace_resource_parse_image, "trace.resource.parse.image")
Q_LOGGING_CATEGORY(trace_resource_parse_image_raw, "trace.resource.parse.image.raw")
Q_LOGGING_CATEGORY(trace_resource_parse_image_ktx, "trace.resource.parse.image.ktx")
@@ -293,7 +295,6 @@ int networkTexturePointerMetaTypeId = qRegisterMetaType(url);
@@ -309,17 +310,25 @@ static bool isLocalUrl(const QUrl& url) {
NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type, const QByteArray& content, int maxNumPixels) :
Resource(url),
_type(type),
- _sourceIsKTX(url.path().endsWith(".ktx")),
_maxNumPixels(maxNumPixels)
{
_textureSource = std::make_shared(url, (int)type);
_lowestRequestedMipLevel = 0;
- _shouldFailOnRedirect = !_sourceIsKTX;
+ qDebug() << "Creating networktexture: " << url;
+ if (url.fileName().endsWith(TEXTURE_META_EXTENSION)) {
+ _currentlyLoadingResourceType = ResourceType::META;
+ } else if (url.fileName().endsWith(".ktx")) {
+ _currentlyLoadingResourceType = ResourceType::KTX;
+ } else {
+ _currentlyLoadingResourceType = ResourceType::ORIGINAL;
+ }
+
+ _shouldFailOnRedirect = _currentlyLoadingResourceType != ResourceType::KTX;
if (type == image::TextureUsage::CUBE_TEXTURE) {
setLoadPriority(this, SKYBOX_LOAD_PRIORITY);
- } else if (_sourceIsKTX) {
+ } else if (_currentlyLoadingResourceType == ResourceType::KTX) {
setLoadPriority(this, HIGH_MIPS_LOAD_PRIORITY);
}
@@ -330,7 +339,7 @@ NetworkTexture::NetworkTexture(const QUrl& url, image::TextureUsage::Type type,
// if we have content, load it after we have our self pointer
if (!content.isEmpty()) {
_startedLoading = true;
- QMetaObject::invokeMethod(this, "loadContent", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));
+ QMetaObject::invokeMethod(this, "downloadFinished", Qt::QueuedConnection, Q_ARG(const QByteArray&, content));
}
}
@@ -393,12 +402,13 @@ NetworkTexture::~NetworkTexture() {
const uint16_t NetworkTexture::NULL_MIP_LEVEL = std::numeric_limits::max();
void NetworkTexture::makeRequest() {
- if (!_sourceIsKTX) {
+ qDebug() << "In makeRequest for " << _activeUrl << (int)_currentlyLoadingResourceType;
+ if (_currentlyLoadingResourceType != ResourceType::KTX) {
Resource::makeRequest();
return;
}
- if (isLocalUrl(_url)) {
+ if (isLocalUrl(_activeUrl)) {
auto self = _self;
QtConcurrent::run(QThreadPool::globalInstance(), [self] {
auto resource = self.lock();
@@ -444,6 +454,7 @@ void NetworkTexture::makeRequest() {
_ktxHeaderRequest->send();
+ qDebug() << "Starting mip range request";
startMipRangeRequest(NULL_MIP_LEVEL, NULL_MIP_LEVEL);
} else if (_ktxResourceState == PENDING_MIP_REQUEST) {
if (_lowestKnownPopulatedMip > 0) {
@@ -466,12 +477,12 @@ void NetworkTexture::handleLocalRequestCompleted() {
}
void NetworkTexture::makeLocalRequest() {
- const QString scheme = _url.scheme();
+ const QString scheme = _activeUrl.scheme();
QString path;
if (scheme == URL_SCHEME_FILE) {
- path = PathUtils::expandToLocalDataAbsolutePath(_url).toLocalFile();
+ path = PathUtils::expandToLocalDataAbsolutePath(_activeUrl).toLocalFile();
} else {
- path = ":" + _url.path();
+ path = ":" + _activeUrl.path();
}
connect(this, &Resource::finished, this, &NetworkTexture::handleLocalRequestCompleted);
@@ -497,7 +508,7 @@ void NetworkTexture::makeLocalRequest() {
});
if (found == ktxDescriptor->keyValues.end() || found->_value.size() != gpu::SOURCE_HASH_BYTES) {
- hash = _url.toString().toLocal8Bit().toHex().toStdString();
+ hash = _activeUrl.toString().toLocal8Bit().toHex().toStdString();
} else {
// at this point the source hash is in binary 16-byte form
// and we need it in a hexadecimal string
@@ -536,11 +547,13 @@ void NetworkTexture::makeLocalRequest() {
}
bool NetworkTexture::handleFailedRequest(ResourceRequest::Result result) {
- if (!_sourceIsKTX && result == ResourceRequest::Result::RedirectFail) {
+ if (_currentlyLoadingResourceType != ResourceType::KTX
+ && result == ResourceRequest::Result::RedirectFail) {
+
auto newPath = _request->getRelativePathUrl();
if (newPath.fileName().endsWith(".ktx")) {
qDebug() << "Redirecting to" << newPath << "from" << _url;
- _sourceIsKTX = true;
+ _currentlyLoadingResourceType = ResourceType::KTX;
_activeUrl = newPath;
_shouldFailOnRedirect = false;
makeRequest();
@@ -581,6 +594,7 @@ void NetworkTexture::startMipRangeRequest(uint16_t low, uint16_t high) {
bool isHighMipRequest = low == NULL_MIP_LEVEL && high == NULL_MIP_LEVEL;
+ qDebug() << "Making ktx mip request to: " << _activeUrl;
_ktxMipRequest = DependencyManager::get()->createResourceRequest(this, _activeUrl);
if (!_ktxMipRequest) {
@@ -930,11 +944,79 @@ void NetworkTexture::handleFinishedInitialLoad() {
}
void NetworkTexture::downloadFinished(const QByteArray& data) {
- loadContent(data);
+ qDebug() << "Loading content: " << _activeUrl;
+ if (_currentlyLoadingResourceType == ResourceType::META) {
+ qDebug() << "Loading meta content: " << _activeUrl;
+ loadMetaContent(data);
+ } else if (_currentlyLoadingResourceType == ResourceType::ORIGINAL) {
+ loadTextureContent(data);
+ } else {
+ TextureCache::requestCompleted(_self);
+ Resource::handleFailedRequest(ResourceRequest::Error);
+ }
}
-void NetworkTexture::loadContent(const QByteArray& content) {
- if (_sourceIsKTX) {
+void NetworkTexture::loadMetaContent(const QByteArray& content) {
+ if (_currentlyLoadingResourceType != ResourceType::META) {
+ qWarning() << "Trying to load meta content when current resource type is not META";
+ assert(false);
+ return;
+ }
+
+ TextureMeta meta;
+ if (!TextureMeta::deserialize(content, &meta)) {
+ qWarning() << "Failed to read texture meta from " << _url;
+ return;
+ }
+
+
+ auto& backend = DependencyManager::get()->getGPUContext()->getBackend();
+ for (auto pair : meta.availableTextureTypes) {
+ gpu::Element elFormat;
+
+ if (gpu::Texture::getCompressedFormat(pair.first, elFormat)) {
+ if (backend->supportedTextureFormat(elFormat)) {
+ auto url = pair.second;
+ if (url.fileName().endsWith(TEXTURE_META_EXTENSION)) {
+ qWarning() << "Found a texture meta URL inside of the texture meta file at" << _activeUrl;
+ continue;
+ }
+
+ _currentlyLoadingResourceType = ResourceType::KTX;
+ _activeUrl = _activeUrl.resolved(url);
+ qDebug() << "Active url is now: " << _activeUrl;
+ auto textureCache = DependencyManager::get();
+ auto self = _self.lock();
+ if (!self) {
+ return;
+ }
+ QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection);
+ return;
+ }
+ }
+ }
+
+ if (!meta.original.isEmpty()) {
+ _currentlyLoadingResourceType = ResourceType::ORIGINAL;
+ _activeUrl = _activeUrl.resolved(meta.original);
+
+ auto textureCache = DependencyManager::get();
+ auto self = _self.lock();
+ if (!self) {
+ return;
+ }
+ QMetaObject::invokeMethod(this, "attemptRequest", Qt::QueuedConnection);
+ return;
+ }
+
+ qWarning() << "Failed to find supported texture type in " << _activeUrl;
+ //TextureCache::requestCompleted(_self);
+ Resource::handleFailedRequest(ResourceRequest::NotFound);
+}
+
+void NetworkTexture::loadTextureContent(const QByteArray& content) {
+ if (_currentlyLoadingResourceType != ResourceType::ORIGINAL) {
+ qWarning() << "Trying to load texture content when currentl resource type is not ORIGINAL";
assert(false);
return;
}
diff --git a/libraries/model-networking/src/model-networking/TextureCache.h b/libraries/model-networking/src/model-networking/TextureCache.h
index 3f46dc3074..d93ddb461e 100644
--- a/libraries/model-networking/src/model-networking/TextureCache.h
+++ b/libraries/model-networking/src/model-networking/TextureCache.h
@@ -24,7 +24,9 @@
#include
#include
#include
+#include
+#include
#include "KTXCache.h"
namespace gpu {
@@ -75,11 +77,13 @@ protected:
virtual bool isCacheable() const override { return _loaded; }
- virtual void downloadFinished(const QByteArray& data) override;
+ Q_INVOKABLE virtual void downloadFinished(const QByteArray& data) override;
bool handleFailedRequest(ResourceRequest::Result result) override;
- Q_INVOKABLE void loadContent(const QByteArray& content);
+ Q_INVOKABLE void loadMetaContent(const QByteArray& content);
+ Q_INVOKABLE void loadTextureContent(const QByteArray& content);
+
Q_INVOKABLE void setImage(gpu::TexturePointer texture, int originalWidth, int originalHeight);
Q_INVOKABLE void startRequestForNextMipLevel();
@@ -93,6 +97,14 @@ private:
image::TextureUsage::Type _type;
+ enum class ResourceType {
+ META,
+ ORIGINAL,
+ KTX
+ };
+
+ ResourceType _currentlyLoadingResourceType { ResourceType::META };
+
static const uint16_t NULL_MIP_LEVEL;
enum KTXResourceState {
PENDING_INITIAL_LOAD = 0,
@@ -103,7 +115,6 @@ private:
FAILED_TO_LOAD
};
- bool _sourceIsKTX { false };
KTXResourceState _ktxResourceState { PENDING_INITIAL_LOAD };
// The current mips that are currently being requested w/ _ktxMipRequest
@@ -236,6 +247,9 @@ public:
static const int DEFAULT_SPECTATOR_CAM_WIDTH { 2048 };
static const int DEFAULT_SPECTATOR_CAM_HEIGHT { 1024 };
+ void setGPUContext(const gpu::ContextPointer& context) { _gpuContext = context; }
+ gpu::ContextPointer getGPUContext() const { return _gpuContext; }
+
signals:
/**jsdoc
* @function TextureCache.spectatorCameraFramebufferReset
@@ -268,6 +282,8 @@ private:
static const std::string KTX_DIRNAME;
static const std::string KTX_EXT;
+ gpu::ContextPointer _gpuContext { nullptr };
+
std::shared_ptr _ktxCache { std::make_shared(KTX_DIRNAME, KTX_EXT) };
// Map from image hashes to texture weak pointers
diff --git a/libraries/networking/src/ResourceCache.cpp b/libraries/networking/src/ResourceCache.cpp
index 7ba7cca96d..4d3ba9da25 100644
--- a/libraries/networking/src/ResourceCache.cpp
+++ b/libraries/networking/src/ResourceCache.cpp
@@ -581,6 +581,7 @@ void Resource::refresh() {
ResourceCache::requestCompleted(_self);
}
+ _activeUrl = _url;
init();
ensureLoading();
emit onRefresh();
@@ -618,7 +619,7 @@ void Resource::init(bool resetLoaded) {
_loaded = false;
}
_attempts = 0;
- _activeUrl = _url;
+ qDebug() << "Initting resource: " << _url;
if (_url.isEmpty()) {
_startedLoading = _loaded = true;
@@ -671,6 +672,7 @@ void Resource::makeRequest() {
PROFILE_ASYNC_BEGIN(resource, "Resource:" + getType(), QString::number(_requestID), { { "url", _url.toString() }, { "activeURL", _activeUrl.toString() } });
+ qDebug() << "Making request to " << _activeUrl;
_request = DependencyManager::get()->createResourceRequest(this, _activeUrl);
if (!_request) {
@@ -724,7 +726,7 @@ void Resource::handleReplyFinished() {
auto result = _request->getResult();
if (result == ResourceRequest::Success) {
auto extraInfo = _url == _activeUrl ? "" : QString(", %1").arg(_activeUrl.toDisplayString());
- qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_url.toDisplayString(), extraInfo);
+ qCDebug(networking).noquote() << QString("Request finished for %1%2").arg(_activeUrl.toDisplayString(), extraInfo);
auto relativePathURL = _request->getRelativePathUrl();
if (!relativePathURL.isEmpty()) {
diff --git a/tools/oven/src/DomainBaker.cpp b/tools/oven/src/DomainBaker.cpp
index 3c6799db88..0a75c72f9a 100644
--- a/tools/oven/src/DomainBaker.cpp
+++ b/tools/oven/src/DomainBaker.cpp
@@ -464,7 +464,7 @@ bool DomainBaker::rewriteSkyboxURL(QJsonValueRef urlValue, TextureBaker* baker)
if (oldSkyboxURL.matches(baker->getTextureURL(), QUrl::RemoveQuery | QUrl::RemoveFragment)) {
// change the URL to point to the baked texture with its original query and fragment
- auto newSkyboxURL = _destinationPath.resolved(baker->getBakedTextureFileName());
+ auto newSkyboxURL = _destinationPath.resolved(baker->getMetaTextureFileName());
newSkyboxURL.setQuery(oldSkyboxURL.query());
newSkyboxURL.setFragment(oldSkyboxURL.fragment());
newSkyboxURL.setUserInfo(oldSkyboxURL.userInfo());
From 12d4cf12cf6790a5d9549df29bc3718648a32994 Mon Sep 17 00:00:00 2001
From: Ryan Huffman
Date: Tue, 1 May 2018 14:11:50 -0700
Subject: [PATCH 036/192] Bump model and texture baking versions in AssetServer
---
assignment-client/src/assets/AssetServer.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/assignment-client/src/assets/AssetServer.h b/assignment-client/src/assets/AssetServer.h
index fb88df0171..c4b1ff07e5 100644
--- a/assignment-client/src/assets/AssetServer.h
+++ b/assignment-client/src/assets/AssetServer.h
@@ -36,10 +36,11 @@ enum class BakedAssetType : int {
Undefined
};
-// ATTENTION! If you change the current version for an asset type, you will also
-// need to update the function currentBakeVersionForAssetType() inside of AssetServer.cpp.
+// ATTENTION! Do not remove baking versions, and do not reorder them. If you add
+// a new value, it will immediately become the "current" version.
enum class ModelBakeVersion : BakeVersion {
Initial = INITIAL_BAKE_VERSION,
+ MetaTextureJson,
COUNT
};
@@ -47,6 +48,7 @@ enum class ModelBakeVersion : BakeVersion {
// ATTENTION! See above.
enum class TextureBakeVersion : BakeVersion {
Initial = INITIAL_BAKE_VERSION,
+ MetaTextureJson,
COUNT
};
From b722c80b3f9f367fdc5758064c4110ecc8216221 Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Tue, 1 May 2018 14:25:33 -0700
Subject: [PATCH 037/192] WIP
---
tools/auto-tester/src/ui/AutoTester.cpp | 12 ++++++++----
tools/auto-tester/src/ui/AutoTester.h | 6 +++---
2 files changed, 11 insertions(+), 7 deletions(-)
diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp
index 3f7d2cba28..860868565a 100644
--- a/tools/auto-tester/src/ui/AutoTester.cpp
+++ b/tools/auto-tester/src/ui/AutoTester.cpp
@@ -10,8 +10,6 @@
//
#include "AutoTester.h"
-#include
-
AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
ui.setupUi(this);
ui.checkBoxInteractiveMode->setChecked(true);
@@ -91,11 +89,17 @@ void AutoTester::saveImage(int index) {
QPixmap pixmap;
pixmap.loadFromData(downloaders[index]->downloadedData());
+ int sdf = pixmap.width();
QImage image = pixmap.toImage();
image = image.convertToFormat(QImage::Format_ARGB32);
QString fullPathname = _directoryName + "/" + _filenames[index];
- image.save(fullPathname, 0, 100);
+ if (!image.save(fullPathname, 0, 100)) {
+ QMessageBox messageBox;
+ messageBox.information(0, "Test Aborted", "Failed to save image: " + _filenames[index]);
+ ui.progressBar->setVisible(false);
+ return;
+ }
++_numberOfImagesDownloaded;
@@ -109,4 +113,4 @@ void AutoTester::saveImage(int index) {
void AutoTester::about() {
QMessageBox messageBox;
messageBox.information(0, "About", QString("Built ") + __DATE__ + " : " + __TIME__);
-}
\ No newline at end of file
+}
diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/auto-tester/src/ui/AutoTester.h
index 03cb7fbcec..d911b6aaff 100644
--- a/tools/auto-tester/src/ui/AutoTester.h
+++ b/tools/auto-tester/src/ui/AutoTester.h
@@ -52,9 +52,9 @@ private:
// Used to enable passing a parameter to slots
QSignalMapper* signalMapper;
- int _numberOfImagesToDownload;
- int _numberOfImagesDownloaded;
- int _index;
+ int _numberOfImagesToDownload { 0 };
+ int _numberOfImagesDownloaded { 0 };
+ int _index { 0 };
};
#endif // hifi_AutoTester_h
\ No newline at end of file
From 0653f49efe1b2ff75c4f8ad7ef4045d89b2c02f4 Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Wed, 2 May 2018 09:31:24 +1200
Subject: [PATCH 038/192] Remove deprecated location.domainId from API
location.domainID is available, instead.
---
libraries/networking/src/AddressManager.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/libraries/networking/src/AddressManager.h b/libraries/networking/src/AddressManager.h
index 94eff46bda..95bf0c18a8 100644
--- a/libraries/networking/src/AddressManager.h
+++ b/libraries/networking/src/AddressManager.h
@@ -36,8 +36,6 @@ const QString GET_PLACE = "/api/v1/places/%1";
* @property {Uuid} domainID - A UUID uniquely identifying the domain you're visiting. Is {@link Uuid|Uuid.NULL} if you're not
* connected to the domain or are in a serverless domain.
* Read-only.
- * @property {Uuid} domainId - Synonym for domainId
. Read-only. Deprecated: This property
- * is deprecated and will soon be removed.
* @property {string} hostname - The name of the domain for your current metaverse address (e.g., "AvatarIsland"
,
* localhost
, or an IP address). Is blank if you're in a serverless domain.
* Read-only.
@@ -68,7 +66,6 @@ class AddressManager : public QObject, public Dependency {
Q_PROPERTY(QString pathname READ currentPath)
Q_PROPERTY(QString placename READ getPlaceName)
Q_PROPERTY(QString domainID READ getDomainID)
- Q_PROPERTY(QString domainId READ getDomainID)
public:
using PositionGetter = std::function;
using OrientationGetter = std::function;
From d1bb37874d0ca832fea0d3d78472ce14b3115ef8 Mon Sep 17 00:00:00 2001
From: Zach Fox
Date: Mon, 30 Apr 2018 17:13:44 -0700
Subject: [PATCH 039/192] Prevent crash in ImageProvider when tablet isn't yet
initialized
---
interface/src/commerce/Wallet.cpp | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 3e0e4adf18..42c8b9973d 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -615,9 +615,12 @@ void Wallet::updateImageProvider() {
securityImageProvider->setSecurityImage(_securityImage);
// inform tablet security image provider
- QQmlEngine* tabletEngine = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system")->getTabletSurface()->getSurfaceContext()->engine();
- securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
- securityImageProvider->setSecurityImage(_securityImage);
+ auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system");
+ if (tablet) {
+ QQmlEngine* tabletEngine = tablet->getTabletSurface()->getSurfaceContext()->engine();
+ securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
+ securityImageProvider->setSecurityImage(_securityImage);
+ }
}
void Wallet::chooseSecurityImage(const QString& filename) {
From d5f809c3c08730a3016c772a4b710fd8e7b10d85 Mon Sep 17 00:00:00 2001
From: Zach Fox
Date: Tue, 1 May 2018 09:54:23 -0700
Subject: [PATCH 040/192] Actually fix
---
interface/src/commerce/Wallet.cpp | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)
diff --git a/interface/src/commerce/Wallet.cpp b/interface/src/commerce/Wallet.cpp
index 42c8b9973d..35e6ca1c92 100644
--- a/interface/src/commerce/Wallet.cpp
+++ b/interface/src/commerce/Wallet.cpp
@@ -615,11 +615,14 @@ void Wallet::updateImageProvider() {
securityImageProvider->setSecurityImage(_securityImage);
// inform tablet security image provider
- auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system");
+ TabletProxy* tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system");
if (tablet) {
- QQmlEngine* tabletEngine = tablet->getTabletSurface()->getSurfaceContext()->engine();
- securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
- securityImageProvider->setSecurityImage(_securityImage);
+ OffscreenQmlSurface* tabletSurface = tablet->getTabletSurface();
+ if (tabletSurface) {
+ QQmlEngine* tabletEngine = tabletSurface->getSurfaceContext()->engine();
+ securityImageProvider = reinterpret_cast(tabletEngine->imageProvider(SecurityImageProvider::PROVIDER_NAME));
+ securityImageProvider->setSecurityImage(_securityImage);
+ }
}
}
From a3e4fa84292ab1607ed6cca3a10003ed75825595 Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Tue, 1 May 2018 15:27:29 -0700
Subject: [PATCH 041/192] Corrected download of images.
---
tools/auto-tester/src/Downloader.cpp | 9 +++++++++
tools/auto-tester/src/Test.cpp | 7 +++++--
tools/auto-tester/src/ui/AutoTester.cpp | 1 +
3 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/tools/auto-tester/src/Downloader.cpp b/tools/auto-tester/src/Downloader.cpp
index 030aa95a19..e66b498bd5 100644
--- a/tools/auto-tester/src/Downloader.cpp
+++ b/tools/auto-tester/src/Downloader.cpp
@@ -9,6 +9,8 @@
//
#include "Downloader.h"
+#include
+
Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
connect(
&_networkAccessManager, SIGNAL (finished(QNetworkReply*)),
@@ -20,6 +22,13 @@ Downloader::Downloader(QUrl imageUrl, QObject *parent) : QObject(parent) {
}
void Downloader::fileDownloaded(QNetworkReply* reply) {
+ QNetworkReply::NetworkError error = reply->error();
+ if (error != QNetworkReply::NetworkError::NoError) {
+ QMessageBox messageBox;
+ messageBox.information(0, "Test Aborted", "Failed to download image: " + reply->errorString());
+ return;
+ }
+
_downloadedData = reply->readAll();
//emit a signal
diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp
index 57806e80f6..d0a1606cd8 100644
--- a/tools/auto-tester/src/Test.cpp
+++ b/tools/auto-tester/src/Test.cpp
@@ -221,8 +221,11 @@ void Test::startTestsEvaluation() {
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
- QString imageURLString("https://github.com/" + githubUser + "/hifi_tests/blob/" + gitHubBranch + "/" +
- expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename + "?raw=true");
+ //https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/content/entity/zone/zoneOrientation/ExpectedImage_00001.png
+
+
+ QString imageURLString("https://raw.githubusercontent.com/" + githubUser + "/hifi_tests/" + gitHubBranch + "/" +
+ expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
expectedImagesURLs << imageURLString;
diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp
index 860868565a..17b1513467 100644
--- a/tools/auto-tester/src/ui/AutoTester.cpp
+++ b/tools/auto-tester/src/ui/AutoTester.cpp
@@ -86,6 +86,7 @@ void AutoTester::downloadImages(const QStringList& URLs, const QString& director
}
void AutoTester::saveImage(int index) {
+ QByteArray q = downloaders[index]->downloadedData();
QPixmap pixmap;
pixmap.loadFromData(downloaders[index]->downloadedData());
From 521ae36cbf9fb2f8dc197e150e2b28c1a78e1f40 Mon Sep 17 00:00:00 2001
From: Bradley Austin Davis
Date: Tue, 1 May 2018 15:29:22 -0700
Subject: [PATCH 042/192] Make resource swapchains immutable, fix for 14638
---
.../src/gpu/gl/GLBackendOutput.cpp | 4 ++--
.../src/gpu/gl/GLBackendPipeline.cpp | 9 ++++----
libraries/gpu/src/gpu/ResourceSwapChain.h | 19 +++++++----------
.../render-utils/src/AntialiasingEffect.cpp | 21 +++++++++----------
4 files changed, 24 insertions(+), 29 deletions(-)
diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp
index 2285b0e486..d1ab34da90 100644
--- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp
+++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendOutput.cpp
@@ -46,10 +46,10 @@ void GLBackend::do_setFramebuffer(const Batch& batch, size_t paramOffset) {
}
void GLBackend::do_setFramebufferSwapChain(const Batch& batch, size_t paramOffset) {
- auto swapChain = batch._swapChains.get(batch._params[paramOffset]._uint);
+ auto swapChain = std::static_pointer_cast(batch._swapChains.get(batch._params[paramOffset]._uint));
if (swapChain) {
auto index = batch._params[paramOffset + 1]._uint;
- FramebufferPointer framebuffer = static_cast(swapChain.get())->get(index);
+ const auto& framebuffer = swapChain->get(index);
setFramebuffer(framebuffer);
}
}
diff --git a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp
index 237b8bc1e9..d5cb331a4a 100644
--- a/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp
+++ b/libraries/gpu-gl-common/src/gpu/gl/GLBackendPipeline.cpp
@@ -1,4 +1,4 @@
-//
+//
// GLBackendPipeline.cpp
// libraries/gpu/src/gpu
//
@@ -263,7 +263,7 @@ void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, si
return;
}
- SwapChainPointer swapChain = batch._swapChains.get(batch._params[paramOffset + 0]._uint);
+ auto swapChain = std::static_pointer_cast(batch._swapChains.get(batch._params[paramOffset + 0]._uint));
if (!swapChain) {
releaseResourceTexture(slot);
@@ -271,9 +271,8 @@ void GLBackend::do_setResourceFramebufferSwapChainTexture(const Batch& batch, si
}
auto index = batch._params[paramOffset + 2]._uint;
auto renderBufferSlot = batch._params[paramOffset + 3]._uint;
- FramebufferPointer resourceFramebuffer = static_cast(swapChain.get())->get(index);
- TexturePointer resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot);
-
+ auto resourceFramebuffer = swapChain->get(index);
+ auto resourceTexture = resourceFramebuffer->getRenderBuffer(renderBufferSlot);
setResourceTexture(slot, resourceTexture);
}
diff --git a/libraries/gpu/src/gpu/ResourceSwapChain.h b/libraries/gpu/src/gpu/ResourceSwapChain.h
index 7b46b35521..84e8ec7c74 100644
--- a/libraries/gpu/src/gpu/ResourceSwapChain.h
+++ b/libraries/gpu/src/gpu/ResourceSwapChain.h
@@ -15,18 +15,18 @@ namespace gpu {
class SwapChain {
public:
- SwapChain(unsigned int size = 2U) : _size{ size } {}
+ SwapChain(size_t size = 2U) : _size{ size } {}
virtual ~SwapChain() {}
void advance() {
_frontIndex = (_frontIndex + 1) % _size;
}
- unsigned int getSize() const { return _size; }
+ size_t getSize() const { return _size; }
protected:
- unsigned int _size;
- unsigned int _frontIndex{ 0U };
+ const size_t _size;
+ size_t _frontIndex{ 0U };
};
typedef std::shared_ptr SwapChainPointer;
@@ -41,16 +41,13 @@ namespace gpu {
using Type = R;
using TypePointer = std::shared_ptr;
+ using TypeConstPointer = std::shared_ptr;
- ResourceSwapChain(unsigned int size = 2U) : SwapChain{ size } {}
-
- void reset() {
- for (auto& ptr : _resources) {
- ptr.reset();
+ ResourceSwapChain(const std::vector& v) : SwapChain{ std::min(v.size(), MAX_SIZE) } {
+ for (size_t i = 0; i < _size; ++i) {
+ _resources[i] = v[i];
}
}
-
- TypePointer& edit(unsigned int index) { return _resources[(index + _frontIndex) % _size]; }
const TypePointer& get(unsigned int index) const { return _resources[(index + _frontIndex) % _size]; }
private:
diff --git a/libraries/render-utils/src/AntialiasingEffect.cpp b/libraries/render-utils/src/AntialiasingEffect.cpp
index ba5036ad68..357782a321 100644
--- a/libraries/render-utils/src/AntialiasingEffect.cpp
+++ b/libraries/render-utils/src/AntialiasingEffect.cpp
@@ -188,7 +188,6 @@ const int AntialiasingPass_NextMapSlot = 4;
Antialiasing::Antialiasing() {
- _antialiasingBuffers = std::make_shared(2U);
}
Antialiasing::~Antialiasing() {
@@ -317,25 +316,25 @@ void Antialiasing::run(const render::RenderContextPointer& renderContext, const
int width = sourceBuffer->getWidth();
int height = sourceBuffer->getHeight();
- if (_antialiasingBuffers->get(0)) {
- if (_antialiasingBuffers->get(0)->getSize() != uvec2(width, height)) {// || (sourceBuffer && (_antialiasingBuffer->getRenderBuffer(1) != sourceBuffer->getRenderBuffer(0)))) {
- _antialiasingBuffers->edit(0).reset();
- _antialiasingBuffers->edit(1).reset();
- _antialiasingTextures[0].reset();
- _antialiasingTextures[1].reset();
- }
+ if (_antialiasingBuffers && _antialiasingBuffers->get(0) && _antialiasingBuffers->get(0)->getSize() != uvec2(width, height)) {
+ _antialiasingBuffers.reset();
+ _antialiasingTextures[0].reset();
+ _antialiasingTextures[1].reset();
}
- if (!_antialiasingBuffers->get(0)) {
+
+ if (!_antialiasingBuffers) {
+ std::vector antiAliasingBuffers;
// Link the antialiasing FBO to texture
for (int i = 0; i < 2; i++) {
- auto& antiAliasingBuffer = _antialiasingBuffers->edit(i);
- antiAliasingBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create("antialiasing"));
+ antiAliasingBuffers.emplace_back(gpu::Framebuffer::create("antialiasing"));
+ const auto& antiAliasingBuffer = antiAliasingBuffers.back();
auto format = gpu::Element::COLOR_SRGBA_32; // DependencyManager::get()->getLightingTexture()->getTexelFormat();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_LINEAR);
_antialiasingTextures[i] = gpu::Texture::createRenderBuffer(format, width, height, gpu::Texture::SINGLE_MIP, defaultSampler);
antiAliasingBuffer->setRenderBuffer(0, _antialiasingTextures[i]);
}
+ _antialiasingBuffers = std::make_shared(antiAliasingBuffers);
}
gpu::doInBatch("Antialiasing::run", args->_context, [&](gpu::Batch& batch) {
From fb929da2280ad3a45c32a41130a5ebeeb31e6160 Mon Sep 17 00:00:00 2001
From: Bradley Austin Davis
Date: Tue, 1 May 2018 16:54:26 -0700
Subject: [PATCH 043/192] Change type used for swap chain count
---
libraries/gpu/src/gpu/ResourceSwapChain.h | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/libraries/gpu/src/gpu/ResourceSwapChain.h b/libraries/gpu/src/gpu/ResourceSwapChain.h
index 84e8ec7c74..28c5ff2ed3 100644
--- a/libraries/gpu/src/gpu/ResourceSwapChain.h
+++ b/libraries/gpu/src/gpu/ResourceSwapChain.h
@@ -15,18 +15,18 @@ namespace gpu {
class SwapChain {
public:
- SwapChain(size_t size = 2U) : _size{ size } {}
+ SwapChain(uint8_t size = 2U) : _size{ size } {}
virtual ~SwapChain() {}
void advance() {
_frontIndex = (_frontIndex + 1) % _size;
}
- size_t getSize() const { return _size; }
+ uint8_t getSize() const { return _size; }
protected:
- const size_t _size;
- size_t _frontIndex{ 0U };
+ const uint8_t _size;
+ uint8_t _frontIndex{ 0U };
};
typedef std::shared_ptr SwapChainPointer;
@@ -43,7 +43,7 @@ namespace gpu {
using TypePointer = std::shared_ptr;
using TypeConstPointer = std::shared_ptr;
- ResourceSwapChain(const std::vector& v) : SwapChain{ std::min(v.size(), MAX_SIZE) } {
+ ResourceSwapChain(const std::vector& v) : SwapChain{ std::min((uint8_t)v.size(), MAX_SIZE) } {
for (size_t i = 0; i < _size; ++i) {
_resources[i] = v[i];
}
From 93afbcdc08f42f68e4c0f9417279bd012f69643f Mon Sep 17 00:00:00 2001
From: NissimHadar
Date: Tue, 1 May 2018 17:59:07 -0700
Subject: [PATCH 044/192] First success with running from command line.
---
tools/auto-tester/src/Test.cpp | 53 ++++++++++++++-----------
tools/auto-tester/src/Test.h | 29 +++++++-------
tools/auto-tester/src/main.cpp | 15 ++++++-
tools/auto-tester/src/ui/AutoTester.cpp | 12 ++++--
tools/auto-tester/src/ui/AutoTester.h | 5 +++
5 files changed, 70 insertions(+), 44 deletions(-)
diff --git a/tools/auto-tester/src/Test.cpp b/tools/auto-tester/src/Test.cpp
index d0a1606cd8..7139f0a43c 100644
--- a/tools/auto-tester/src/Test.cpp
+++ b/tools/auto-tester/src/Test.cpp
@@ -27,7 +27,7 @@ Test::Test() {
mismatchWindow.setModal(true);
}
-bool Test::createTestResultsFolderPath(QString directory) {
+bool Test::createTestResultsFolderPath(const QString& directory) {
QDateTime now = QDateTime::currentDateTime();
testResultsFolderPath = directory + "/" + TEST_RESULTS_FOLDER + "--" + now.toString(DATETIME_FORMAT);
QDir testResultsFolder(testResultsFolderPath);
@@ -125,7 +125,7 @@ bool Test::compareImageLists(bool isInteractiveMode, QProgressBar* progressBar)
return success;
}
-void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
+void Test::appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage) {
if (!QDir().exists(testResultsFolderPath)) {
messageBox.critical(0, "Internal error: " + QString(__FILE__) + ":" + QString::number(__LINE__), "Folder " + testResultsFolderPath + " not found");
exit(-1);
@@ -174,10 +174,16 @@ void Test::appendTestResultsToFile(QString testResultsFolderPath, TestFailure te
comparisonImage.save(failureFolderPath + "/" + "Difference Image.jpg");
}
-void Test::startTestsEvaluation() {
- // Get list of JPEG images in folder, sorted by name
- pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
- if (pathToTestResultsDirectory == "") {
+void Test::startTestsEvaluation(const QString& testFolder) {
+ QString pathToTestResultsDirectory;
+ if (testFolder.isNull()) {
+ // Get list of JPEG images in folder, sorted by name
+ pathToTestResultsDirectory = QFileDialog::getExistingDirectory(nullptr, "Please select folder containing the test images", ".", QFileDialog::ShowDirsOnly);
+ } else {
+ pathToTestResultsDirectory = testFolder;
+ }
+
+ if (pathToTestResultsDirectory == QString()) {
return;
}
@@ -221,9 +227,6 @@ void Test::startTestsEvaluation() {
QString expectedImageFilenameTail = currentFilename.left(currentFilename.length() - 4).right(NUM_DIGITS);
QString expectedImageStoredFilename = EXPECTED_IMAGE_PREFIX + expectedImageFilenameTail + ".png";
- //https://raw.githubusercontent.com/highfidelity/hifi_tests/master/tests/content/entity/zone/zoneOrientation/ExpectedImage_00001.png
-
-
QString imageURLString("https://raw.githubusercontent.com/" + githubUser + "/hifi_tests/" + gitHubBranch + "/" +
expectedImagePartialSourceDirectory + "/" + expectedImageStoredFilename);
@@ -240,19 +243,21 @@ void Test::startTestsEvaluation() {
autoTester->downloadImages(expectedImagesURLs, pathToTestResultsDirectory, expectedImagesFilenames);
}
-void Test::finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar) {
- bool success = compareImageLists(interactiveMode, progressBar);
+void Test::finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar) {
+ bool success = compareImageLists((!isRunningFromCommandline && interactiveMode), progressBar);
- if (success) {
- messageBox.information(0, "Success", "All images are as expected");
- } else {
- messageBox.information(0, "Failure", "One or more images are not as expected");
+ if (!isRunningFromCommandline) {
+ if (success) {
+ messageBox.information(0, "Success", "All images are as expected");
+ } else {
+ messageBox.information(0, "Failure", "One or more images are not as expected");
+ }
}
zipAndDeleteTestResultsFolder();
}
-bool Test::isAValidDirectory(QString pathname) {
+bool Test::isAValidDirectory(const QString& pathname) {
// Only process directories
QDir dir(pathname);
if (!dir.exists()) {
@@ -267,7 +272,7 @@ bool Test::isAValidDirectory(QString pathname) {
return true;
}
-QString Test::extractPathFromTestsDown(QString fullPath) {
+QString Test::extractPathFromTestsDown(const QString& fullPath) {
// `fullPath` includes the full path to the test. We need the portion below (and including) `tests`
QStringList pathParts = fullPath.split('/');
int i{ 0 };
@@ -348,7 +353,7 @@ void Test::createAllRecursiveScripts() {
messageBox.information(0, "Success", "Scripts have been created");
}
-void Test::createRecursiveScript(QString topLevelDirectory, bool interactiveMode) {
+void Test::createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode) {
const QString recursiveTestsFilename("testRecursive.js");
QFile allTestsFilename(topLevelDirectory + "/" + recursiveTestsFilename);
if (!allTestsFilename.open(QIODevice::WriteOnly | QIODevice::Text)) {
@@ -615,7 +620,7 @@ void Test::createAllMDFiles() {
messageBox.information(0, "Success", "MD files have been created");
}
-void Test::createMDFile(QString testDirectory) {
+void Test::createMDFile(const QString& testDirectory) {
// Verify folder contains test.js file
QString testFileName(testDirectory + "/" + TEST_FILENAME);
QFileInfo testFileInfo(testFileName);
@@ -790,7 +795,7 @@ void Test::createTestsOutline() {
messageBox.information(0, "Success", "Test outline file " + testsOutlineFilename + " has been created");
}
-void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename) {
+void Test::copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename) {
QFile::remove(destinationPNGFullFilename);
QImageReader reader;
@@ -803,7 +808,7 @@ void Test::copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFul
writer.write(image);
}
-QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory) {
+QStringList Test::createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory) {
imageDirectory = QDir(pathToImageDirectory);
QStringList nameFilters;
nameFilters << "*." + imageFormat;
@@ -816,7 +821,7 @@ QStringList Test::createListOfAll_imagesInDirectory(QString imageFormat, QString
// Filename (i.e. without extension) contains _tests_ (this is based on all test scripts being within the tests folder
// Last 5 characters in filename are digits
// Extension is jpg
-bool Test::isInSnapshotFilenameFormat(QString imageFormat, QString filename) {
+bool Test::isInSnapshotFilenameFormat(const QString& imageFormat, const QString& filename) {
QStringList filenameParts = filename.split(".");
bool filnameHasNoPeriods = (filenameParts.size() == 2);
@@ -833,7 +838,7 @@ bool Test::isInSnapshotFilenameFormat(QString imageFormat, QString filename) {
// For a file named "D_GitHub_hifi-tests_tests_content_entity_zone_create_0.jpg", the test directory is
// D:/GitHub/hifi-tests/tests/content/entity/zone/create
// This method assumes the filename is in the correct format
-QString Test::getExpectedImageDestinationDirectory(QString filename) {
+QString Test::getExpectedImageDestinationDirectory(const QString& filename) {
QString filenameWithoutExtension = filename.split(".")[0];
QStringList filenameParts = filenameWithoutExtension.split("_");
@@ -850,7 +855,7 @@ QString Test::getExpectedImageDestinationDirectory(QString filename) {
// is ...tests/content/entity/zone/create
// This is used to create the full URL
// This method assumes the filename is in the correct format
-QString Test::getExpectedImagePartialSourceDirectory(QString filename) {
+QString Test::getExpectedImagePartialSourceDirectory(const QString& filename) {
QString filenameWithoutExtension = filename.split(".")[0];
QStringList filenameParts = filenameWithoutExtension.split("_");
diff --git a/tools/auto-tester/src/Test.h b/tools/auto-tester/src/Test.h
index 7f5553f9e3..02cab53381 100644
--- a/tools/auto-tester/src/Test.h
+++ b/tools/auto-tester/src/Test.h
@@ -43,39 +43,39 @@ class Test {
public:
Test();
- void startTestsEvaluation();
- void finishTestsEvaluation(bool interactiveMode, QProgressBar* progressBar);
+ void startTestsEvaluation(const QString& testFolder = QString());
+ void finishTestsEvaluation(bool isRunningFromCommandline, bool interactiveMode, QProgressBar* progressBar);
void createRecursiveScript();
void createAllRecursiveScripts();
- void createRecursiveScript(QString topLevelDirectory, bool interactiveMode);
+ void createRecursiveScript(const QString& topLevelDirectory, bool interactiveMode);
void createTest();
void createMDFile();
void createAllMDFiles();
- void createMDFile(QString topLevelDirectory);
+ void createMDFile(const QString& topLevelDirectory);
void createTestsOutline();
bool compareImageLists(bool isInteractiveMode, QProgressBar* progressBar);
- QStringList createListOfAll_imagesInDirectory(QString imageFormat, QString pathToImageDirectory);
+ QStringList createListOfAll_imagesInDirectory(const QString& imageFormat, const QString& pathToImageDirectory);
- bool isInSnapshotFilenameFormat(QString imageFormat, QString filename);
+ bool isInSnapshotFilenameFormat(const QString& imageFormat, const QString& filename);
void importTest(QTextStream& textStream, const QString& testPathname);
- void appendTestResultsToFile(QString testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
+ void appendTestResultsToFile(const QString& testResultsFolderPath, TestFailure testFailure, QPixmap comparisonImage);
- bool createTestResultsFolderPath(QString directory);
+ bool createTestResultsFolderPath(const QString& directory);
void zipAndDeleteTestResultsFolder();
- bool isAValidDirectory(QString pathname);
- QString extractPathFromTestsDown(QString fullPath);
- QString getExpectedImageDestinationDirectory(QString filename);
- QString getExpectedImagePartialSourceDirectory(QString filename);
+ bool isAValidDirectory(const QString& pathname);
+ QString extractPathFromTestsDown(const QString& fullPath);
+ QString getExpectedImageDestinationDirectory(const QString& filename);
+ QString getExpectedImagePartialSourceDirectory(const QString& filename);
- void copyJPGtoPNG(QString sourceJPGFullFilename, QString destinationPNGFullFilename);
+ void copyJPGtoPNG(const QString& sourceJPGFullFilename, const QString& destinationPNGFullFilename);
private:
const QString TEST_FILENAME { "test.js" };
@@ -90,14 +90,13 @@ private:
ImageComparer imageComparer;
- QString testResultsFolderPath { "" };
+ QString testResultsFolderPath;
int index { 1 };
// Expected images are in the format ExpectedImage_dddd.jpg (d == decimal digit)
const int NUM_DIGITS { 5 };
const QString EXPECTED_IMAGE_PREFIX { "ExpectedImage_" };
- QString pathToTestResultsDirectory;
QStringList expectedImagesFilenames;
QStringList expectedImagesFullFilenames;
QStringList resultImagesFullFilenames;
diff --git a/tools/auto-tester/src/main.cpp b/tools/auto-tester/src/main.cpp
index cd0ce22b13..ffa7a0b237 100644
--- a/tools/auto-tester/src/main.cpp
+++ b/tools/auto-tester/src/main.cpp
@@ -13,10 +13,23 @@
AutoTester* autoTester;
int main(int argc, char *argv[]) {
+ // Only parameter is "--testFolder"
+ QString testFolder;
+ if (argc == 3) {
+ if (QString(argv[1]) == "--testFolder") {
+ testFolder = QString(argv[2]);
+ }
+ }
+
QApplication application(argc, argv);
autoTester = new AutoTester();
- autoTester->show();
+
+ if (!testFolder.isNull()) {
+ autoTester->runFromCommandLine(testFolder);
+ } else {
+ autoTester->show();
+ }
return application.exec();
}
diff --git a/tools/auto-tester/src/ui/AutoTester.cpp b/tools/auto-tester/src/ui/AutoTester.cpp
index 17b1513467..e0f92664ef 100644
--- a/tools/auto-tester/src/ui/AutoTester.cpp
+++ b/tools/auto-tester/src/ui/AutoTester.cpp
@@ -15,12 +15,17 @@ AutoTester::AutoTester(QWidget *parent) : QMainWindow(parent) {
ui.checkBoxInteractiveMode->setChecked(true);
ui.progressBar->setVisible(false);
- test = new Test();
-
signalMapper = new QSignalMapper();
connect(ui.actionClose, &QAction::triggered, this, &AutoTester::on_closeButton_clicked);
connect(ui.actionAbout, &QAction::triggered, this, &AutoTester::about);
+
+ test = new Test();
+}
+
+void AutoTester::runFromCommandLine(const QString& testFolder) {
+ isRunningFromCommandline = true;
+ test->startTestsEvaluation(testFolder);
}
void AutoTester::on_evaluateTestsButton_clicked() {
@@ -86,7 +91,6 @@ void AutoTester::downloadImages(const QStringList& URLs, const QString& director
}
void AutoTester::saveImage(int index) {
- QByteArray q = downloaders[index]->downloadedData();
QPixmap pixmap;
pixmap.loadFromData(downloaders[index]->downloadedData());
@@ -105,7 +109,7 @@ void AutoTester::saveImage(int index) {
++_numberOfImagesDownloaded;
if (_numberOfImagesDownloaded == _numberOfImagesToDownload) {
- test->finishTestsEvaluation(ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
+ test->finishTestsEvaluation(isRunningFromCommandline, ui.checkBoxInteractiveMode->isChecked(), ui.progressBar);
} else {
ui.progressBar->setValue(_numberOfImagesDownloaded);
}
diff --git a/tools/auto-tester/src/ui/AutoTester.h b/tools/auto-tester/src/ui/AutoTester.h
index d911b6aaff..fe37f2298d 100644
--- a/tools/auto-tester/src/ui/AutoTester.h
+++ b/tools/auto-tester/src/ui/AutoTester.h
@@ -22,6 +22,9 @@ class AutoTester : public QMainWindow {
public:
AutoTester(QWidget *parent = Q_NULLPTR);
+
+ void runFromCommandLine(const QString& testFolder);
+
void downloadImage(const QUrl& url);
void downloadImages(const QStringList& URLs, const QString& directoryName, const QStringList& filenames);
@@ -55,6 +58,8 @@ private:
int _numberOfImagesToDownload { 0 };
int _numberOfImagesDownloaded { 0 };
int _index { 0 };
+
+ bool isRunningFromCommandline { false };
};
#endif // hifi_AutoTester_h
\ No newline at end of file
From 49fad3d8685dc7a5d430b27574ee4b7525d0c7d8 Mon Sep 17 00:00:00 2001
From: Clement
Date: Fri, 13 Apr 2018 18:01:54 -0700
Subject: [PATCH 045/192] EntityServer traversal aware of all ViewFrustums
---
.../src/entities/EntityPriorityQueue.cpp | 23 +++-
.../src/entities/EntityPriorityQueue.h | 16 ++-
.../src/entities/EntityTreeSendThread.cpp | 100 +++++++--------
.../src/entities/EntityTreeSendThread.h | 3 +-
.../src/octree/OctreeHeadlessViewer.cpp | 9 +-
.../src/octree/OctreeSendThread.cpp | 6 +-
.../src/scripts/EntityScriptServer.cpp | 1 -
interface/src/Application.cpp | 10 +-
libraries/entities/src/DiffTraversal.cpp | 121 +++++++++++++-----
libraries/entities/src/DiffTraversal.h | 20 +--
libraries/octree/src/Octree.h | 1 -
libraries/octree/src/OctreeQuery.cpp | 107 +++++++---------
libraries/octree/src/OctreeQuery.h | 63 +++------
libraries/octree/src/OctreeQueryNode.cpp | 111 +++++++---------
libraries/octree/src/OctreeQueryNode.h | 9 +-
libraries/octree/src/OctreeUtils.cpp | 9 +-
libraries/octree/src/OctreeUtils.h | 3 +
libraries/shared/src/ViewFrustum.cpp | 4 +-
libraries/shared/src/ViewFrustum.h | 2 +-
19 files changed, 322 insertions(+), 296 deletions(-)
diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp
index 999a05f2e2..a38d537649 100644
--- a/assignment-client/src/entities/EntityPriorityQueue.cpp
+++ b/assignment-client/src/entities/EntityPriorityQueue.cpp
@@ -15,7 +15,7 @@ const float PrioritizedEntity::DO_NOT_SEND = -1.0e-6f;
const float PrioritizedEntity::FORCE_REMOVE = -1.0e-5f;
const float PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY = 1.0f;
-void ConicalView::set(const ViewFrustum& viewFrustum) {
+void ConicalViewFrustum::set(const ViewFrustum& viewFrustum) {
// The ConicalView has two parts: a central sphere (same as ViewFrustum) and a circular cone that bounds the frustum part.
// Why? Because approximate intersection tests are much faster to compute for a cone than for a frustum.
_position = viewFrustum.getPosition();
@@ -31,7 +31,7 @@ void ConicalView::set(const ViewFrustum& viewFrustum) {
_radius = viewFrustum.getCenterRadius();
}
-float ConicalView::computePriority(const AACube& cube) const {
+float ConicalViewFrustum::computePriority(const AACube& cube) const {
glm::vec3 p = cube.calcCenter() - _position; // position of bounding sphere in view-frame
float d = glm::length(p); // distance to center of bounding sphere
float r = 0.5f * cube.getScale(); // radius of bounding sphere
@@ -51,3 +51,22 @@ float ConicalView::computePriority(const AACube& cube) const {
}
return PrioritizedEntity::DO_NOT_SEND;
}
+
+
+void ConicalView::set(const DiffTraversal::View& view) {
+ auto size = view.viewFrustums.size();
+ _conicalViewFrustums.resize(size);
+ for (size_t i = 0; i < size; ++i) {
+ _conicalViewFrustums[i].set(view.viewFrustums[i]);
+ }
+}
+
+float ConicalView::computePriority(const AACube& cube) const {
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+ for (const auto& view : _conicalViewFrustums) {
+ priority = std::max(priority, view.computePriority(cube));
+ }
+
+ return priority;
+}
diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h
index e308d9b549..4068b4dc4b 100644
--- a/assignment-client/src/entities/EntityPriorityQueue.h
+++ b/assignment-client/src/entities/EntityPriorityQueue.h
@@ -15,16 +15,17 @@
#include
#include
+#include
#include
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
const float DEFAULT_VIEW_RADIUS = 10.0f;
// ConicalView is an approximation of a ViewFrustum for fast calculation of sort priority.
-class ConicalView {
+class ConicalViewFrustum {
public:
- ConicalView() {}
- ConicalView(const ViewFrustum& viewFrustum) { set(viewFrustum); }
+ ConicalViewFrustum() {}
+ ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
void set(const ViewFrustum& viewFrustum);
float computePriority(const AACube& cube) const;
private:
@@ -35,6 +36,15 @@ private:
float _radius { DEFAULT_VIEW_RADIUS };
};
+class ConicalView {
+public:
+ ConicalView() {}
+ void set(const DiffTraversal::View& view);
+ float computePriority(const AACube& cube) const;
+private:
+ std::vector _conicalViewFrustums;
+};
+
// PrioritizedEntity is a placeholder in a sorted queue.
class PrioritizedEntity {
public:
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index 4aa52922c0..d14d31bd09 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -103,11 +103,25 @@ void EntityTreeSendThread::preDistributionProcessing() {
void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, OctreeQueryNode* nodeData,
bool viewFrustumChanged, bool isFullScene) {
if (viewFrustumChanged || _traversal.finished()) {
- ViewFrustum viewFrustum;
- nodeData->copyCurrentViewFrustum(viewFrustum);
EntityTreeElementPointer root = std::dynamic_pointer_cast(_myServer->getOctree()->getRoot());
+
+
+ DiffTraversal::View newView;
+
+ ViewFrustum viewFrustum;
+ if (nodeData->hasMainViewFrustum()) {
+ nodeData->copyCurrentMainViewFrustum(viewFrustum);
+ newView.viewFrustums.push_back(viewFrustum);
+ }
+ if (nodeData->hasSecondaryViewFrustum()) {
+ nodeData->copyCurrentSecondaryViewFrustum(viewFrustum);
+ newView.viewFrustums.push_back(viewFrustum);
+ }
+
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
- startNewTraversal(viewFrustum, root, lodLevelOffset, nodeData->getUsesFrustum());
+ newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
+
+ startNewTraversal(newView, root);
// When the viewFrustum changed the sort order may be incorrect, so we re-sort
// and also use the opportunity to cull anything no longer in view
@@ -116,8 +130,6 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
_sendQueue.swap(prevSendQueue);
_entitiesInQueue.clear();
// Re-add elements from previous traversal if they still need to be sent
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
while (!prevSendQueue.empty()) {
EntityItemPointer entity = prevSendQueue.top().getEntity();
bool forceRemove = prevSendQueue.top().shouldForceRemove();
@@ -127,12 +139,10 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
- if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
+ if (_traversal.getCurrentView().intersects(cube)) {
float priority = _conicalView.computePriority(cube);
if (priority != PrioritizedEntity::DO_NOT_SEND) {
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
+ if (_traversal.getCurrentView().isBigEnough(cube)) {
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
}
@@ -215,10 +225,9 @@ bool EntityTreeSendThread::addDescendantsToExtraFlaggedEntities(const QUuid& fil
return hasNewChild || hasNewDescendants;
}
-void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTreeElementPointer root, int32_t lodLevelOffset,
- bool usesViewFrustum) {
+void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
- DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root, lodLevelOffset, usesViewFrustum);
+ DiffTraversal::Type type = _traversal.prepareNewTraversal(view, root);
// there are three types of traversal:
//
// (1) FirstTime = at login --> find everything in view
@@ -236,11 +245,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
case DiffTraversal::First:
// When we get to a First traversal, clear the _knownState
_knownState.clear();
- if (usesViewFrustum) {
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
- _traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([=](EntityItemPointer entity) {
+ if (view.usesViewFrustums()) {
+ _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@@ -248,14 +255,12 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
- if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
+ if (_traversal.getCurrentView().intersects(cube)) {
// Check the size of the entity, it's possible that a "too small to see" entity is included in a
// larger octree cell because of its position (for example if it crosses the boundary of a cell it
// pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
// before we consider including it.
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
+ if (_traversal.getCurrentView().isBigEnough(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
@@ -269,7 +274,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
});
} else {
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([this](EntityItemPointer entity) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@@ -281,13 +286,11 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
}
break;
case DiffTraversal::Repeat:
- if (usesViewFrustum) {
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
- _traversal.setScanCallback([=](DiffTraversal::VisibleElement& next) {
+ if (view.usesViewFrustums()) {
+ _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
- next.element->forEachEntity([=](EntityItemPointer entity) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@@ -297,11 +300,10 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
- if (next.intersection == ViewFrustum::INSIDE || _traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
+ if (next.intersection == ViewFrustum::INSIDE ||
+ _traversal.getCurrentView().intersects(cube)) {
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
+ if (_traversal.getCurrentView().isBigEnough(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
@@ -325,7 +327,7 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
- next.element->forEachEntity([this](EntityItemPointer entity) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@@ -343,13 +345,9 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
}
break;
case DiffTraversal::Differential:
- assert(usesViewFrustum);
- float lodScaleFactor = _traversal.getCurrentLODScaleFactor();
- glm::vec3 viewPosition = _traversal.getCurrentView().getPosition();
- float completedLODScaleFactor = _traversal.getCompletedLODScaleFactor();
- glm::vec3 completedViewPosition = _traversal.getCompletedView().getPosition();
- _traversal.setScanCallback([=] (DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([=](EntityItemPointer entity) {
+ assert(view.usesViewFrustums());
+ _traversal.setScanCallback([this] (DiffTraversal::VisibleElement& next) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
@@ -359,25 +357,19 @@ void EntityTreeSendThread::startNewTraversal(const ViewFrustum& view, EntityTree
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
- if (_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
+ if (_traversal.getCurrentView().intersects(cube)) {
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
- float distance = glm::distance(cube.calcCenter(), viewPosition) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ENTITY_ANGULAR_DIAMETER * lodScaleFactor) {
- if (!_traversal.getCompletedView().cubeIntersectsKeyhole(cube)) {
+ if (_traversal.getCurrentView().isBigEnough(cube)) {
+ if (!_traversal.getCompletedView().intersects(cube)) {
float priority = _conicalView.computePriority(cube);
_sendQueue.push(PrioritizedEntity(entity, priority));
_entitiesInQueue.insert(entity.get());
- } else {
+ } else if (!_traversal.getCompletedView().isBigEnough(cube)) {
// If this entity was skipped last time because it was too small, we still need to send it
- distance = glm::distance(cube.calcCenter(), completedViewPosition) + MIN_VISIBLE_DISTANCE;
- angularDiameter = cube.getScale() / distance;
- if (angularDiameter <= MIN_ENTITY_ANGULAR_DIAMETER * completedLODScaleFactor) {
- // this object was skipped in last completed traversal
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
+ // this object was skipped in last completed traversal
+ float priority = _conicalView.computePriority(cube);
+ _sendQueue.push(PrioritizedEntity(entity, priority));
+ _entitiesInQueue.insert(entity.get());
}
}
}
@@ -506,7 +498,7 @@ void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity)
AACube cube = entity->getQueryAACube(success);
if (success) {
// We can force a removal from _knownState if the current view is used and entity is out of view
- if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().cubeIntersectsKeyhole(cube)) {
+ if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().intersects(cube)) {
_sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
_entitiesInQueue.insert(entity.get());
}
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 1e2bd15429..5ea723c8b2 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -41,8 +41,7 @@ private:
bool addAncestorsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
bool addDescendantsToExtraFlaggedEntities(const QUuid& filteredEntityID, EntityItem& entityItem, EntityNodeData& nodeData);
- void startNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset,
- bool usesViewFrustum);
+ void startNewTraversal(const DiffTraversal::View& viewFrustum, EntityTreeElementPointer root);
bool traverseTreeAndBuildNextPacketPayload(EncodeBitstreamParams& params, const QJsonObject& jsonFilters) override;
void preDistributionProcessing() override;
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
index d3b20fb623..4f022ae838 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.cpp
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
@@ -23,14 +23,7 @@ void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType();
- _octreeQuery.setCameraPosition(_viewFrustum.getPosition());
- _octreeQuery.setCameraOrientation(_viewFrustum.getOrientation());
- _octreeQuery.setCameraFov(_viewFrustum.getFieldOfView());
- _octreeQuery.setCameraAspectRatio(_viewFrustum.getAspectRatio());
- _octreeQuery.setCameraNearClip(_viewFrustum.getNearClip());
- _octreeQuery.setCameraFarClip(_viewFrustum.getFarClip());
- _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
- _octreeQuery.setCameraCenterRadius(_viewFrustum.getCenterRadius());
+ _octreeQuery.setMainViewFrustum(_viewFrustum);
_octreeQuery.setOctreeSizeScale(_voxelSizeScale);
_octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
diff --git a/assignment-client/src/octree/OctreeSendThread.cpp b/assignment-client/src/octree/OctreeSendThread.cpp
index de49bd461c..40c052659d 100644
--- a/assignment-client/src/octree/OctreeSendThread.cpp
+++ b/assignment-client/src/octree/OctreeSendThread.cpp
@@ -330,8 +330,9 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
} else {
// we aren't forcing a full scene, check if something else suggests we should
isFullScene = nodeData->haveJSONParametersChanged() ||
- (nodeData->getUsesFrustum()
- && ((!viewFrustumChanged && nodeData->getViewFrustumJustStoppedChanging()) || nodeData->hasLodChanged()));
+ (nodeData->hasMainViewFrustum() &&
+ (nodeData->getViewFrustumJustStoppedChanging() ||
+ nodeData->hasLodChanged()));
}
if (nodeData->isPacketWaiting()) {
@@ -445,7 +446,6 @@ void OctreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, Octre
params.trackSend = [this](const QUuid& dataID, quint64 dataEdited) {
_myServer->trackSend(dataID, dataEdited, _nodeUuid);
};
- nodeData->copyCurrentViewFrustum(params.viewFrustum);
bool somethingToSend = true; // assume we have something
bool hadSomething = hasSomethingToSend(nodeData);
diff --git a/assignment-client/src/scripts/EntityScriptServer.cpp b/assignment-client/src/scripts/EntityScriptServer.cpp
index d242b393bf..eea8e8b470 100644
--- a/assignment-client/src/scripts/EntityScriptServer.cpp
+++ b/assignment-client/src/scripts/EntityScriptServer.cpp
@@ -294,7 +294,6 @@ void EntityScriptServer::run() {
queryJSONParameters[EntityJSONQueryProperties::FLAGS_PROPERTY] = queryFlags;
// setup the JSON parameters so that OctreeQuery does not use a frustum and uses our JSON filter
- _entityViewer.getOctreeQuery().setUsesFrustum(false);
_entityViewer.getOctreeQuery().setJSONParameters(queryJSONParameters);
entityScriptingInterface->setEntityTree(_entityViewer.getTree());
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index cd4562da54..d1d44aa706 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5791,14 +5791,8 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
ViewFrustum viewFrustum;
copyViewFrustum(viewFrustum);
- _octreeQuery.setCameraPosition(viewFrustum.getPosition());
- _octreeQuery.setCameraOrientation(viewFrustum.getOrientation());
- _octreeQuery.setCameraFov(viewFrustum.getFieldOfView());
- _octreeQuery.setCameraAspectRatio(viewFrustum.getAspectRatio());
- _octreeQuery.setCameraNearClip(viewFrustum.getNearClip());
- _octreeQuery.setCameraFarClip(viewFrustum.getFarClip());
- _octreeQuery.setCameraEyeOffsetPosition(glm::vec3());
- _octreeQuery.setCameraCenterRadius(viewFrustum.getCenterRadius());
+ _octreeQuery.setMainViewFrustum(viewFrustum);
+
auto lodManager = DependencyManager::get();
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp
index 764c420197..11f37728df 100644
--- a/libraries/entities/src/DiffTraversal.cpp
+++ b/libraries/entities/src/DiffTraversal.cpp
@@ -37,15 +37,14 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
++_nextIndex;
if (nextElement) {
- if (!view.usesViewFrustum) {
+ const auto& cube = nextElement->getAACube();
+ if (!view.usesViewFrustums()) {
// No LOD truncation if we aren't using the view frustum
next.element = nextElement;
return;
- } else if (view.viewFrustum.cubeIntersectsKeyhole(nextElement->getAACube())) {
+ } else if (view.intersects(cube)) {
// check for LOD truncation
- float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = nextElement->getAACube().getScale() / distance;
- if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
+ if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
next.element = nextElement;
return;
}
@@ -76,17 +75,16 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
++_nextIndex;
if (nextElement && nextElement->getLastChanged() > lastTime) {
- if (!view.usesViewFrustum) {
+ if (!view.usesViewFrustums()) {
// No LOD truncation if we aren't using the view frustum
next.element = nextElement;
next.intersection = ViewFrustum::INSIDE;
return;
} else {
// check for LOD truncation
- float distance = glm::distance(view.viewFrustum.getPosition(), nextElement->getAACube().calcCenter()) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = nextElement->getAACube().getScale() / distance;
- if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
- ViewFrustum::intersection intersection = view.viewFrustum.calculateCubeKeyholeIntersection(nextElement->getAACube());
+ const auto& cube = nextElement->getAACube();
+ if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
+ ViewFrustum::intersection intersection = view.calculateIntersection(cube);
if (intersection != ViewFrustum::OUTSIDE) {
next.element = nextElement;
next.intersection = intersection;
@@ -118,14 +116,13 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
++_nextIndex;
if (nextElement) {
- AACube cube = nextElement->getAACube();
// check for LOD truncation
- float distance = glm::distance(view.viewFrustum.getPosition(), cube.calcCenter()) + MIN_VISIBLE_DISTANCE;
- float angularDiameter = cube.getScale() / distance;
- if (angularDiameter > MIN_ELEMENT_ANGULAR_DIAMETER * view.lodScaleFactor) {
- if (view.viewFrustum.calculateCubeKeyholeIntersection(cube) != ViewFrustum::OUTSIDE) {
+ const auto& cube = nextElement->getAACube();
+ if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
+ ViewFrustum::intersection intersection = view.calculateIntersection(cube);
+ if (intersection != ViewFrustum::OUTSIDE) {
next.element = nextElement;
- next.intersection = ViewFrustum::OUTSIDE;
+ next.intersection = intersection;
return;
}
}
@@ -137,13 +134,83 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
next.intersection = ViewFrustum::OUTSIDE;
}
+bool DiffTraversal::View::isBigEnough(const AACube& cube, float minDiameter) const {
+ if (viewFrustums.empty()) {
+ // Everything is big enough when not using view frustums
+ return true;
+ }
+
+ for (const auto& viewFrustum : viewFrustums) {
+ if (isAngularSizeBigEnough(viewFrustum.getPosition(), cube, lodScaleFactor, minDiameter)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool DiffTraversal::View::intersects(const AACube& cube) const {
+ if (viewFrustums.empty()) {
+ // Everything intersects when not using view frustums
+ return true;
+ }
+
+ for (const auto& viewFrustum : viewFrustums) {
+ if (viewFrustum.cubeIntersectsKeyhole(cube)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+ViewFrustum::intersection DiffTraversal::View::calculateIntersection(const AACube& cube) const {
+ if (viewFrustums.empty()) {
+ // Everything is inside when not using view frustums
+ return ViewFrustum::INSIDE;
+ }
+
+ ViewFrustum::intersection intersection = ViewFrustum::OUTSIDE;
+
+ for (const auto& viewFrustum : viewFrustums) {
+ switch (viewFrustum.calculateCubeKeyholeIntersection(cube)) {
+ case ViewFrustum::INSIDE:
+ return ViewFrustum::INSIDE;
+ case ViewFrustum::INTERSECT:
+ intersection = ViewFrustum::INTERSECT;
+ break;
+ default:
+ // DO NOTHING
+ break;
+ }
+ }
+ return intersection;
+}
+
+bool DiffTraversal::View::usesViewFrustums() const {
+ return !viewFrustums.empty();
+}
+
+bool DiffTraversal::View::isVerySimilar(const View& view) const {
+ auto size = view.viewFrustums.size();
+
+ if (view.lodScaleFactor != lodScaleFactor ||
+ viewFrustums.size() != size) {
+ return false;
+ }
+
+ for (size_t i = 0; i < size; ++i) {
+ if (!viewFrustums[i].isVerySimilar(view.viewFrustums[i])) {
+ return false;
+ }
+ }
+ return true;
+}
+
DiffTraversal::DiffTraversal() {
const int32_t MIN_PATH_DEPTH = 16;
_path.reserve(MIN_PATH_DEPTH);
}
-DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root,
- int32_t lodLevelOffset, bool usesViewFrustum) {
+DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root) {
assert(root);
// there are three types of traversal:
//
@@ -155,33 +222,29 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr
//
// _getNextVisibleElementCallback = identifies elements that need to be traversed,
// updates VisibleElement ref argument with pointer-to-element and view-intersection
- // (INSIDE, INTERSECT, or OUTSIDE)
+ // (INSIDE, INTERSECtT, or OUTSIDE)
//
// external code should update the _scanElementCallback after calling prepareNewTraversal
//
- _currentView.usesViewFrustum = usesViewFrustum;
- float lodScaleFactor = powf(2.0f, lodLevelOffset);
Type type;
// If usesViewFrustum changes, treat it as a First traversal
- if (_completedView.startTime == 0 || _currentView.usesViewFrustum != _completedView.usesViewFrustum) {
+ if (_completedView.startTime == 0 || _currentView.usesViewFrustums() != _completedView.usesViewFrustums()) {
type = Type::First;
- _currentView.viewFrustum = viewFrustum;
- _currentView.lodScaleFactor = lodScaleFactor;
+ _currentView.viewFrustums = std::move(view.viewFrustums);
+ _currentView.lodScaleFactor = view.lodScaleFactor;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementFirstTime(next, _currentView);
};
- } else if (!_currentView.usesViewFrustum ||
- (_completedView.viewFrustum.isVerySimilar(viewFrustum) &&
- lodScaleFactor == _completedView.lodScaleFactor)) {
+ } else if (!_currentView.usesViewFrustums() || _completedView.isVerySimilar(view)) {
type = Type::Repeat;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementRepeat(next, _completedView, _completedView.startTime);
};
} else {
type = Type::Differential;
- _currentView.viewFrustum = viewFrustum;
- _currentView.lodScaleFactor = lodScaleFactor;
+ _currentView.viewFrustums = std::move(view.viewFrustums);
+ _currentView.lodScaleFactor = view.lodScaleFactor;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementDifferential(next, _currentView, _completedView);
};
diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h
index 69431d8db5..50fe74a75b 100644
--- a/libraries/entities/src/DiffTraversal.h
+++ b/libraries/entities/src/DiffTraversal.h
@@ -30,10 +30,15 @@ public:
// View is a struct with a ViewFrustum and LOD parameters
class View {
public:
- ViewFrustum viewFrustum;
+ bool isBigEnough(const AACube& cube, float minDiameter = MIN_ENTITY_ANGULAR_DIAMETER) const;
+ bool intersects(const AACube& cube) const;
+ bool usesViewFrustums() const;
+ bool isVerySimilar(const View& view) const;
+ ViewFrustum::intersection calculateIntersection(const AACube& cube) const;
+
+ std::vector viewFrustums;
uint64_t startTime { 0 };
float lodScaleFactor { 1.0f };
- bool usesViewFrustum { true };
};
// Waypoint is an bookmark in a "path" of waypoints during a traversal.
@@ -57,15 +62,12 @@ public:
DiffTraversal();
- Type prepareNewTraversal(const ViewFrustum& viewFrustum, EntityTreeElementPointer root, int32_t lodLevelOffset,
- bool usesViewFrustum);
+ Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
- const ViewFrustum& getCurrentView() const { return _currentView.viewFrustum; }
- const ViewFrustum& getCompletedView() const { return _completedView.viewFrustum; }
+ const View& getCurrentView() const { return _currentView; }
+ const View& getCompletedView() const { return _completedView; }
- bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustum; }
- float getCurrentLODScaleFactor() const { return _currentView.lodScaleFactor; }
- float getCompletedLODScaleFactor() const { return _completedView.lodScaleFactor; }
+ bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustums(); }
uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
bool finished() const { return _path.empty(); }
diff --git a/libraries/octree/src/Octree.h b/libraries/octree/src/Octree.h
index a2ad834e18..b827ed7cd0 100644
--- a/libraries/octree/src/Octree.h
+++ b/libraries/octree/src/Octree.h
@@ -59,7 +59,6 @@ const int LOW_RES_MOVING_ADJUST = 1;
class EncodeBitstreamParams {
public:
- ViewFrustum viewFrustum;
bool includeExistsBits;
NodeData* nodeData;
diff --git a/libraries/octree/src/OctreeQuery.cpp b/libraries/octree/src/OctreeQuery.cpp
index 18766dd7f6..18e907cb8c 100644
--- a/libraries/octree/src/OctreeQuery.cpp
+++ b/libraries/octree/src/OctreeQuery.cpp
@@ -9,6 +9,8 @@
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
//
+#include "OctreeQuery.h"
+
#include
#include
@@ -16,23 +18,11 @@
#include
#include
-#include "OctreeConstants.h"
-#include "OctreeQuery.h"
-
-const float DEFAULT_FOV = 45.0f; // degrees
-const float DEFAULT_ASPECT_RATIO = 1.0f;
-const float DEFAULT_NEAR_CLIP = 0.1f;
-const float DEFAULT_FAR_CLIP = 3.0f;
-
-OctreeQuery::OctreeQuery(bool randomizeConnectionID) :
- _cameraFov(DEFAULT_FOV),
- _cameraAspectRatio(DEFAULT_ASPECT_RATIO),
- _cameraNearClip(DEFAULT_NEAR_CLIP),
- _cameraFarClip(DEFAULT_FAR_CLIP),
- _cameraCenterRadius(DEFAULT_FAR_CLIP)
-{
- _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
+using QueryFlags = uint8_t;
+const QueryFlags QUERY_HAS_MAIN_FRUSTUM = 1U << 0;
+const QueryFlags QUERY_HAS_SECONDARY_FRUSTUM = 1U << 1;
+OctreeQuery::OctreeQuery(bool randomizeConnectionID) {
if (randomizeConnectionID) {
// randomize our initial octree query connection ID using random_device
// the connection ID is 16 bits so we take a generated 32 bit value from random device and chop off the top
@@ -47,26 +37,28 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
// pack the connection ID so the server can detect when we start a new connection
memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID));
destinationBuffer += sizeof(_connectionID);
-
- // back a boolean (cut to 1 byte) to designate if this query uses the sent view frustum
- memcpy(destinationBuffer, &_usesFrustum, sizeof(_usesFrustum));
- destinationBuffer += sizeof(_usesFrustum);
-
- if (_usesFrustum) {
- // TODO: DRY this up to a shared method
- // that can pack any type given the number of bytes
- // and return the number of bytes to push the pointer
-
- // camera details
- memcpy(destinationBuffer, &_cameraPosition, sizeof(_cameraPosition));
- destinationBuffer += sizeof(_cameraPosition);
- destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _cameraOrientation);
- destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _cameraFov);
- destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _cameraAspectRatio);
- destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraNearClip);
- destinationBuffer += packClipValueToTwoByte(destinationBuffer, _cameraFarClip);
- memcpy(destinationBuffer, &_cameraEyeOffsetPosition, sizeof(_cameraEyeOffsetPosition));
- destinationBuffer += sizeof(_cameraEyeOffsetPosition);
+
+ // flags for wether the frustums are present
+ QueryFlags frustumFlags = 0;
+ if (_hasMainFrustum) {
+ frustumFlags |= QUERY_HAS_MAIN_FRUSTUM;
+ }
+ if (_hasSecondaryFrustum) {
+ frustumFlags |= QUERY_HAS_SECONDARY_FRUSTUM;
+ }
+ memcpy(destinationBuffer, &frustumFlags, sizeof(frustumFlags));
+ destinationBuffer += sizeof(frustumFlags);
+
+ if (_hasMainFrustum) {
+ auto byteArray = _mainViewFrustum.toByteArray();
+ memcpy(destinationBuffer, byteArray.constData(), byteArray.size());
+ destinationBuffer += byteArray.size();
+ }
+
+ if (_hasSecondaryFrustum) {
+ auto byteArray = _secondaryViewFrustum.toByteArray();
+ memcpy(destinationBuffer, byteArray.constData(), byteArray.size());
+ destinationBuffer += byteArray.size();
}
// desired Max Octree PPS
@@ -80,9 +72,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
// desired boundaryLevelAdjust
memcpy(destinationBuffer, &_boundaryLevelAdjust, sizeof(_boundaryLevelAdjust));
destinationBuffer += sizeof(_boundaryLevelAdjust);
-
- memcpy(destinationBuffer, &_cameraCenterRadius, sizeof(_cameraCenterRadius));
- destinationBuffer += sizeof(_cameraCenterRadius);
// create a QByteArray that holds the binary representation of the JSON parameters
QByteArray binaryParametersDocument;
@@ -110,6 +99,7 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
int OctreeQuery::parseData(ReceivedMessage& message) {
const unsigned char* startPosition = reinterpret_cast(message.getRawMessage());
+ const unsigned char* endPosition = startPosition + message.getSize();
const unsigned char* sourceBuffer = startPosition;
// unpack the connection ID
@@ -133,20 +123,23 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
}
// check if this query uses a view frustum
- memcpy(&_usesFrustum, sourceBuffer, sizeof(_usesFrustum));
- sourceBuffer += sizeof(_usesFrustum);
-
- if (_usesFrustum) {
- // unpack camera details
- memcpy(&_cameraPosition, sourceBuffer, sizeof(_cameraPosition));
- sourceBuffer += sizeof(_cameraPosition);
- sourceBuffer += unpackOrientationQuatFromBytes(sourceBuffer, _cameraOrientation);
- sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*) sourceBuffer, &_cameraFov);
- sourceBuffer += unpackFloatRatioFromTwoByte(sourceBuffer,_cameraAspectRatio);
- sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraNearClip);
- sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer,_cameraFarClip);
- memcpy(&_cameraEyeOffsetPosition, sourceBuffer, sizeof(_cameraEyeOffsetPosition));
- sourceBuffer += sizeof(_cameraEyeOffsetPosition);
+ QueryFlags frustumFlags { 0 };
+ memcpy(&frustumFlags, sourceBuffer, sizeof(frustumFlags));
+ sourceBuffer += sizeof(frustumFlags);
+
+ _hasMainFrustum = frustumFlags & QUERY_HAS_MAIN_FRUSTUM;
+ _hasSecondaryFrustum = frustumFlags & QUERY_HAS_SECONDARY_FRUSTUM;
+
+ if (_hasMainFrustum) {
+ auto bytesLeft = endPosition - sourceBuffer;
+ auto byteArray = QByteArray::fromRawData(reinterpret_cast(sourceBuffer), bytesLeft);
+ sourceBuffer += _mainViewFrustum.fromByteArray(byteArray);
+ }
+
+ if (_hasSecondaryFrustum) {
+ auto bytesLeft = endPosition - sourceBuffer;
+ auto byteArray = QByteArray::fromRawData(reinterpret_cast(sourceBuffer), bytesLeft);
+ sourceBuffer += _secondaryViewFrustum.fromByteArray(byteArray);
}
// desired Max Octree PPS
@@ -161,9 +154,6 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
memcpy(&_boundaryLevelAdjust, sourceBuffer, sizeof(_boundaryLevelAdjust));
sourceBuffer += sizeof(_boundaryLevelAdjust);
- memcpy(&_cameraCenterRadius, sourceBuffer, sizeof(_cameraCenterRadius));
- sourceBuffer += sizeof(_cameraCenterRadius);
-
// check if we have a packed JSON filter
uint16_t binaryParametersBytes;
memcpy(&binaryParametersBytes, sourceBuffer, sizeof(binaryParametersBytes));
@@ -184,8 +174,3 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
return sourceBuffer - startPosition;
}
-
-glm::vec3 OctreeQuery::calculateCameraDirection() const {
- glm::vec3 direction = glm::vec3(_cameraOrientation * glm::vec4(IDENTITY_FORWARD, 0.0f));
- return direction;
-}
diff --git a/libraries/octree/src/OctreeQuery.h b/libraries/octree/src/OctreeQuery.h
index 21ce2e7fac..ef52e29f51 100644
--- a/libraries/octree/src/OctreeQuery.h
+++ b/libraries/octree/src/OctreeQuery.h
@@ -12,16 +12,14 @@
#ifndef hifi_OctreeQuery_h
#define hifi_OctreeQuery_h
-#include
-
-#include
-#include
-
#include
#include
#include
+#include
+
+#include "OctreeConstants.h"
class OctreeQuery : public NodeData {
Q_OBJECT
@@ -30,31 +28,22 @@ public:
OctreeQuery(bool randomizeConnectionID = false);
virtual ~OctreeQuery() {}
+ OctreeQuery(const OctreeQuery&) = delete;
+ OctreeQuery& operator=(const OctreeQuery&) = delete;
+
int getBroadcastData(unsigned char* destinationBuffer);
int parseData(ReceivedMessage& message) override;
- // getters for camera details
- const glm::vec3& getCameraPosition() const { return _cameraPosition; }
- const glm::quat& getCameraOrientation() const { return _cameraOrientation; }
- float getCameraFov() const { return _cameraFov; }
- float getCameraAspectRatio() const { return _cameraAspectRatio; }
- float getCameraNearClip() const { return _cameraNearClip; }
- float getCameraFarClip() const { return _cameraFarClip; }
- const glm::vec3& getCameraEyeOffsetPosition() const { return _cameraEyeOffsetPosition; }
- float getCameraCenterRadius() const { return _cameraCenterRadius; }
+ bool hasMainViewFrustum() const { return _hasMainFrustum; }
+ void setMainViewFrustum(const ViewFrustum& viewFrustum) { _hasMainFrustum = true; _mainViewFrustum = viewFrustum; }
+ void clearMainViewFrustum() { _hasMainFrustum = false; }
+ const ViewFrustum& getMainViewFrustum() const { return _mainViewFrustum; }
- glm::vec3 calculateCameraDirection() const;
+ bool hasSecondaryViewFrustum() const { return _hasSecondaryFrustum; }
+ void setSecondaryViewFrustum(const ViewFrustum& viewFrustum) { _hasSecondaryFrustum = true; _secondaryViewFrustum = viewFrustum; }
+ void clearSecondaryViewFrustum() { _hasSecondaryFrustum = false; }
+ const ViewFrustum& getSecondaryViewFrustum() const { return _secondaryViewFrustum; }
- // setters for camera details
- void setCameraPosition(const glm::vec3& position) { _cameraPosition = position; }
- void setCameraOrientation(const glm::quat& orientation) { _cameraOrientation = orientation; }
- void setCameraFov(float fov) { _cameraFov = fov; }
- void setCameraAspectRatio(float aspectRatio) { _cameraAspectRatio = aspectRatio; }
- void setCameraNearClip(float nearClip) { _cameraNearClip = nearClip; }
- void setCameraFarClip(float farClip) { _cameraFarClip = farClip; }
- void setCameraEyeOffsetPosition(const glm::vec3& eyeOffsetPosition) { _cameraEyeOffsetPosition = eyeOffsetPosition; }
- void setCameraCenterRadius(float radius) { _cameraCenterRadius = radius; }
-
// getters/setters for JSON filter
QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; }
void setJSONParameters(const QJsonObject& jsonParameters)
@@ -64,9 +53,6 @@ public:
int getMaxQueryPacketsPerSecond() const { return _maxQueryPPS; }
float getOctreeSizeScale() const { return _octreeElementSizeScale; }
int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
-
- bool getUsesFrustum() { return _usesFrustum; }
- void setUsesFrustum(bool usesFrustum) { _usesFrustum = usesFrustum; }
void incrementConnectionID() { ++_connectionID; }
@@ -81,33 +67,22 @@ public slots:
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
protected:
- // camera details for the avatar
- glm::vec3 _cameraPosition { glm::vec3(0.0f) };
- glm::quat _cameraOrientation { glm::quat() };
- float _cameraFov;
- float _cameraAspectRatio;
- float _cameraNearClip;
- float _cameraFarClip;
- float _cameraCenterRadius;
- glm::vec3 _cameraEyeOffsetPosition { glm::vec3(0.0f) };
+ bool _hasMainFrustum { false };
+ ViewFrustum _mainViewFrustum;
+ bool _hasSecondaryFrustum { false };
+ ViewFrustum _secondaryViewFrustum;
// octree server sending items
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
float _octreeElementSizeScale = DEFAULT_OCTREE_SIZE_SCALE; /// used for LOD calculations
int _boundaryLevelAdjust = 0; /// used for LOD calculations
-
- uint8_t _usesFrustum = true;
+
uint16_t _connectionID; // query connection ID, randomized to start, increments with each new connection to server
QJsonObject _jsonParameters;
QReadWriteLock _jsonParametersLock;
bool _hasReceivedFirstQuery { false };
-
-private:
- // privatize the copy constructor and assignment operator so they cannot be called
- OctreeQuery(const OctreeQuery&);
- OctreeQuery& operator= (const OctreeQuery&);
};
#endif // hifi_OctreeQuery_h
diff --git a/libraries/octree/src/OctreeQueryNode.cpp b/libraries/octree/src/OctreeQueryNode.cpp
index 16542b697e..d6e9343896 100644
--- a/libraries/octree/src/OctreeQueryNode.cpp
+++ b/libraries/octree/src/OctreeQueryNode.cpp
@@ -139,9 +139,14 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
}
}
-void OctreeQueryNode::copyCurrentViewFrustum(ViewFrustum& viewOut) const {
+void OctreeQueryNode::copyCurrentMainViewFrustum(ViewFrustum& viewOut) const {
QMutexLocker viewLocker(&_viewMutex);
- viewOut = _currentViewFrustum;
+ viewOut = _currentMainViewFrustum;
+}
+
+void OctreeQueryNode::copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const {
+ QMutexLocker viewLocker(&_viewMutex);
+ viewOut = _currentSecondaryViewFrustum;
}
bool OctreeQueryNode::updateCurrentViewFrustum() {
@@ -150,70 +155,50 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
return false;
}
- if (!_usesFrustum) {
+ if (!_hasMainFrustum && !_hasSecondaryFrustum) {
// this client does not use a view frustum so the view frustum for this query has not changed
return false;
- } else {
- bool currentViewFrustumChanged = false;
-
- ViewFrustum newestViewFrustum;
- // get position and orientation details from the camera
- newestViewFrustum.setPosition(getCameraPosition());
- newestViewFrustum.setOrientation(getCameraOrientation());
-
- newestViewFrustum.setCenterRadius(getCameraCenterRadius());
-
- // Also make sure it's got the correct lens details from the camera
- float originalFOV = getCameraFov();
- float wideFOV = originalFOV + VIEW_FRUSTUM_FOV_OVERSEND;
-
- if (0.0f != getCameraAspectRatio() &&
- 0.0f != getCameraNearClip() &&
- 0.0f != getCameraFarClip() &&
- getCameraNearClip() != getCameraFarClip()) {
- newestViewFrustum.setProjection(glm::perspective(
- glm::radians(wideFOV), // hack
- getCameraAspectRatio(),
- getCameraNearClip(),
- getCameraFarClip()));
- newestViewFrustum.calculate();
- }
-
-
- { // if there has been a change, then recalculate
- QMutexLocker viewLocker(&_viewMutex);
- if (!newestViewFrustum.isVerySimilar(_currentViewFrustum)) {
- _currentViewFrustum = newestViewFrustum;
- currentViewFrustumChanged = true;
- }
- }
-
- // Also check for LOD changes from the client
- if (_lodInitialized) {
- if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
- _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
- _lodChanged = true;
- }
- if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
- _lastClientOctreeSizeScale = getOctreeSizeScale();
- _lodChanged = true;
- }
- } else {
- _lodInitialized = true;
- _lastClientOctreeSizeScale = getOctreeSizeScale();
- _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
- _lodChanged = false;
- }
-
- // When we first detect that the view stopped changing, we record this.
- // but we don't change it back to false until we've completely sent this
- // scene.
- if (_viewFrustumChanging && !currentViewFrustumChanged) {
- _viewFrustumJustStoppedChanging = true;
- }
- _viewFrustumChanging = currentViewFrustumChanged;
- return currentViewFrustumChanged;
}
+
+ bool currentViewFrustumChanged = false;
+
+ { // if there has been a change, then recalculate
+ QMutexLocker viewLocker(&_viewMutex);
+ if (_hasMainFrustum && !_mainViewFrustum.isVerySimilar(_currentMainViewFrustum)) {
+ _currentMainViewFrustum = _mainViewFrustum;
+ currentViewFrustumChanged = true;
+ }
+ if (_hasSecondaryFrustum && !_secondaryViewFrustum.isVerySimilar(_currentSecondaryViewFrustum)) {
+ _currentSecondaryViewFrustum = _secondaryViewFrustum;
+ currentViewFrustumChanged = true;
+ }
+ }
+
+ // Also check for LOD changes from the client
+ if (_lodInitialized) {
+ if (_lastClientBoundaryLevelAdjust != getBoundaryLevelAdjust()) {
+ _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
+ _lodChanged = true;
+ }
+ if (_lastClientOctreeSizeScale != getOctreeSizeScale()) {
+ _lastClientOctreeSizeScale = getOctreeSizeScale();
+ _lodChanged = true;
+ }
+ } else {
+ _lodInitialized = true;
+ _lastClientOctreeSizeScale = getOctreeSizeScale();
+ _lastClientBoundaryLevelAdjust = getBoundaryLevelAdjust();
+ _lodChanged = false;
+ }
+
+ // When we first detect that the view stopped changing, we record this.
+ // but we don't change it back to false until we've completely sent this
+ // scene.
+ if (_viewFrustumChanging && !currentViewFrustumChanged) {
+ _viewFrustumJustStoppedChanging = true;
+ }
+ _viewFrustumChanging = currentViewFrustumChanged;
+ return currentViewFrustumChanged;
}
void OctreeQueryNode::setViewSent(bool viewSent) {
diff --git a/libraries/octree/src/OctreeQueryNode.h b/libraries/octree/src/OctreeQueryNode.h
index 640a7c7ddc..d8f05c4043 100644
--- a/libraries/octree/src/OctreeQueryNode.h
+++ b/libraries/octree/src/OctreeQueryNode.h
@@ -49,7 +49,8 @@ public:
OctreeElementExtraEncodeData extraEncodeData;
- void copyCurrentViewFrustum(ViewFrustum& viewOut) const;
+ void copyCurrentMainViewFrustum(ViewFrustum& viewOut) const;
+ void copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const;
// These are not classic setters because they are calculating and maintaining state
// which is set asynchronously through the network receive
@@ -87,9 +88,6 @@ public:
void setShouldForceFullScene(bool shouldForceFullScene) { _shouldForceFullScene = shouldForceFullScene; }
private:
- OctreeQueryNode(const OctreeQueryNode &);
- OctreeQueryNode& operator= (const OctreeQueryNode&);
-
bool _viewSent { false };
std::unique_ptr _octreePacket;
bool _octreePacketWaiting;
@@ -99,7 +97,8 @@ private:
quint64 _firstSuppressedPacket { usecTimestampNow() };
mutable QMutex _viewMutex { QMutex::Recursive };
- ViewFrustum _currentViewFrustum;
+ ViewFrustum _currentMainViewFrustum;
+ ViewFrustum _currentSecondaryViewFrustum;
bool _viewFrustumChanging { false };
bool _viewFrustumJustStoppedChanging { true };
diff --git a/libraries/octree/src/OctreeUtils.cpp b/libraries/octree/src/OctreeUtils.cpp
index 8980504431..8eaf22e198 100644
--- a/libraries/octree/src/OctreeUtils.cpp
+++ b/libraries/octree/src/OctreeUtils.cpp
@@ -16,6 +16,7 @@
#include
#include
+#include
float calculateRenderAccuracy(const glm::vec3& position,
const AABox& bounds,
@@ -73,4 +74,10 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust
// Smallest visible element is 1cm
const float smallestSize = 0.01f;
return (smallestSize * MAX_VISIBILITY_DISTANCE_FOR_UNIT_ELEMENT) / boundaryDistanceForRenderLevel(boundaryLevelAdjust, octreeSizeScale);
-}
\ No newline at end of file
+}
+
+bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter) {
+ float distance = glm::distance(cube.calcCenter(), position) + MIN_VISIBLE_DISTANCE;
+ float angularDiameter = cube.getScale() / distance;
+ return angularDiameter > minDiameter * lodScaleFactor;
+}
diff --git a/libraries/octree/src/OctreeUtils.h b/libraries/octree/src/OctreeUtils.h
index d5008376ea..58ab366d8d 100644
--- a/libraries/octree/src/OctreeUtils.h
+++ b/libraries/octree/src/OctreeUtils.h
@@ -15,6 +15,7 @@
#include "OctreeConstants.h"
class AABox;
+class AACube;
class QJsonDocument;
/// renderAccuracy represents a floating point "visibility" of an object based on it's view from the camera. At a simple
@@ -36,4 +37,6 @@ const float SQRT_THREE = 1.73205080f;
const float MIN_ENTITY_ANGULAR_DIAMETER = MIN_ELEMENT_ANGULAR_DIAMETER * SQRT_THREE;
const float MIN_VISIBLE_DISTANCE = 0.0001f; // helps avoid divide-by-zero check
+bool isAngularSizeBigEnough(glm::vec3 position, const AACube& cube, float lodScaleFactor, float minDiameter);
+
#endif // hifi_OctreeUtils_h
diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp
index 2a2eebc0a7..f65f5407fd 100644
--- a/libraries/shared/src/ViewFrustum.cpp
+++ b/libraries/shared/src/ViewFrustum.cpp
@@ -134,7 +134,7 @@ const char* ViewFrustum::debugPlaneName (int plane) const {
return "Unknown";
}
-void ViewFrustum::fromByteArray(const QByteArray& input) {
+int ViewFrustum::fromByteArray(const QByteArray& input) {
// From the wire!
glm::vec3 cameraPosition;
@@ -176,6 +176,8 @@ void ViewFrustum::fromByteArray(const QByteArray& input) {
calculate();
}
+
+ return sourceBuffer - startPosition;
}
diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h
index 981aabe70c..70fcb4cc32 100644
--- a/libraries/shared/src/ViewFrustum.h
+++ b/libraries/shared/src/ViewFrustum.h
@@ -147,7 +147,7 @@ public:
void invalidate(); // causes all reasonable intersection tests to fail
QByteArray toByteArray();
- void fromByteArray(const QByteArray& input);
+ int fromByteArray(const QByteArray& input);
private:
glm::mat4 _view;
From 98cf48694e8718887a2a6c1592d8e1171fe38ff2 Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 16 Apr 2018 15:27:43 -0700
Subject: [PATCH 046/192] Expose secondary camera to game logic
---
interface/src/Application.cpp | 32 ++++++++++++++++++++++++++++
interface/src/Application.h | 5 +++++
libraries/shared/src/ViewFrustum.cpp | 10 ++++-----
libraries/shared/src/ViewFrustum.h | 1 +
4 files changed, 43 insertions(+), 5 deletions(-)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index d1d44aa706..a38f4d022d 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5532,6 +5532,26 @@ void Application::update(float deltaTime) {
{
QMutexLocker viewLocker(&_viewMutex);
_myCamera.loadViewFrustum(_viewFrustum);
+
+
+ auto renderConfig = _renderEngine->getConfiguration();
+ assert(renderConfig);
+ auto secondaryCamera = dynamic_cast(renderConfig->getConfig("SecondaryCamera"));
+ assert(secondaryCamera);
+
+ if (secondaryCamera->isEnabled()) {
+ _secondaryViewFrustum.setPosition(secondaryCamera->position);
+ _secondaryViewFrustum.setOrientation(secondaryCamera->orientation);
+ _secondaryViewFrustum.setProjection(secondaryCamera->vFoV,
+ secondaryCamera->textureWidth / secondaryCamera->textureHeight,
+ secondaryCamera->nearClipPlaneDistance,
+ secondaryCamera->farClipPlaneDistance);
+ _secondaryViewFrustum.calculate();
+ _hasSecondaryViewFrustum = true;
+ } else {
+ _hasSecondaryViewFrustum = false;
+ }
+
}
quint64 now = usecTimestampNow();
@@ -5793,6 +5813,13 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
copyViewFrustum(viewFrustum);
_octreeQuery.setMainViewFrustum(viewFrustum);
+ if (hasSecondaryViewFrustum()) {
+ copySecondaryViewFrustum(viewFrustum);
+ _octreeQuery.setSecondaryViewFrustum(viewFrustum);
+ } else {
+ _octreeQuery.clearSecondaryViewFrustum();
+ }
+
auto lodManager = DependencyManager::get();
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
_octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust());
@@ -5876,6 +5903,11 @@ void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const {
viewOut = _displayViewFrustum;
}
+void Application::copySecondaryViewFrustum(ViewFrustum& viewOut) const {
+ QMutexLocker viewLocker(&_viewMutex);
+ viewOut = _secondaryViewFrustum;
+}
+
void Application::resetSensors(bool andReload) {
DependencyManager::get()->reset();
DependencyManager::get()->reset();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 6d611bc8e2..043a9b52ab 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -178,6 +178,9 @@ public:
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
+ void copySecondaryViewFrustum(ViewFrustum& viewOut) const;
+ bool hasSecondaryViewFrustum() const { return _hasSecondaryViewFrustum; }
+
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
QSharedPointer getEntities() const { return DependencyManager::get(); }
QUndoStack* getUndoStack() { return &_undoStack; }
@@ -554,6 +557,8 @@ private:
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
ViewFrustum _displayViewFrustum;
+ ViewFrustum _secondaryViewFrustum;
+ bool _hasSecondaryViewFrustum;
quint64 _lastQueriedTime;
OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
diff --git a/libraries/shared/src/ViewFrustum.cpp b/libraries/shared/src/ViewFrustum.cpp
index f65f5407fd..c2b92aac5f 100644
--- a/libraries/shared/src/ViewFrustum.cpp
+++ b/libraries/shared/src/ViewFrustum.cpp
@@ -75,6 +75,10 @@ void ViewFrustum::setProjection(const glm::mat4& projection) {
_width = _corners[TOP_RIGHT_NEAR].x - _corners[TOP_LEFT_NEAR].x;
}
+void ViewFrustum::setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip) {
+ setProjection(glm::perspective(glm::radians(cameraFov), cameraAspectRatio, cameraNearClip, cameraFarClip));
+}
+
// ViewFrustum::calculate()
//
// Description: this will calculate the view frustum bounds for a given position and direction
@@ -168,12 +172,8 @@ int ViewFrustum::fromByteArray(const QByteArray& input) {
0.0f != cameraNearClip &&
0.0f != cameraFarClip &&
cameraNearClip != cameraFarClip) {
- setProjection(glm::perspective(
- glm::radians(cameraFov),
- cameraAspectRatio,
- cameraNearClip,
- cameraFarClip));
+ setProjection(cameraFov, cameraAspectRatio, cameraNearClip, cameraFarClip);
calculate();
}
diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h
index 70fcb4cc32..ba8957bba3 100644
--- a/libraries/shared/src/ViewFrustum.h
+++ b/libraries/shared/src/ViewFrustum.h
@@ -48,6 +48,7 @@ public:
// setters for lens attributes
void setProjection(const glm::mat4 & projection);
+ void setProjection(float cameraFov, float cameraAspectRatio, float cameraNearClip, float cameraFarClip);
void setFocalLength(float focalLength) { _focalLength = focalLength; }
bool isPerspective() const;
From 7f67547faef1068a765f0aaba8b10a4a08060d12 Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 16 Apr 2018 17:45:11 -0700
Subject: [PATCH 047/192] Update HeadlessViewer to not always send a frustum
---
.../src/octree/OctreeHeadlessViewer.cpp | 13 +++------
.../src/octree/OctreeHeadlessViewer.h | 27 ++++++++-----------
2 files changed, 14 insertions(+), 26 deletions(-)
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
index 4f022ae838..6d91a134c2 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.cpp
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp
@@ -14,25 +14,18 @@
#include
#include
-
-OctreeHeadlessViewer::OctreeHeadlessViewer() {
- _viewFrustum.setProjection(glm::perspective(glm::radians(DEFAULT_FIELD_OF_VIEW_DEGREES), DEFAULT_ASPECT_RATIO, DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP));
-}
-
void OctreeHeadlessViewer::queryOctree() {
char serverType = getMyNodeType();
PacketType packetType = getMyQueryMessageType();
- _octreeQuery.setMainViewFrustum(_viewFrustum);
- _octreeQuery.setOctreeSizeScale(_voxelSizeScale);
- _octreeQuery.setBoundaryLevelAdjust(_boundaryLevelAdjust);
+ if (_hasViewFrustum) {
+ _octreeQuery.setMainViewFrustum(_viewFrustum);
+ }
auto nodeList = DependencyManager::get();
auto node = nodeList->soloNodeOfType(serverType);
if (node && node->getActiveSocket()) {
- _octreeQuery.setMaxQueryPacketsPerSecond(getMaxPacketsPerSecond());
-
auto queryPacket = NLPacket::create(packetType);
// encode the query data
diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.h b/assignment-client/src/octree/OctreeHeadlessViewer.h
index feb8211c39..dea91ce66f 100644
--- a/assignment-client/src/octree/OctreeHeadlessViewer.h
+++ b/assignment-client/src/octree/OctreeHeadlessViewer.h
@@ -20,9 +20,6 @@
class OctreeHeadlessViewer : public OctreeProcessor {
Q_OBJECT
public:
- OctreeHeadlessViewer();
- virtual ~OctreeHeadlessViewer() {};
-
OctreeQuery& getOctreeQuery() { return _octreeQuery; }
static int parseOctreeStats(QSharedPointer message, SharedNodePointer sourceNode);
@@ -32,34 +29,32 @@ public slots:
void queryOctree();
// setters for camera attributes
- void setPosition(const glm::vec3& position) { _viewFrustum.setPosition(position); }
- void setOrientation(const glm::quat& orientation) { _viewFrustum.setOrientation(orientation); }
- void setCenterRadius(float radius) { _viewFrustum.setCenterRadius(radius); }
- void setKeyholeRadius(float radius) { _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
+ void setPosition(const glm::vec3& position) { _hasViewFrustum = true; _viewFrustum.setPosition(position); }
+ void setOrientation(const glm::quat& orientation) { _hasViewFrustum = true; _viewFrustum.setOrientation(orientation); }
+ void setCenterRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); }
+ void setKeyholeRadius(float radius) { _hasViewFrustum = true; _viewFrustum.setCenterRadius(radius); } // TODO: remove this legacy support
// setters for LOD and PPS
- void setVoxelSizeScale(float sizeScale) { _voxelSizeScale = sizeScale; }
- void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
- void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _maxPacketsPerSecond = maxPacketsPerSecond; }
+ void setVoxelSizeScale(float sizeScale) { _octreeQuery.setOctreeSizeScale(sizeScale) ; }
+ void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _octreeQuery.setBoundaryLevelAdjust(boundaryLevelAdjust); }
+ void setMaxPacketsPerSecond(int maxPacketsPerSecond) { _octreeQuery.setMaxQueryPacketsPerSecond(maxPacketsPerSecond); }
// getters for camera attributes
const glm::vec3& getPosition() const { return _viewFrustum.getPosition(); }
const glm::quat& getOrientation() const { return _viewFrustum.getOrientation(); }
// getters for LOD and PPS
- float getVoxelSizeScale() const { return _voxelSizeScale; }
- int getBoundaryLevelAdjust() const { return _boundaryLevelAdjust; }
- int getMaxPacketsPerSecond() const { return _maxPacketsPerSecond; }
+ float getVoxelSizeScale() const { return _octreeQuery.getOctreeSizeScale(); }
+ int getBoundaryLevelAdjust() const { return _octreeQuery.getBoundaryLevelAdjust(); }
+ int getMaxPacketsPerSecond() const { return _octreeQuery.getMaxQueryPacketsPerSecond(); }
unsigned getOctreeElementsCount() const { return _tree->getOctreeElementsCount(); }
private:
OctreeQuery _octreeQuery;
+ bool _hasViewFrustum { false };
ViewFrustum _viewFrustum;
- float _voxelSizeScale { DEFAULT_OCTREE_SIZE_SCALE };
- int _boundaryLevelAdjust { 0 };
- int _maxPacketsPerSecond { DEFAULT_MAX_OCTREE_PPS };
};
#endif // hifi_OctreeHeadlessViewer_h
From 3862a02ceed3c1779761b039bd9f48dfb6ad11e6 Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 16 Apr 2018 18:30:51 -0700
Subject: [PATCH 048/192] DRY traversal scan callbacks
---
.../src/entities/EntityPriorityQueue.cpp | 4 +
.../src/entities/EntityTreeSendThread.cpp | 218 ++++++++----------
libraries/entities/src/DiffTraversal.cpp | 29 ++-
3 files changed, 115 insertions(+), 136 deletions(-)
diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp
index a38d537649..88dee58f9d 100644
--- a/assignment-client/src/entities/EntityPriorityQueue.cpp
+++ b/assignment-client/src/entities/EntityPriorityQueue.cpp
@@ -62,6 +62,10 @@ void ConicalView::set(const DiffTraversal::View& view) {
}
float ConicalView::computePriority(const AACube& cube) const {
+ if (_conicalViewFrustums.empty()) {
+ return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+ }
+
float priority = PrioritizedEntity::DO_NOT_SEND;
for (const auto& view : _conicalViewFrustums) {
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index d14d31bd09..a282c4ad4c 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -135,25 +135,27 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
bool forceRemove = prevSendQueue.top().shouldForceRemove();
prevSendQueue.pop();
if (entity) {
- if (!forceRemove) {
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+
+ if (forceRemove) {
+ priority = PrioritizedEntity::FORCE_REMOVE;
+ } else {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
- if (_traversal.getCurrentView().intersects(cube)) {
- float priority = _conicalView.computePriority(cube);
- if (priority != PrioritizedEntity::DO_NOT_SEND) {
- if (_traversal.getCurrentView().isBigEnough(cube)) {
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
+ const auto& view = _traversal.getCurrentView();
+ if (view.intersects(cube) && view.isBigEnough(cube)) {
+ priority = _conicalView.computePriority(cube);
}
} else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
}
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
+ }
+
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority, forceRemove);
_entitiesInQueue.insert(entity.get());
}
}
@@ -245,104 +247,79 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
case DiffTraversal::First:
// When we get to a First traversal, clear the _knownState
_knownState.clear();
- if (view.usesViewFrustums()) {
- _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+ _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+ next.element->forEachEntity([&](EntityItemPointer entity) {
+ // Bail early if we've already checked this entity this frame
+ if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
+ return;
+ }
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+
+ bool success = false;
+ AACube cube = entity->getQueryAACube(success);
+ if (success) {
+ const auto& view = _traversal.getCurrentView();
+ // Check the size of the entity, it's possible that a "too small to see" entity is included in a
+ // larger octree cell because of its position (for example if it crosses the boundary of a cell it
+ // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
+ // before we consider including it.
+ if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
+ view.isBigEnough(cube)) {
+ priority = _conicalView.computePriority(cube);
+ }
+ } else {
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+ }
+
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority);
+ _entitiesInQueue.insert(entity.get());
+ }
+ });
+ });
+ break;
+ case DiffTraversal::Repeat:
+ _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
+ uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
+ if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- if (_traversal.getCurrentView().intersects(cube)) {
- // Check the size of the entity, it's possible that a "too small to see" entity is included in a
- // larger octree cell because of its position (for example if it crosses the boundary of a cell it
- // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen
- // before we consider including it.
- if (_traversal.getCurrentView().isBigEnough(cube)) {
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+
+ auto knownTimestamp = _knownState.find(entity.get());
+ if (knownTimestamp == _knownState.end()) {
+ bool success = false;
+ AACube cube = entity->getQueryAACube(success);
+ if (success) {
+ const auto& view = _traversal.getCurrentView();
+ // See the DiffTraversal::First case for an explanation of the "entity is too small" check
+ if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
+ view.isBigEnough(cube)) {
+ priority = _conicalView.computePriority(cube);
}
+ } else {
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
}
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
+ } else if (entity->getLastEdited() > knownTimestamp->second) {
+ // it is known and it changed --> put it on the queue with any priority
+ // TODO: sort these correctly
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+ }
+
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority);
_entitiesInQueue.insert(entity.get());
}
});
- });
- } else {
- _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
- next.element->forEachEntity([&](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- });
- });
- }
- break;
- case DiffTraversal::Repeat:
- if (view.usesViewFrustums()) {
- _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
- uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
- if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
- next.element->forEachEntity([&](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- auto knownTimestamp = _knownState.find(entity.get());
- if (knownTimestamp == _knownState.end()) {
- bool success = false;
- AACube cube = entity->getQueryAACube(success);
- if (success) {
- if (next.intersection == ViewFrustum::INSIDE ||
- _traversal.getCurrentView().intersects(cube)) {
- // See the DiffTraversal::First case for an explanation of the "entity is too small" check
- if (_traversal.getCurrentView().isBigEnough(cube)) {
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
- } else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- } else if (entity->getLastEdited() > knownTimestamp->second
- || entity->getLastChangedOnServer() > knownTimestamp->second) {
- // it is known and it changed --> put it on the queue with any priority
- // TODO: sort these correctly
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- });
- }
- });
- } else {
- _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
- uint64_t startOfCompletedTraversal = _traversal.getStartOfCompletedTraversal();
- if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
- next.element->forEachEntity([&](EntityItemPointer entity) {
- // Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
- return;
- }
- auto knownTimestamp = _knownState.find(entity.get());
- if (knownTimestamp == _knownState.end()
- || entity->getLastEdited() > knownTimestamp->second
- || entity->getLastChangedOnServer() > knownTimestamp->second) {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
- }
- });
- }
- });
- }
+ }
+ });
break;
case DiffTraversal::Differential:
assert(view.usesViewFrustums());
@@ -352,36 +329,35 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
return;
}
+ float priority = PrioritizedEntity::DO_NOT_SEND;
+
+
auto knownTimestamp = _knownState.find(entity.get());
if (knownTimestamp == _knownState.end()) {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
- if (_traversal.getCurrentView().intersects(cube)) {
- // See the DiffTraversal::First case for an explanation of the "entity is too small" check
- if (_traversal.getCurrentView().isBigEnough(cube)) {
- if (!_traversal.getCompletedView().intersects(cube)) {
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- } else if (!_traversal.getCompletedView().isBigEnough(cube)) {
- // If this entity was skipped last time because it was too small, we still need to send it
- // this object was skipped in last completed traversal
- float priority = _conicalView.computePriority(cube);
- _sendQueue.push(PrioritizedEntity(entity, priority));
- _entitiesInQueue.insert(entity.get());
- }
- }
+ const auto& view = _traversal.getCurrentView();
+ // See the DiffTraversal::First case for an explanation of the "entity is too small" check
+ if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
+ view.isBigEnough(cube)) {
+ // If this entity wasn't in the last view or
+ // If this entity was skipped last time because it was too small, we still need to send it
+ priority = _conicalView.computePriority(cube);
}
} else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
- _entitiesInQueue.insert(entity.get());
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
}
} else if (entity->getLastEdited() > knownTimestamp->second
|| entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY));
+ priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
+ }
+
+
+ if (priority != PrioritizedEntity::DO_NOT_SEND) {
+ _sendQueue.emplace(entity, priority);
_entitiesInQueue.insert(entity.get());
}
});
@@ -499,11 +475,11 @@ void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity)
if (success) {
// We can force a removal from _knownState if the current view is used and entity is out of view
if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().intersects(cube)) {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true));
+ _sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
_entitiesInQueue.insert(entity.get());
}
} else {
- _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true));
+ _sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
_entitiesInQueue.insert(entity.get());
}
}
diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp
index 11f37728df..d5f2273fd5 100644
--- a/libraries/entities/src/DiffTraversal.cpp
+++ b/libraries/entities/src/DiffTraversal.cpp
@@ -13,7 +13,6 @@
#include
-
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
assert(element);
_weakElement = element;
@@ -140,12 +139,12 @@ bool DiffTraversal::View::isBigEnough(const AACube& cube, float minDiameter) con
return true;
}
- for (const auto& viewFrustum : viewFrustums) {
- if (isAngularSizeBigEnough(viewFrustum.getPosition(), cube, lodScaleFactor, minDiameter)) {
- return true;
- }
- }
- return false;
+ bool isBigEnough = std::any_of(std::begin(viewFrustums), std::end(viewFrustums),
+ [&](const ViewFrustum& viewFrustum) {
+ return isAngularSizeBigEnough(viewFrustum.getPosition(), cube, lodScaleFactor, minDiameter);
+ });
+
+ return isBigEnough;
}
bool DiffTraversal::View::intersects(const AACube& cube) const {
@@ -154,12 +153,12 @@ bool DiffTraversal::View::intersects(const AACube& cube) const {
return true;
}
- for (const auto& viewFrustum : viewFrustums) {
- if (viewFrustum.cubeIntersectsKeyhole(cube)) {
- return true;
- }
- }
- return false;
+ bool intersects = std::any_of(std::begin(viewFrustums), std::end(viewFrustums),
+ [&](const ViewFrustum& viewFrustum) {
+ return viewFrustum.cubeIntersectsKeyhole(cube);
+ });
+
+ return intersects;
}
ViewFrustum::intersection DiffTraversal::View::calculateIntersection(const AACube& cube) const {
@@ -231,7 +230,7 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
// If usesViewFrustum changes, treat it as a First traversal
if (_completedView.startTime == 0 || _currentView.usesViewFrustums() != _completedView.usesViewFrustums()) {
type = Type::First;
- _currentView.viewFrustums = std::move(view.viewFrustums);
+ _currentView.viewFrustums = view.viewFrustums;
_currentView.lodScaleFactor = view.lodScaleFactor;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementFirstTime(next, _currentView);
@@ -243,7 +242,7 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
};
} else {
type = Type::Differential;
- _currentView.viewFrustums = std::move(view.viewFrustums);
+ _currentView.viewFrustums = view.viewFrustums;
_currentView.lodScaleFactor = view.lodScaleFactor;
_getNextVisibleElementCallback = [this](DiffTraversal::VisibleElement& next) {
_path.back().getNextVisibleElementDifferential(next, _currentView, _completedView);
From 7a710093acc4abd982535985cd48ceed311380f0 Mon Sep 17 00:00:00 2001
From: Atlante45
Date: Tue, 17 Apr 2018 16:28:50 -0700
Subject: [PATCH 049/192] Fix build error
---
libraries/entities/src/EntityTree.h | 3 ---
1 file changed, 3 deletions(-)
diff --git a/libraries/entities/src/EntityTree.h b/libraries/entities/src/EntityTree.h
index 3289101967..d95dbf2990 100644
--- a/libraries/entities/src/EntityTree.h
+++ b/libraries/entities/src/EntityTree.h
@@ -27,9 +27,6 @@ using EntityTreePointer = std::shared_ptr;
#include "MovingEntitiesOperator.h"
class EntityEditFilters;
-class Model;
-using ModelPointer = std::shared_ptr;
-using ModelWeakPointer = std::weak_ptr;
class EntitySimulation;
From 69a7f2d4aa2852ac43f4f5454beea494553c2925 Mon Sep 17 00:00:00 2001
From: Clement
Date: Tue, 17 Apr 2018 17:41:35 -0700
Subject: [PATCH 050/192] Fix entity server crash
---
.../src/entities/EntityPriorityQueue.h | 47 ++++++++++++++++++-
.../src/entities/EntityTreeSendThread.cpp | 22 ++++-----
.../src/entities/EntityTreeSendThread.h | 1 -
3 files changed, 54 insertions(+), 16 deletions(-)
diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h
index 4068b4dc4b..730e08591e 100644
--- a/assignment-client/src/entities/EntityPriorityQueue.h
+++ b/assignment-client/src/entities/EntityPriorityQueue.h
@@ -13,6 +13,7 @@
#define hifi_EntityPriorityQueue_h
#include
+#include
#include
#include
@@ -71,6 +72,50 @@ private:
bool _forceRemove;
};
-using EntityPriorityQueue = std::priority_queue< PrioritizedEntity, std::vector, PrioritizedEntity::Compare >;
+class EntityPriorityQueue {
+public:
+ inline bool empty() const {
+ assert(_queue.empty() == _entities.empty());
+ return _queue.empty();
+ }
+
+ inline const PrioritizedEntity& top() const {
+ assert(!_queue.empty());
+ return _queue.top();
+ }
+
+ inline bool contains(const EntityItem* entity) const {
+ return _entities.find(entity) != std::end(_entities);
+ }
+
+ inline void emplace(const EntityItemPointer& entity, float priority, bool forceRemove = false) {
+ assert(entity && !contains(entity.get()));
+ _queue.emplace(entity, priority, forceRemove);
+ _entities.insert(entity.get());
+ assert(_queue.size() == _entities.size());
+ }
+
+ inline void pop() {
+ assert(!empty());
+ _entities.erase(_queue.top().getRawEntityPointer());
+ _queue.pop();
+ assert(_queue.size() == _entities.size());
+ }
+
+ inline void swap(EntityPriorityQueue& other) {
+ std::swap(_queue, other._queue);
+ std::swap(_entities, other._entities);
+ }
+
+private:
+ using PriorityQueue = std::priority_queue,
+ PrioritizedEntity::Compare>;
+
+ PriorityQueue _queue;
+ // Keep dictionary of al the entities in the queue for fast contain checks.
+ std::unordered_set _entities;
+
+};
#endif // hifi_EntityPriorityQueue_h
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index a282c4ad4c..ae7de01c3e 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -127,8 +127,9 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
// and also use the opportunity to cull anything no longer in view
if (viewFrustumChanged && !_sendQueue.empty()) {
EntityPriorityQueue prevSendQueue;
- _sendQueue.swap(prevSendQueue);
- _entitiesInQueue.clear();
+ std::swap(_sendQueue, prevSendQueue);
+ assert(_sendQueue.empty());
+
// Re-add elements from previous traversal if they still need to be sent
while (!prevSendQueue.empty()) {
EntityItemPointer entity = prevSendQueue.top().getEntity();
@@ -156,7 +157,6 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
if (priority != PrioritizedEntity::DO_NOT_SEND) {
_sendQueue.emplace(entity, priority, forceRemove);
- _entitiesInQueue.insert(entity.get());
}
}
}
@@ -250,7 +250,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
_traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
+ if (_sendQueue.contains(entity.get())) {
return;
}
float priority = PrioritizedEntity::DO_NOT_SEND;
@@ -275,7 +275,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
if (priority != PrioritizedEntity::DO_NOT_SEND) {
_sendQueue.emplace(entity, priority);
- _entitiesInQueue.insert(entity.get());
}
});
});
@@ -286,7 +285,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
if (next.element->getLastChangedContent() > startOfCompletedTraversal) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
+ if (_sendQueue.contains(entity.get())) {
return;
}
float priority = PrioritizedEntity::DO_NOT_SEND;
@@ -315,7 +314,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
if (priority != PrioritizedEntity::DO_NOT_SEND) {
_sendQueue.emplace(entity, priority);
- _entitiesInQueue.insert(entity.get());
}
});
}
@@ -326,7 +324,7 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
_traversal.setScanCallback([this] (DiffTraversal::VisibleElement& next) {
next.element->forEachEntity([&](EntityItemPointer entity) {
// Bail early if we've already checked this entity this frame
- if (_entitiesInQueue.find(entity.get()) != _entitiesInQueue.end()) {
+ if (_sendQueue.contains(entity.get())) {
return;
}
float priority = PrioritizedEntity::DO_NOT_SEND;
@@ -358,7 +356,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
if (priority != PrioritizedEntity::DO_NOT_SEND) {
_sendQueue.emplace(entity, priority);
- _entitiesInQueue.insert(entity.get());
}
});
});
@@ -447,11 +444,10 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
}
}
_sendQueue.pop();
- _entitiesInQueue.erase(entity.get());
}
nodeData->stats.encodeStopped();
if (_sendQueue.empty()) {
- assert(_entitiesInQueue.empty());
+ assert(_sendQueue.empty());
params.stopReason = EncodeBitstreamParams::FINISHED;
_extraEncodeData->entities.clear();
}
@@ -469,18 +465,16 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
if (entity) {
- if (_entitiesInQueue.find(entity.get()) == _entitiesInQueue.end() && _knownState.find(entity.get()) != _knownState.end()) {
+ if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
bool success = false;
AACube cube = entity->getQueryAACube(success);
if (success) {
// We can force a removal from _knownState if the current view is used and entity is out of view
if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().intersects(cube)) {
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
- _entitiesInQueue.insert(entity.get());
}
} else {
_sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
- _entitiesInQueue.insert(entity.get());
}
}
}
diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h
index 5ea723c8b2..9eea98b7fd 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.h
+++ b/assignment-client/src/entities/EntityTreeSendThread.h
@@ -50,7 +50,6 @@ private:
DiffTraversal _traversal;
EntityPriorityQueue _sendQueue;
- std::unordered_set _entitiesInQueue;
std::unordered_map _knownState;
ConicalView _conicalView; // cached optimized view for fast priority calculations
From fea49744ed3acbc673eb1b160f526b6c9a898718 Mon Sep 17 00:00:00 2001
From: Clement
Date: Tue, 17 Apr 2018 18:15:47 -0700
Subject: [PATCH 051/192] Add comment for future work
---
interface/src/Application.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a38f4d022d..ae84491097 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5534,6 +5534,9 @@ void Application::update(float deltaTime) {
_myCamera.loadViewFrustum(_viewFrustum);
+ // TODO: Fix this by modeling the way the secondary camera works on how the main camera works
+ // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
+ // camera should be.
auto renderConfig = _renderEngine->getConfiguration();
assert(renderConfig);
auto secondaryCamera = dynamic_cast(renderConfig->getConfig("SecondaryCamera"));
From 4c90763236dcd83d66d7476fed57dfd25e48895c Mon Sep 17 00:00:00 2001
From: Clement
Date: Wed, 18 Apr 2018 17:52:15 -0700
Subject: [PATCH 052/192] Correctly update secondary camera frustum
---
interface/src/Application.cpp | 92 +++++++++++++++++++++++++++--------
interface/src/Application.h | 2 +
2 files changed, 75 insertions(+), 19 deletions(-)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index ae84491097..bbdae9218b 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5179,6 +5179,78 @@ void Application::updateDialogs(float deltaTime) const {
}
}
+void Application::updateSecondaryCameraViewFrustum() {
+ // TODO: Fix this by modeling the way the secondary camera works on how the main camera works
+ // ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
+ // camera should be.
+
+ // Code based on SecondaryCameraJob
+ auto renderConfig = _renderEngine->getConfiguration();
+ assert(renderConfig);
+ auto camera = dynamic_cast(renderConfig->getConfig("SecondaryCamera"));
+ assert(camera);
+
+ if (!camera->isEnabled()) {
+ _hasSecondaryViewFrustum = false;
+ return;
+ }
+
+ if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
+ auto entityScriptingInterface = DependencyManager::get();
+ auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
+ glm::vec3 mirrorPropertiesPosition = entityProperties.getPosition();
+ glm::quat mirrorPropertiesRotation = entityProperties.getRotation();
+ glm::vec3 mirrorPropertiesDimensions = entityProperties.getDimensions();
+ glm::vec3 halfMirrorPropertiesDimensions = 0.5f * mirrorPropertiesDimensions;
+
+ // setup mirror from world as inverse of world from mirror transformation using inverted x and z for mirrored image
+ // TODO: we are assuming here that UP is world y-axis
+ glm::mat4 worldFromMirrorRotation = glm::mat4_cast(mirrorPropertiesRotation) * glm::scale(vec3(-1.0f, 1.0f, -1.0f));
+ glm::mat4 worldFromMirrorTranslation = glm::translate(mirrorPropertiesPosition);
+ glm::mat4 worldFromMirror = worldFromMirrorTranslation * worldFromMirrorRotation;
+ glm::mat4 mirrorFromWorld = glm::inverse(worldFromMirror);
+
+ // get mirror camera position by reflecting main camera position's z coordinate in mirror space
+ glm::vec3 mainCameraPositionWorld = getCamera().getPosition();
+ glm::vec3 mainCameraPositionMirror = vec3(mirrorFromWorld * vec4(mainCameraPositionWorld, 1.0f));
+ glm::vec3 mirrorCameraPositionMirror = vec3(mainCameraPositionMirror.x, mainCameraPositionMirror.y,
+ -mainCameraPositionMirror.z);
+ glm::vec3 mirrorCameraPositionWorld = vec3(worldFromMirror * vec4(mirrorCameraPositionMirror, 1.0f));
+
+ // set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation
+ glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation);
+ _secondaryViewFrustum.setPosition(mirrorCameraPositionWorld);
+ _secondaryViewFrustum.setOrientation(mirrorCameraOrientation);
+
+ // build frustum using mirror space translation of mirrored camera
+ float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f;
+ glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+ glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
+ glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance);
+ _secondaryViewFrustum.setProjection(frustum);
+ } else {
+ if (!camera->attachedEntityId.isNull()) {
+ auto entityScriptingInterface = DependencyManager::get();
+ auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
+ _secondaryViewFrustum.setPosition(entityProperties.getPosition());
+ _secondaryViewFrustum.setOrientation(entityProperties.getRotation());
+ } else {
+ _secondaryViewFrustum.setPosition(camera->position);
+ _secondaryViewFrustum.setOrientation(camera->orientation);
+ }
+
+ float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight;
+ _secondaryViewFrustum.setProjection(camera->vFoV,
+ aspectRatio,
+ camera->nearClipPlaneDistance,
+ camera->farClipPlaneDistance);
+ }
+ // Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera,
+ // which is not what we want here.
+ _secondaryViewFrustum.calculate();
+ _hasSecondaryViewFrustum = true;
+}
+
static bool domainLoadingInProgress = false;
void Application::update(float deltaTime) {
@@ -5533,28 +5605,10 @@ void Application::update(float deltaTime) {
QMutexLocker viewLocker(&_viewMutex);
_myCamera.loadViewFrustum(_viewFrustum);
-
// TODO: Fix this by modeling the way the secondary camera works on how the main camera works
// ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
// camera should be.
- auto renderConfig = _renderEngine->getConfiguration();
- assert(renderConfig);
- auto secondaryCamera = dynamic_cast(renderConfig->getConfig("SecondaryCamera"));
- assert(secondaryCamera);
-
- if (secondaryCamera->isEnabled()) {
- _secondaryViewFrustum.setPosition(secondaryCamera->position);
- _secondaryViewFrustum.setOrientation(secondaryCamera->orientation);
- _secondaryViewFrustum.setProjection(secondaryCamera->vFoV,
- secondaryCamera->textureWidth / secondaryCamera->textureHeight,
- secondaryCamera->nearClipPlaneDistance,
- secondaryCamera->farClipPlaneDistance);
- _secondaryViewFrustum.calculate();
- _hasSecondaryViewFrustum = true;
- } else {
- _hasSecondaryViewFrustum = false;
- }
-
+ updateSecondaryCameraViewFrustum();
}
quint64 now = usecTimestampNow();
diff --git a/interface/src/Application.h b/interface/src/Application.h
index 043a9b52ab..f31ca6547e 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -149,6 +149,8 @@ public:
void initializeRenderEngine();
void initializeUi();
+ void updateSecondaryCameraViewFrustum();
+
void updateCamera(RenderArgs& renderArgs, float deltaTime);
void paintGL();
void resizeGL();
From 0820ef3c9582a89388090e3e929b3cd7a5010d8a Mon Sep 17 00:00:00 2001
From: Atlante45
Date: Wed, 18 Apr 2018 20:19:27 -0700
Subject: [PATCH 053/192] Account for secondary view when deciding to query
---
interface/src/Application.cpp | 3 +++
interface/src/Application.h | 3 ++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index bbdae9218b..a5b859628f 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5622,6 +5622,8 @@ void Application::update(float deltaTime) {
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
+ viewIsDifferentEnough |= _hasSecondaryViewFrustum && !_lastQueriedSecondaryViewFrustum.isVerySimilar(_secondaryViewFrustum);
+
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
if (queryIsDue || viewIsDifferentEnough) {
_lastQueriedTime = now;
@@ -5630,6 +5632,7 @@ void Application::update(float deltaTime) {
}
sendAvatarViewFrustum();
_lastQueriedViewFrustum = _viewFrustum;
+ _lastQueriedSecondaryViewFrustum = _secondaryViewFrustum;
}
}
diff --git a/interface/src/Application.h b/interface/src/Application.h
index f31ca6547e..eed6ceeae5 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -557,9 +557,10 @@ private:
mutable QMutex _viewMutex { QMutex::Recursive };
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
- ViewFrustum _lastQueriedViewFrustum; /// last view frustum used to query octree servers (voxels)
+ ViewFrustum _lastQueriedViewFrustum; // last view frustum used to query octree servers
ViewFrustum _displayViewFrustum;
ViewFrustum _secondaryViewFrustum;
+ ViewFrustum _lastQueriedSecondaryViewFrustum; // last secondary view frustum used to query octree servers
bool _hasSecondaryViewFrustum;
quint64 _lastQueriedTime;
From 30d14dcd45834c7241b67b3be63430399417f386 Mon Sep 17 00:00:00 2001
From: Clement
Date: Thu, 19 Apr 2018 13:23:24 -0700
Subject: [PATCH 054/192] Push packet version
---
libraries/networking/src/udt/PacketHeaders.cpp | 2 +-
libraries/networking/src/udt/PacketHeaders.h | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index 98b0e1d892..b16f9c903e 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -34,7 +34,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
case PacketType::EntityPhysics:
return static_cast(EntityVersion::MaterialData);
case PacketType::EntityQuery:
- return static_cast(EntityQueryPacketVersion::RemovedJurisdictions);
+ return static_cast(EntityQueryPacketVersion::MultiFrustumQuery);
case PacketType::AvatarIdentity:
case PacketType::AvatarData:
case PacketType::BulkAvatarData:
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index e6b133c158..9b48e3a132 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -244,7 +244,8 @@ enum class EntityQueryPacketVersion: PacketVersion {
JSONFilter = 18,
JSONFilterWithFamilyTree = 19,
ConnectionIdentifier = 20,
- RemovedJurisdictions = 21
+ RemovedJurisdictions = 21,
+ MultiFrustumQuery = 22
};
enum class AssetServerPacketVersion: PacketVersion {
From d47ddbd6e4f5a9e09425fb9b8c51e215e195672d Mon Sep 17 00:00:00 2001
From: Clement
Date: Tue, 1 May 2018 18:02:15 -0700
Subject: [PATCH 055/192] CR
---
assignment-client/src/entities/EntityPriorityQueue.h | 5 +++--
.../src/entities/EntityTreeSendThread.cpp | 11 ++++-------
libraries/entities/src/DiffTraversal.cpp | 2 +-
3 files changed, 8 insertions(+), 10 deletions(-)
diff --git a/assignment-client/src/entities/EntityPriorityQueue.h b/assignment-client/src/entities/EntityPriorityQueue.h
index 730e08591e..9210ac549f 100644
--- a/assignment-client/src/entities/EntityPriorityQueue.h
+++ b/assignment-client/src/entities/EntityPriorityQueue.h
@@ -22,7 +22,7 @@
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
const float DEFAULT_VIEW_RADIUS = 10.0f;
-// ConicalView is an approximation of a ViewFrustum for fast calculation of sort priority.
+// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority.
class ConicalViewFrustum {
public:
ConicalViewFrustum() {}
@@ -37,6 +37,7 @@ private:
float _radius { DEFAULT_VIEW_RADIUS };
};
+// Simple wrapper around a set of conical view frustums
class ConicalView {
public:
ConicalView() {}
@@ -113,7 +114,7 @@ private:
PrioritizedEntity::Compare>;
PriorityQueue _queue;
- // Keep dictionary of al the entities in the queue for fast contain checks.
+ // Keep dictionary of all the entities in the queue for fast contain checks.
std::unordered_set _entities;
};
diff --git a/assignment-client/src/entities/EntityTreeSendThread.cpp b/assignment-client/src/entities/EntityTreeSendThread.cpp
index ae7de01c3e..2e57f2e00f 100644
--- a/assignment-client/src/entities/EntityTreeSendThread.cpp
+++ b/assignment-client/src/entities/EntityTreeSendThread.cpp
@@ -138,7 +138,6 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
if (entity) {
float priority = PrioritizedEntity::DO_NOT_SEND;
-
if (forceRemove) {
priority = PrioritizedEntity::FORCE_REMOVE;
} else {
@@ -154,7 +153,6 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
}
}
-
if (priority != PrioritizedEntity::DO_NOT_SEND) {
_sendQueue.emplace(entity, priority, forceRemove);
}
@@ -305,7 +303,8 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
} else {
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
}
- } else if (entity->getLastEdited() > knownTimestamp->second) {
+ } else if (entity->getLastEdited() > knownTimestamp->second ||
+ entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
@@ -339,15 +338,13 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
// See the DiffTraversal::First case for an explanation of the "entity is too small" check
if ((next.intersection == ViewFrustum::INSIDE || view.intersects(cube)) &&
view.isBigEnough(cube)) {
- // If this entity wasn't in the last view or
- // If this entity was skipped last time because it was too small, we still need to send it
priority = _conicalView.computePriority(cube);
}
} else {
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
}
- } else if (entity->getLastEdited() > knownTimestamp->second
- || entity->getLastChangedOnServer() > knownTimestamp->second) {
+ } else if (entity->getLastEdited() > knownTimestamp->second ||
+ entity->getLastChangedOnServer() > knownTimestamp->second) {
// it is known and it changed --> put it on the queue with any priority
// TODO: sort these correctly
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp
index d5f2273fd5..39328e11ad 100644
--- a/libraries/entities/src/DiffTraversal.cpp
+++ b/libraries/entities/src/DiffTraversal.cpp
@@ -221,7 +221,7 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
//
// _getNextVisibleElementCallback = identifies elements that need to be traversed,
// updates VisibleElement ref argument with pointer-to-element and view-intersection
- // (INSIDE, INTERSECtT, or OUTSIDE)
+ // (INSIDE, INTERSECT, or OUTSIDE)
//
// external code should update the _scanElementCallback after calling prepareNewTraversal
//
From 21213e81f4bda569b8cca9db2088ec3375fe1d71 Mon Sep 17 00:00:00 2001
From: Clement
Date: Wed, 18 Apr 2018 16:21:03 -0700
Subject: [PATCH 056/192] Multiview support for priority queue
---
.../src/avatars/AvatarMixerSlave.cpp | 2 +-
interface/src/Application.h | 6 ++--
interface/src/avatar/AvatarManager.cpp | 16 +++++++--
.../src/EntityTreeRenderer.cpp | 20 ++++++++---
.../src/EntityTreeRenderer.h | 3 +-
.../src/AbstractViewStateInterface.h | 4 +++
libraries/shared/src/PrioritySortUtil.h | 33 ++++++++++++-------
tests/render-perf/src/main.cpp | 10 ++++++
8 files changed, 71 insertions(+), 23 deletions(-)
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index 6f19b73cc5..da9b7934ad 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -223,7 +223,7 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
// prepare to sort
ViewFrustum cameraView = nodeData->getViewFrustum();
- PrioritySortUtil::PriorityQueue sortedAvatars(cameraView,
+ PrioritySortUtil::PriorityQueue sortedAvatars({cameraView},
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
diff --git a/interface/src/Application.h b/interface/src/Application.h
index eed6ceeae5..256f428d12 100644
--- a/interface/src/Application.h
+++ b/interface/src/Application.h
@@ -175,13 +175,13 @@ public:
Camera& getCamera() { return _myCamera; }
const Camera& getCamera() const { return _myCamera; }
// Represents the current view frustum of the avatar.
- void copyViewFrustum(ViewFrustum& viewOut) const;
+ void copyViewFrustum(ViewFrustum& viewOut) const override;
+ void copySecondaryViewFrustum(ViewFrustum& viewOut) const override;
+ bool hasSecondaryViewFrustum() const override { return _hasSecondaryViewFrustum; }
// Represents the view frustum of the current rendering pass,
// which might be different from the viewFrustum, i.e. shadowmap
// passes, mirror window passes, etc
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
- void copySecondaryViewFrustum(ViewFrustum& viewOut) const;
- bool hasSecondaryViewFrustum() const { return _hasSecondaryViewFrustum; }
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
QSharedPointer getEntities() const { return DependencyManager::get(); }
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index b71c060465..cd20fd9350 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -155,9 +155,19 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
AvatarSharedPointer _avatar;
};
- ViewFrustum cameraView;
- qApp->copyDisplayViewFrustum(cameraView);
- PrioritySortUtil::PriorityQueue sortedAvatars(cameraView,
+
+ std::vector views;
+
+ ViewFrustum view;
+ qApp->copyCurrentViewFrustum(view);
+ views.push_back(view);
+
+ if (qApp->hasSecondaryViewFrustum()) {
+ qApp->copySecondaryViewFrustum(view);
+ views.push_back(view);
+ }
+
+ PrioritySortUtil::PriorityQueue sortedAvatars(views,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index ba81922979..511fa33591 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -296,7 +296,8 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
}
}
-void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction) {
+void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const std::vector& views,
+ render::Transaction& transaction) {
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
PerformanceTimer pt("change");
std::unordered_set changedEntities;
@@ -357,7 +358,7 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
// prioritize and sort the renderables
uint64_t sortStart = usecTimestampNow();
- PrioritySortUtil::PriorityQueue sortedRenderables(view);
+ PrioritySortUtil::PriorityQueue sortedRenderables(views);
{
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
std::unordered_map::iterator itr = _renderablesToUpdate.begin();
@@ -415,9 +416,20 @@ void EntityTreeRenderer::update(bool simulate) {
if (scene) {
render::Transaction transaction;
addPendingEntities(scene, transaction);
+
+ std::vector views;
+
ViewFrustum view;
- _viewState->copyCurrentViewFrustum(view);
- updateChangedEntities(scene, view, transaction);
+ _viewState->copyViewFrustum(view);
+ views.push_back(view);
+
+ if (_viewState->hasSecondaryViewFrustum()) {
+ _viewState->copySecondaryViewFrustum(view);
+ views.push_back(view);
+ }
+
+
+ updateChangedEntities(scene, views, transaction);
scene->enqueueTransaction(transaction);
}
}
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index f5cedfdd01..7a7920d5b2 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -148,7 +148,8 @@ protected:
private:
void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction);
- void updateChangedEntities(const render::ScenePointer& scene, const ViewFrustum& view, render::Transaction& transaction);
+ void updateChangedEntities(const render::ScenePointer& scene, const std::vector& views,
+ render::Transaction& transaction);
EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); }
render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); }
diff --git a/libraries/render-utils/src/AbstractViewStateInterface.h b/libraries/render-utils/src/AbstractViewStateInterface.h
index 54fdc903ca..9d781b7d18 100644
--- a/libraries/render-utils/src/AbstractViewStateInterface.h
+++ b/libraries/render-utils/src/AbstractViewStateInterface.h
@@ -31,6 +31,10 @@ public:
/// copies the current view frustum for rendering the view state
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0;
+ virtual void copyViewFrustum(ViewFrustum& viewOut) const = 0;
+ virtual void copySecondaryViewFrustum(ViewFrustum& viewOut) const = 0;
+ virtual bool hasSecondaryViewFrustum() const = 0;
+
virtual QThread* getMainThread() = 0;
virtual PickRay computePickRay(float x, float y) const = 0;
diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h
index 279fa42ea4..7c0f30ec75 100644
--- a/libraries/shared/src/PrioritySortUtil.h
+++ b/libraries/shared/src/PrioritySortUtil.h
@@ -83,15 +83,15 @@ namespace PrioritySortUtil {
template
class PriorityQueue {
public:
+ using Views = std::vector;
+
PriorityQueue() = delete;
-
- PriorityQueue(const ViewFrustum& view) : _view(view) { }
-
- PriorityQueue(const ViewFrustum& view, float angularWeight, float centerWeight, float ageWeight)
- : _view(view), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
+ PriorityQueue(const Views& views) : _views(views) { }
+ PriorityQueue(const Views& views, float angularWeight, float centerWeight, float ageWeight)
+ : _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
{ }
- void setView(const ViewFrustum& view) { _view = view; }
+ void setViews(const Views& views) { _views = views; }
void setWeights(float angularWeight, float centerWeight, float ageWeight) {
_angularWeight = angularWeight;
@@ -109,7 +109,18 @@ namespace PrioritySortUtil {
bool empty() const { return _queue.empty(); }
private:
+
float computePriority(const T& thing) const {
+ float priority = std::numeric_limits::min();
+
+ for (const auto& view : _views) {
+ priority = std::max(priority, computePriority(view, thing));
+ }
+
+ return priority;
+ }
+
+ float computePriority(const ViewFrustum& view, const T& thing) const {
// priority = weighted linear combination of multiple values:
// (a) angular size
// (b) proximity to center of view
@@ -117,11 +128,11 @@ namespace PrioritySortUtil {
// where the relative "weights" are tuned to scale the contributing values into units of "priority".
glm::vec3 position = thing.getPosition();
- glm::vec3 offset = position - _view.getPosition();
+ glm::vec3 offset = position - view.getPosition();
float distance = glm::length(offset) + 0.001f; // add 1mm to avoid divide by zero
const float MIN_RADIUS = 0.1f; // WORKAROUND for zero size objects (we still want them to sort by distance)
float radius = glm::min(thing.getRadius(), MIN_RADIUS);
- float cosineAngle = (glm::dot(offset, _view.getDirection()) / distance);
+ float cosineAngle = (glm::dot(offset, view.getDirection()) / distance);
float age = (float)(usecTimestampNow() - thing.getTimestamp());
// we modulatate "age" drift rate by the cosineAngle term to make periphrial objects sort forward
@@ -134,8 +145,8 @@ namespace PrioritySortUtil {
+ _ageWeight * cosineAngleFactor * age;
// decrement priority of things outside keyhole
- if (distance - radius > _view.getCenterRadius()) {
- if (!_view.sphereIntersectsFrustum(position, radius)) {
+ if (distance - radius > view.getCenterRadius()) {
+ if (!view.sphereIntersectsFrustum(position, radius)) {
constexpr float OUT_OF_VIEW_PENALTY = -10.0f;
priority += OUT_OF_VIEW_PENALTY;
}
@@ -143,7 +154,7 @@ namespace PrioritySortUtil {
return priority;
}
- ViewFrustum _view;
+ Views _views;
std::priority_queue _queue;
float _angularWeight { DEFAULT_ANGULAR_COEF };
float _centerWeight { DEFAULT_CENTER_COEF };
diff --git a/tests/render-perf/src/main.cpp b/tests/render-perf/src/main.cpp
index 9249b3d957..8c17d7c5c2 100644
--- a/tests/render-perf/src/main.cpp
+++ b/tests/render-perf/src/main.cpp
@@ -441,6 +441,16 @@ protected:
viewOut = _viewFrustum;
}
+ void copyViewFrustum(ViewFrustum& viewOut) const override {
+ viewOut = _viewFrustum;
+ }
+
+ void copySecondaryViewFrustum(ViewFrustum& viewOut) const override {}
+
+ bool hasSecondaryViewFrustum() const override {
+ return false;
+ }
+
QThread* getMainThread() override {
return QThread::currentThread();
}
From 1b2b70b7691b65cfa96881cddd005a6f2d664147 Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 23 Apr 2018 15:47:05 -0700
Subject: [PATCH 057/192] Send both frustums to Avatar Mixer
---
assignment-client/src/avatars/AvatarMixer.cpp | 8 +++-----
.../src/avatars/AvatarMixerClientData.cpp | 16 ++++++++++++----
.../src/avatars/AvatarMixerClientData.h | 4 ++--
.../src/avatars/AvatarMixerSlave.cpp | 4 ++--
interface/src/Application.cpp | 4 ++++
5 files changed, 23 insertions(+), 13 deletions(-)
diff --git a/assignment-client/src/avatars/AvatarMixer.cpp b/assignment-client/src/avatars/AvatarMixer.cpp
index 29340f6474..6353a1664f 100644
--- a/assignment-client/src/avatars/AvatarMixer.cpp
+++ b/assignment-client/src/avatars/AvatarMixer.cpp
@@ -521,11 +521,9 @@ void AvatarMixer::handleViewFrustumPacket(QSharedPointer messag
auto start = usecTimestampNow();
getOrCreateClientData(senderNode);
- if (senderNode->getLinkedData()) {
- AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData());
- if (nodeData != nullptr) {
- nodeData->readViewFrustumPacket(message->getMessage());
- }
+ AvatarMixerClientData* nodeData = dynamic_cast(senderNode->getLinkedData());
+ if (nodeData) {
+ nodeData->readViewFrustumPacket(message->getMessage());
}
auto end = usecTimestampNow();
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 268aba62d6..8c159cf744 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -19,8 +19,6 @@
AvatarMixerClientData::AvatarMixerClientData(const QUuid& nodeID) :
NodeData(nodeID)
{
- _currentViewFrustum.invalidate();
-
// in case somebody calls getSessionUUID on the AvatarData instance, make sure it has the right ID
_avatar->setID(nodeID);
}
@@ -129,11 +127,21 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self,
}
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
- _currentViewFrustum.fromByteArray(message);
+ _currentViewFrustums.clear();
+
+ auto offset = 0;
+ while (offset < message.size()) {
+ ViewFrustum frustum;
+ offset += frustum.fromByteArray(message);
+ _currentViewFrustums.push_back(frustum);
+ }
}
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
- return _currentViewFrustum.boxIntersectsKeyhole(otherAvatarBox);
+ return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
+ [&](const ViewFrustum& viewFrustum) {
+ return viewFrustum.boxIntersectsKeyhole(otherAvatarBox);
+ });
}
void AvatarMixerClientData::loadJSONStats(QJsonObject& jsonObject) const {
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 6963f4df0d..4b06617175 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -110,7 +110,7 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
- ViewFrustum getViewFrustum() const { return _currentViewFrustum; }
+ const std::vector& getViewFrustums() const { return _currentViewFrustums; }
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
@@ -150,7 +150,7 @@ private:
SimpleMovingAverage _avgOtherAvatarDataRate;
std::unordered_set _radiusIgnoredOthers;
- ViewFrustum _currentViewFrustum;
+ std::vector _currentViewFrustums;
int _recentOtherAvatarsInView { 0 };
int _recentOtherAvatarsOutOfView { 0 };
diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp
index da9b7934ad..30d94ed772 100644
--- a/assignment-client/src/avatars/AvatarMixerSlave.cpp
+++ b/assignment-client/src/avatars/AvatarMixerSlave.cpp
@@ -222,8 +222,8 @@ void AvatarMixerSlave::broadcastAvatarDataToAgent(const SharedNodePointer& node)
};
// prepare to sort
- ViewFrustum cameraView = nodeData->getViewFrustum();
- PrioritySortUtil::PriorityQueue sortedAvatars({cameraView},
+ const auto& cameraViews = nodeData->getViewFrustums();
+ PrioritySortUtil::PriorityQueue sortedAvatars(cameraViews,
AvatarData::_avatarSortCoefficientSize,
AvatarData::_avatarSortCoefficientCenter,
AvatarData::_avatarSortCoefficientAge);
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index a5b859628f..33c334b493 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5806,6 +5806,10 @@ void Application::update(float deltaTime) {
void Application::sendAvatarViewFrustum() {
QByteArray viewFrustumByteArray = _viewFrustum.toByteArray();
+ if (hasSecondaryViewFrustum()) {
+ viewFrustumByteArray += _viewFrustum.toByteArray();
+ }
+
auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
avatarPacket->write(viewFrustumByteArray);
From 538f24162f3ecc2892ca361a0339eced1087113f Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 23 Apr 2018 15:57:21 -0700
Subject: [PATCH 058/192] Define ViewFrustums type alias
---
assignment-client/src/avatars/AvatarMixerClientData.h | 4 ++--
interface/src/avatar/AvatarManager.cpp | 2 +-
libraries/entities-renderer/src/EntityTreeRenderer.cpp | 4 ++--
libraries/entities-renderer/src/EntityTreeRenderer.h | 2 +-
libraries/entities/src/DiffTraversal.h | 2 +-
libraries/shared/src/PrioritySortUtil.h | 10 ++++------
libraries/shared/src/ViewFrustum.h | 1 +
7 files changed, 12 insertions(+), 13 deletions(-)
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 4b06617175..13415d6a66 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -110,7 +110,7 @@ public:
bool getRequestsDomainListData() { return _requestsDomainListData; }
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
- const std::vector& getViewFrustums() const { return _currentViewFrustums; }
+ const ViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
@@ -150,7 +150,7 @@ private:
SimpleMovingAverage _avgOtherAvatarDataRate;
std::unordered_set _radiusIgnoredOthers;
- std::vector _currentViewFrustums;
+ ViewFrustums _currentViewFrustums;
int _recentOtherAvatarsInView { 0 };
int _recentOtherAvatarsOutOfView { 0 };
diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp
index cd20fd9350..087e23a933 100644
--- a/interface/src/avatar/AvatarManager.cpp
+++ b/interface/src/avatar/AvatarManager.cpp
@@ -156,7 +156,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
};
- std::vector views;
+ ViewFrustums views;
ViewFrustum view;
qApp->copyCurrentViewFrustum(view);
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
index 511fa33591..6dd13c7332 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.cpp
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.cpp
@@ -296,7 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
}
}
-void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const std::vector& views,
+void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustums& views,
render::Transaction& transaction) {
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
PerformanceTimer pt("change");
@@ -417,7 +417,7 @@ void EntityTreeRenderer::update(bool simulate) {
render::Transaction transaction;
addPendingEntities(scene, transaction);
- std::vector views;
+ ViewFrustums views;
ViewFrustum view;
_viewState->copyViewFrustum(view);
diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.h b/libraries/entities-renderer/src/EntityTreeRenderer.h
index 7a7920d5b2..9ed4f9d21d 100644
--- a/libraries/entities-renderer/src/EntityTreeRenderer.h
+++ b/libraries/entities-renderer/src/EntityTreeRenderer.h
@@ -148,7 +148,7 @@ protected:
private:
void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction);
- void updateChangedEntities(const render::ScenePointer& scene, const std::vector& views,
+ void updateChangedEntities(const render::ScenePointer& scene, const ViewFrustums& views,
render::Transaction& transaction);
EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); }
render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); }
diff --git a/libraries/entities/src/DiffTraversal.h b/libraries/entities/src/DiffTraversal.h
index 50fe74a75b..0fd014ac23 100644
--- a/libraries/entities/src/DiffTraversal.h
+++ b/libraries/entities/src/DiffTraversal.h
@@ -36,7 +36,7 @@ public:
bool isVerySimilar(const View& view) const;
ViewFrustum::intersection calculateIntersection(const AACube& cube) const;
- std::vector viewFrustums;
+ ViewFrustums viewFrustums;
uint64_t startTime { 0 };
float lodScaleFactor { 1.0f };
};
diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h
index 7c0f30ec75..ba15222611 100644
--- a/libraries/shared/src/PrioritySortUtil.h
+++ b/libraries/shared/src/PrioritySortUtil.h
@@ -83,15 +83,13 @@ namespace PrioritySortUtil {
template
class PriorityQueue {
public:
- using Views = std::vector;
-
PriorityQueue() = delete;
- PriorityQueue(const Views& views) : _views(views) { }
- PriorityQueue(const Views& views, float angularWeight, float centerWeight, float ageWeight)
+ PriorityQueue(const ViewFrustums& views) : _views(views) { }
+ PriorityQueue(const ViewFrustums& views, float angularWeight, float centerWeight, float ageWeight)
: _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
{ }
- void setViews(const Views& views) { _views = views; }
+ void setViews(const ViewFrustums& views) { _views = views; }
void setWeights(float angularWeight, float centerWeight, float ageWeight) {
_angularWeight = angularWeight;
@@ -154,7 +152,7 @@ namespace PrioritySortUtil {
return priority;
}
- Views _views;
+ ViewFrustums _views;
std::priority_queue _queue;
float _angularWeight { DEFAULT_ANGULAR_COEF };
float _centerWeight { DEFAULT_CENTER_COEF };
diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h
index ba8957bba3..128a717df8 100644
--- a/libraries/shared/src/ViewFrustum.h
+++ b/libraries/shared/src/ViewFrustum.h
@@ -189,5 +189,6 @@ private:
};
using ViewFrustumPointer = std::shared_ptr;
+using ViewFrustums = std::vector;
#endif // hifi_ViewFrustum_h
From ddde0228ba3b1fd6b8a3006101594ff6ead9e2a8 Mon Sep 17 00:00:00 2001
From: Clement
Date: Mon, 23 Apr 2018 16:10:44 -0700
Subject: [PATCH 059/192] Fix AC not sending Avatar Mixer a frustum
---
assignment-client/src/Agent.cpp | 15 +++++++++++++++
assignment-client/src/Agent.h | 1 +
interface/src/Application.cpp | 4 ++--
3 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 1df901dd98..26bfb586a6 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -594,9 +594,24 @@ void Agent::sendAvatarIdentityPacket() {
auto scriptedAvatar = DependencyManager::get();
scriptedAvatar->markIdentityDataChanged();
scriptedAvatar->sendIdentityPacket();
+ sendAvatarViewFrustum();
}
}
+void Agent::sendAvatarViewFrustum() {
+ auto scriptedAvatar = DependencyManager::get();
+ ViewFrustum view;
+ view.setPosition(scriptedAvatar->getWorldPosition());
+ view.setOrientation(scriptedAvatar->getHeadOrientation());
+ auto viewFrustumByteArray = view.toByteArray();
+
+ auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
+ avatarPacket->write(viewFrustumByteArray);
+
+ DependencyManager::get()->broadcastToNodes(std::move(avatarPacket),
+ { NodeType::AvatarMixer });
+}
+
void Agent::processAgentAvatar() {
if (!_scriptEngine->isFinished() && _isAvatar) {
auto scriptedAvatar = DependencyManager::get();
diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h
index 1229f06276..e4ce0f95eb 100644
--- a/assignment-client/src/Agent.h
+++ b/assignment-client/src/Agent.h
@@ -97,6 +97,7 @@ private:
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
void sendAvatarIdentityPacket();
+ void sendAvatarViewFrustum();
QString _scriptContents;
QTimer* _scriptRequestTimeout { nullptr };
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index 33c334b493..cdb535fad1 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5806,8 +5806,8 @@ void Application::update(float deltaTime) {
void Application::sendAvatarViewFrustum() {
QByteArray viewFrustumByteArray = _viewFrustum.toByteArray();
- if (hasSecondaryViewFrustum()) {
- viewFrustumByteArray += _viewFrustum.toByteArray();
+ if (_hasSecondaryViewFrustum) {
+ viewFrustumByteArray += _secondaryViewFrustum.toByteArray();
}
auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
From a283d28686fcdb9aa138dad0d66e4d4ab5ccb2b4 Mon Sep 17 00:00:00 2001
From: Clement
Date: Tue, 24 Apr 2018 19:09:16 -0700
Subject: [PATCH 060/192] Send number of frustums in packet
---
assignment-client/src/Agent.cpp | 7 ++++++-
.../src/avatars/AvatarMixerClientData.cpp | 13 +++++++++----
.../src/avatars/AvatarMixerClientData.h | 2 +-
interface/src/Application.cpp | 6 +++++-
4 files changed, 21 insertions(+), 7 deletions(-)
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 26bfb586a6..8816ed9629 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -600,12 +600,17 @@ void Agent::sendAvatarIdentityPacket() {
void Agent::sendAvatarViewFrustum() {
auto scriptedAvatar = DependencyManager::get();
+
ViewFrustum view;
view.setPosition(scriptedAvatar->getWorldPosition());
view.setOrientation(scriptedAvatar->getHeadOrientation());
+ view.calculate();
+
+ uint8_t numFrustums = 1;
auto viewFrustumByteArray = view.toByteArray();
- auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
+ auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size() + sizeof(numFrustums));
+ avatarPacket->writePrimitive(numFrustums);
avatarPacket->write(viewFrustumByteArray);
DependencyManager::get()->broadcastToNodes(std::move(avatarPacket),
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.cpp b/assignment-client/src/avatars/AvatarMixerClientData.cpp
index 8c159cf744..a8e5a7c541 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.cpp
+++ b/assignment-client/src/avatars/AvatarMixerClientData.cpp
@@ -126,13 +126,18 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self,
}
}
-void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
+void AvatarMixerClientData::readViewFrustumPacket(QByteArray message) {
_currentViewFrustums.clear();
+
+ uint8_t numFrustums = 0;
+ memcpy(&numFrustums, message.constData(), sizeof(numFrustums));
+ message.remove(0, sizeof(numFrustums));
- auto offset = 0;
- while (offset < message.size()) {
+ for (uint8_t i = 0; i < numFrustums; ++i) {
ViewFrustum frustum;
- offset += frustum.fromByteArray(message);
+ auto bytesRead = frustum.fromByteArray(message);
+ message.remove(0, bytesRead);
+
_currentViewFrustums.push_back(frustum);
}
}
diff --git a/assignment-client/src/avatars/AvatarMixerClientData.h b/assignment-client/src/avatars/AvatarMixerClientData.h
index 13415d6a66..f17404b79f 100644
--- a/assignment-client/src/avatars/AvatarMixerClientData.h
+++ b/assignment-client/src/avatars/AvatarMixerClientData.h
@@ -98,7 +98,7 @@ public:
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
- void readViewFrustumPacket(const QByteArray& message);
+ void readViewFrustumPacket(QByteArray message);
bool otherAvatarInView(const AABox& otherAvatarBox);
diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp
index cdb535fad1..c88b5981b4 100644
--- a/interface/src/Application.cpp
+++ b/interface/src/Application.cpp
@@ -5805,12 +5805,16 @@ void Application::update(float deltaTime) {
}
void Application::sendAvatarViewFrustum() {
+ uint8_t numFrustums = 1;
QByteArray viewFrustumByteArray = _viewFrustum.toByteArray();
+
if (_hasSecondaryViewFrustum) {
+ ++numFrustums;
viewFrustumByteArray += _secondaryViewFrustum.toByteArray();
}
- auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size());
+ auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size() + sizeof(numFrustums));
+ avatarPacket->writePrimitive(numFrustums);
avatarPacket->write(viewFrustumByteArray);
DependencyManager::get()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
From 2ad948c46219e75bbfab174aea2aeb4674adadb7 Mon Sep 17 00:00:00 2001
From: Clement
Date: Tue, 24 Apr 2018 19:12:42 -0700
Subject: [PATCH 061/192] Bump ViewFrustum packet version
---
libraries/networking/src/udt/PacketHeaders.cpp | 2 ++
libraries/networking/src/udt/PacketHeaders.h | 4 ++++
2 files changed, 6 insertions(+)
diff --git a/libraries/networking/src/udt/PacketHeaders.cpp b/libraries/networking/src/udt/PacketHeaders.cpp
index b16f9c903e..17b0d90cfe 100644
--- a/libraries/networking/src/udt/PacketHeaders.cpp
+++ b/libraries/networking/src/udt/PacketHeaders.cpp
@@ -90,6 +90,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height
case PacketType::Ping:
return static_cast(PingVersion::IncludeConnectionID);
+ case PacketType::ViewFrustum:
+ return static_cast(ViewFrustumVersion::SendMultipleFrustums);
default:
return 20;
}
diff --git a/libraries/networking/src/udt/PacketHeaders.h b/libraries/networking/src/udt/PacketHeaders.h
index 9b48e3a132..c72bbb0129 100644
--- a/libraries/networking/src/udt/PacketHeaders.h
+++ b/libraries/networking/src/udt/PacketHeaders.h
@@ -328,4 +328,8 @@ enum class PingVersion : PacketVersion {
IncludeConnectionID = 18
};
+enum class ViewFrustumVersion : PacketVersion {
+ SendMultipleFrustums = 21
+};
+
#endif // hifi_PacketHeaders_h
From 83a438eb22c02ea6a86301433588c119d023a1f3 Mon Sep 17 00:00:00 2001
From: Clement
Date: Tue, 1 May 2018 18:10:55 -0700
Subject: [PATCH 062/192] Set avatar view packets on their own timer
---
assignment-client/src/Agent.cpp | 10 ++++++++--
assignment-client/src/Agent.h | 1 +
libraries/avatars/src/AvatarData.h | 1 +
3 files changed, 10 insertions(+), 2 deletions(-)
diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp
index 8816ed9629..f560ea72bd 100644
--- a/assignment-client/src/Agent.cpp
+++ b/assignment-client/src/Agent.cpp
@@ -548,16 +548,18 @@ void Agent::setIsAvatar(bool isAvatar) {
if (_isAvatar && !_avatarIdentityTimer) {
// set up the avatar timers
_avatarIdentityTimer = new QTimer(this);
+ _avatarViewTimer = new QTimer(this);
// connect our slot
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
+ connect(_avatarViewTimer, &QTimer::timeout, this, &Agent::sendAvatarViewFrustum);
// start the timers
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
+ _avatarViewTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
// tell the avatarAudioTimer to start ticking
QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
-
}
if (!_isAvatar) {
@@ -567,6 +569,10 @@ void Agent::setIsAvatar(bool isAvatar) {
delete _avatarIdentityTimer;
_avatarIdentityTimer = nullptr;
+ _avatarViewTimer->stop();
+ delete _avatarViewTimer;
+ _avatarViewTimer = nullptr;
+
// The avatar mixer never times out a connection (e.g., based on identity or data packets)
// but rather keeps avatars in its list as long as "connected". As a result, clients timeout
// when we stop sending identity, but then get woken up again by the mixer itself, which sends
@@ -585,6 +591,7 @@ void Agent::setIsAvatar(bool isAvatar) {
nodeList->sendPacket(std::move(packet), *node);
});
}
+
QMetaObject::invokeMethod(&_avatarAudioTimer, "stop");
}
}
@@ -594,7 +601,6 @@ void Agent::sendAvatarIdentityPacket() {
auto scriptedAvatar = DependencyManager::get();
scriptedAvatar->markIdentityDataChanged();
scriptedAvatar->sendIdentityPacket();
- sendAvatarViewFrustum();
}
}
diff --git a/assignment-client/src/Agent.h b/assignment-client/src/Agent.h
index e4ce0f95eb..d144f0bc01 100644
--- a/assignment-client/src/Agent.h
+++ b/assignment-client/src/Agent.h
@@ -107,6 +107,7 @@ private:
int _numAvatarSoundSentBytes = 0;
bool _isAvatar = false;
QTimer* _avatarIdentityTimer = nullptr;
+ QTimer* _avatarViewTimer = nullptr;
QHash _outgoingScriptAudioSequenceNumbers;
AudioGate _audioGate;
diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h
index 888c3bfb24..3db94f6691 100644
--- a/libraries/avatars/src/AvatarData.h
+++ b/libraries/avatars/src/AvatarData.h
@@ -278,6 +278,7 @@ namespace AvatarDataPacket {
const float MAX_AUDIO_LOUDNESS = 1000.0f; // close enough for mouth animation
const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
+const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 100;
// See also static AvatarData::defaultFullAvatarModelUrl().
const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default");
From 5a064d3499681c6b1376007e550fb7cea8526010 Mon Sep 17 00:00:00 2001
From: David Rowe
Date: Wed, 2 May 2018 14:13:15 +1200
Subject: [PATCH 063/192] Remove selection handles and list highlight when
entity deletes
---
scripts/system/edit.js | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/scripts/system/edit.js b/scripts/system/edit.js
index c99c8d401a..9b6b70b954 100644
--- a/scripts/system/edit.js
+++ b/scripts/system/edit.js
@@ -471,6 +471,10 @@ var toolBar = (function () {
}
}
+ function checkDeletedEntityAndUpdate(entityID) {
+ selectionManager.removeEntity(entityID);
+ }
+
function initialize() {
Script.scriptEnding.connect(cleanup);
Window.domainChanged.connect(function () {
@@ -493,8 +497,10 @@ var toolBar = (function () {
Entities.canRezTmpChanged.connect(checkEditPermissionsAndUpdate);
Entities.canRezCertifiedChanged.connect(checkEditPermissionsAndUpdate);
Entities.canRezTmpCertifiedChanged.connect(checkEditPermissionsAndUpdate);
-
var hasRezPermissions = (Entities.canRez() || Entities.canRezTmp() || Entities.canRezCertified() || Entities.canRezTmpCertified());
+
+ Entities.deletingEntity.connect(checkDeletedEntityAndUpdate);
+
var createButtonIconRsrc = (hasRezPermissions ? CREATE_ENABLED_ICON : CREATE_DISABLED_ICON);
tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
activeButton = tablet.addButton({
From f27e363b6879d6a3e064ee45e91fafcb770ad465 Mon Sep 17 00:00:00 2001
From: David Rowe