diff --git a/assignment-client/src/entities/EntityPriorityQueue.cpp b/assignment-client/src/entities/EntityPriorityQueue.cpp index 999a05f2e2..88dee58f9d 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,26 @@ 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 { + if (_conicalViewFrustums.empty()) { + return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; + } + + 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..9210ac549f 100644 --- a/assignment-client/src/entities/EntityPriorityQueue.h +++ b/assignment-client/src/entities/EntityPriorityQueue.h @@ -13,18 +13,20 @@ #define hifi_EntityPriorityQueue_h #include +#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 { +// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority. +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 +37,16 @@ private: float _radius { DEFAULT_VIEW_RADIUS }; }; +// Simple wrapper around a set of conical view frustums +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: @@ -61,6 +73,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 all 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 4aa52922c0..2e57f2e00f 100644 --- a/assignment-client/src/entities/EntityTreeSendThread.cpp +++ b/assignment-client/src/entities/EntityTreeSendThread.cpp @@ -103,48 +103,58 @@ 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 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 - float lodScaleFactor = _traversal.getCurrentLODScaleFactor(); - glm::vec3 viewPosition = _traversal.getCurrentView().getPosition(); while (!prevSendQueue.empty()) { EntityItemPointer entity = prevSendQueue.top().getEntity(); 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().cubeIntersectsKeyhole(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) { - _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)); - _entitiesInQueue.insert(entity.get()); + } + + if (priority != PrioritizedEntity::DO_NOT_SEND) { + _sendQueue.emplace(entity, priority, forceRemove); } } } @@ -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,161 +245,114 @@ 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) { - // 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().cubeIntersectsKeyhole(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) { - 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 { - _traversal.setScanCallback([this](DiffTraversal::VisibleElement& next) { - next.element->forEachEntity([this](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 (usesViewFrustum) { - float lodScaleFactor = _traversal.getCurrentLODScaleFactor(); - glm::vec3 viewPosition = _traversal.getCurrentView().getPosition(); - _traversal.setScanCallback([=](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().cubeIntersectsKeyhole(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) { - 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([this](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(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) { + _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; + + + 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); + } + }); + }); + 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 (_sendQueue.contains(entity.get())) { + 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) { + 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 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; + } + + + if (priority != PrioritizedEntity::DO_NOT_SEND) { + _sendQueue.emplace(entity, priority); + } + }); + } + }); + break; + case DiffTraversal::Differential: + 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 (_sendQueue.contains(entity.get())) { + 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().cubeIntersectsKeyhole(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)) { - float priority = _conicalView.computePriority(cube); - _sendQueue.push(PrioritizedEntity(entity, priority)); - _entitiesInQueue.insert(entity.get()); - } else { - // 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()); - } - } - } + 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 { - _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) { + } 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()); + priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY; + } + + + if (priority != PrioritizedEntity::DO_NOT_SEND) { + _sendQueue.emplace(entity, priority); } }); }); @@ -479,11 +441,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(); } @@ -501,18 +462,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().cubeIntersectsKeyhole(cube)) { - _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::FORCE_REMOVE, true)); - _entitiesInQueue.insert(entity.get()); + if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().intersects(cube)) { + _sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true); } } else { - _sendQueue.push(PrioritizedEntity(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true)); - _entitiesInQueue.insert(entity.get()); + _sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true); } } } diff --git a/assignment-client/src/entities/EntityTreeSendThread.h b/assignment-client/src/entities/EntityTreeSendThread.h index 1e2bd15429..9eea98b7fd 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; @@ -51,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 diff --git a/assignment-client/src/octree/OctreeHeadlessViewer.cpp b/assignment-client/src/octree/OctreeHeadlessViewer.cpp index d3b20fb623..6d91a134c2 100644 --- a/assignment-client/src/octree/OctreeHeadlessViewer.cpp +++ b/assignment-client/src/octree/OctreeHeadlessViewer.cpp @@ -14,32 +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.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.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 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 75e6472078..d97b14fa1c 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -5218,6 +5218,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) { @@ -5571,6 +5643,11 @@ 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. + updateSecondaryCameraViewFrustum(); } quint64 now = usecTimestampNow(); @@ -5584,6 +5661,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; @@ -5592,6 +5671,7 @@ void Application::update(float deltaTime) { } sendAvatarViewFrustum(); _lastQueriedViewFrustum = _viewFrustum; + _lastQueriedSecondaryViewFrustum = _secondaryViewFrustum; } } @@ -5830,14 +5910,15 @@ 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); + + if (hasSecondaryViewFrustum()) { + copySecondaryViewFrustum(viewFrustum); + _octreeQuery.setSecondaryViewFrustum(viewFrustum); + } else { + _octreeQuery.clearSecondaryViewFrustum(); + } + auto lodManager = DependencyManager::get(); _octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale()); _octreeQuery.setBoundaryLevelAdjust(lodManager->getBoundaryLevelAdjust()); @@ -5921,6 +6002,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 e1787b7a32..baaa886910 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(); @@ -178,6 +180,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; } @@ -569,8 +574,11 @@ 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; OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers diff --git a/libraries/entities/src/DiffTraversal.cpp b/libraries/entities/src/DiffTraversal.cpp index 764c420197..39328e11ad 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; @@ -37,15 +36,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 +74,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 +115,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 +133,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; + } + + 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 { + if (viewFrustums.empty()) { + // Everything intersects when not using view frustums + return true; + } + + 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 { + 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: // @@ -159,29 +225,25 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const ViewFrustum& viewFr // // 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 = 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 = 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/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; 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 { 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..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 @@ -134,7 +138,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; @@ -168,14 +172,12 @@ void 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(); } + + return sourceBuffer - startPosition; } diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h index 981aabe70c..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; @@ -147,7 +148,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;