From 5ab16302cac5a63f6ac3aa084850a145b43f4785 Mon Sep 17 00:00:00 2001 From: Atlante45 Date: Tue, 24 Apr 2018 15:09:30 -0700 Subject: [PATCH 01/23] 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 02/23] 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 f3fe1f3e58386363ddb2e2b5a48e5a08bd73f4c3 Mon Sep 17 00:00:00 2001 From: Clement Date: Mon, 30 Apr 2018 13:51:53 -0700 Subject: [PATCH 03/23] 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 49fad3d8685dc7a5d430b27574ee4b7525d0c7d8 Mon Sep 17 00:00:00 2001 From: Clement Date: Fri, 13 Apr 2018 18:01:54 -0700 Subject: [PATCH 04/23] 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 05/23] 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 06/23] 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 07/23] 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 08/23] 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 09/23] 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 10/23] 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 11/23] 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 12/23] 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 13/23] 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 14/23] 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 15/23] 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 16/23] 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 17/23] 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 18/23] 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 19/23] 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 20/23] 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 21/23] 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 e1c5eb9bda063367ab0af270d14505ab925af9ab Mon Sep 17 00:00:00 2001 From: Clement Date: Wed, 2 May 2018 17:17:19 -0700 Subject: [PATCH 22/23] Fix wizard wording --- domain-server/resources/web/wizard/index.shtml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/domain-server/resources/web/wizard/index.shtml b/domain-server/resources/web/wizard/index.shtml index 5a3286296d..3bc7503b44 100644 --- a/domain-server/resources/web/wizard/index.shtml +++ b/domain-server/resources/web/wizard/index.shtml @@ -26,7 +26,7 @@ Place names are similar to web addresses. Users who want to visit your domain can enter its Place Name in High Fidelity's Interface. You can choose a Place Name for your domain.
- People can also use your domain's IP address (shown below) to visit your High Fidelity domain. + Your domain may also be reachable by IP address.
@@ -35,10 +35,10 @@ From 7fe16f82fbdaed268b03ce8caa935b196019c929 Mon Sep 17 00:00:00 2001 From: Clement Date: Thu, 3 May 2018 17:08:17 -0700 Subject: [PATCH 23/23] Move variables to Agent.cpp --- assignment-client/src/Agent.cpp | 3 +++ libraries/avatars/src/AvatarData.h | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index f560ea72bd..0b373e511b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -554,6 +554,9 @@ void Agent::setIsAvatar(bool isAvatar) { connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket); connect(_avatarViewTimer, &QTimer::timeout, this, &Agent::sendAvatarViewFrustum); + static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000; + static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000; + // 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); diff --git a/libraries/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 3db94f6691..bbcdd3693d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -277,9 +277,6 @@ 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");