diff --git a/assignment-client/src/Agent.cpp b/assignment-client/src/Agent.cpp index 1df901dd98..0b373e511b 100644 --- a/assignment-client/src/Agent.cpp +++ b/assignment-client/src/Agent.cpp @@ -548,16 +548,21 @@ 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); + + 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); // tell the avatarAudioTimer to start ticking QMetaObject::invokeMethod(&_avatarAudioTimer, "start"); - } if (!_isAvatar) { @@ -567,6 +572,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 +594,7 @@ void Agent::setIsAvatar(bool isAvatar) { nodeList->sendPacket(std::move(packet), *node); }); } + QMetaObject::invokeMethod(&_avatarAudioTimer, "stop"); } } @@ -597,6 +607,25 @@ 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() + sizeof(numFrustums)); + avatarPacket->writePrimitive(numFrustums); + 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..d144f0bc01 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 }; @@ -106,6 +107,7 @@ private: int _numAvatarSoundSentBytes = 0; bool _isAvatar = false; QTimer* _avatarIdentityTimer = nullptr; + QTimer* _avatarViewTimer = nullptr; QHash _outgoingScriptAudioSequenceNumbers; AudioGate _audioGate; 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..a8e5a7c541 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); } @@ -128,12 +126,27 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self, } } -void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) { - _currentViewFrustum.fromByteArray(message); +void AvatarMixerClientData::readViewFrustumPacket(QByteArray message) { + _currentViewFrustums.clear(); + + uint8_t numFrustums = 0; + memcpy(&numFrustums, message.constData(), sizeof(numFrustums)); + message.remove(0, sizeof(numFrustums)); + + for (uint8_t i = 0; i < numFrustums; ++i) { + ViewFrustum frustum; + auto bytesRead = frustum.fromByteArray(message); + message.remove(0, bytesRead); + + _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..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); @@ -110,7 +110,7 @@ public: bool getRequestsDomainListData() { return _requestsDomainListData; } void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; } - ViewFrustum getViewFrustum() const { return _currentViewFrustum; } + 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; - ViewFrustum _currentViewFrustum; + ViewFrustums _currentViewFrustums; int _recentOtherAvatarsInView { 0 }; int _recentOtherAvatarsOutOfView { 0 }; diff --git a/assignment-client/src/avatars/AvatarMixerSlave.cpp b/assignment-client/src/avatars/AvatarMixerSlave.cpp index 6f19b73cc5..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/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/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 @@ diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 75e6472078..5e10d29505 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; } } @@ -5764,8 +5844,16 @@ void Application::update(float deltaTime) { } void Application::sendAvatarViewFrustum() { + uint8_t numFrustums = 1; QByteArray viewFrustumByteArray = _viewFrustum.toByteArray(); - auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size()); + + if (_hasSecondaryViewFrustum) { + ++numFrustums; + viewFrustumByteArray += _secondaryViewFrustum.toByteArray(); + } + + 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); @@ -5830,14 +5918,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 +6010,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..83e719333d 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(); @@ -173,11 +175,14 @@ 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; + 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/interface/src/Crashpad.cpp b/interface/src/Crashpad.cpp index e39cd42d81..45f1d0778f 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,27 @@ #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(); +CrashpadClient* client { nullptr }; std::mutex annotationMutex; crashpad::SimpleStringDictionary* crashpadAnnotations { nullptr }; -#include - 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) { - CrashpadClient client; - if (gIPCPipe.length()) { - client.SetHandlerIPCPipe(gIPCPipe); - } - client.DumpAndCrash(pExceptionInfo); + client->DumpAndCrash(pExceptionInfo); } return EXCEPTION_CONTINUE_SEARCH; @@ -60,7 +61,8 @@ bool startCrashHandler() { return false; } - CrashpadClient client; + assert(!client); + client = new CrashpadClient(); std::vector arguments; std::map annotations; @@ -96,12 +98,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) { diff --git a/interface/src/avatar/AvatarManager.cpp b/interface/src/avatar/AvatarManager.cpp index b71c060465..087e23a933 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, + + ViewFrustums 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/avatars/src/AvatarData.h b/libraries/avatars/src/AvatarData.h index 888c3bfb24..bbcdd3693d 100644 --- a/libraries/avatars/src/AvatarData.h +++ b/libraries/avatars/src/AvatarData.h @@ -277,8 +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; - // See also static AvatarData::defaultFullAvatarModelUrl(). const QString DEFAULT_FULL_AVATAR_MODEL_NAME = QString("Default"); diff --git a/libraries/entities-renderer/src/EntityTreeRenderer.cpp b/libraries/entities-renderer/src/EntityTreeRenderer.cpp index ba81922979..6dd13c7332 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 ViewFrustums& 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); + + ViewFrustums 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..9ed4f9d21d 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 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.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..0fd014ac23 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; + + ViewFrustums 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..17b0d90cfe 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: @@ -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 e6b133c158..c72bbb0129 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 { @@ -327,4 +328,8 @@ enum class PingVersion : PacketVersion { IncludeConnectionID = 18 }; +enum class ViewFrustumVersion : PacketVersion { + SendMultipleFrustums = 21 +}; + #endif // hifi_PacketHeaders_h 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/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/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(); } diff --git a/libraries/shared/src/PrioritySortUtil.h b/libraries/shared/src/PrioritySortUtil.h index 279fa42ea4..ba15222611 100644 --- a/libraries/shared/src/PrioritySortUtil.h +++ b/libraries/shared/src/PrioritySortUtil.h @@ -84,14 +84,12 @@ namespace PrioritySortUtil { class PriorityQueue { public: 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 ViewFrustums& views) : _views(views) { } + PriorityQueue(const ViewFrustums& 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 ViewFrustums& views) { _views = views; } void setWeights(float angularWeight, float centerWeight, float ageWeight) { _angularWeight = angularWeight; @@ -109,7 +107,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 +126,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 +143,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 +152,7 @@ namespace PrioritySortUtil { return priority; } - ViewFrustum _view; + ViewFrustums _views; std::priority_queue _queue; float _angularWeight { DEFAULT_ANGULAR_COEF }; float _centerWeight { DEFAULT_CENTER_COEF }; 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..128a717df8 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; @@ -188,5 +189,6 @@ private: }; using ViewFrustumPointer = std::shared_ptr; +using ViewFrustums = std::vector; #endif // hifi_ViewFrustum_h 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(); }