mirror of
https://github.com/overte-org/overte.git
synced 2025-08-10 01:00:44 +02:00
Merge pull request #13074 from Atlante45/fix/spec-cam-punchlist
Multifrustum queries punchlist
This commit is contained in:
commit
39e2a78b1a
36 changed files with 510 additions and 597 deletions
|
@ -548,18 +548,18 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
if (_isAvatar && !_avatarIdentityTimer) {
|
if (_isAvatar && !_avatarIdentityTimer) {
|
||||||
// set up the avatar timers
|
// set up the avatar timers
|
||||||
_avatarIdentityTimer = new QTimer(this);
|
_avatarIdentityTimer = new QTimer(this);
|
||||||
_avatarViewTimer = new QTimer(this);
|
_avatarQueryTimer = new QTimer(this);
|
||||||
|
|
||||||
// connect our slot
|
// connect our slot
|
||||||
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
|
connect(_avatarIdentityTimer, &QTimer::timeout, this, &Agent::sendAvatarIdentityPacket);
|
||||||
connect(_avatarViewTimer, &QTimer::timeout, this, &Agent::sendAvatarViewFrustum);
|
connect(_avatarQueryTimer, &QTimer::timeout, this, &Agent::queryAvatars);
|
||||||
|
|
||||||
static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
static const int AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||||
static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
|
static const int AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS = 1000;
|
||||||
|
|
||||||
// start the timers
|
// start the timers
|
||||||
_avatarIdentityTimer->start(AVATAR_IDENTITY_PACKET_SEND_INTERVAL_MSECS); // FIXME - we shouldn't really need to constantly send identity packets
|
_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);
|
_avatarQueryTimer->start(AVATAR_VIEW_PACKET_SEND_INTERVAL_MSECS);
|
||||||
|
|
||||||
// tell the avatarAudioTimer to start ticking
|
// tell the avatarAudioTimer to start ticking
|
||||||
QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
|
QMetaObject::invokeMethod(&_avatarAudioTimer, "start");
|
||||||
|
@ -572,9 +572,9 @@ void Agent::setIsAvatar(bool isAvatar) {
|
||||||
delete _avatarIdentityTimer;
|
delete _avatarIdentityTimer;
|
||||||
_avatarIdentityTimer = nullptr;
|
_avatarIdentityTimer = nullptr;
|
||||||
|
|
||||||
_avatarViewTimer->stop();
|
_avatarQueryTimer->stop();
|
||||||
delete _avatarViewTimer;
|
delete _avatarQueryTimer;
|
||||||
_avatarViewTimer = nullptr;
|
_avatarQueryTimer = nullptr;
|
||||||
|
|
||||||
// The avatar mixer never times out a connection (e.g., based on identity or data packets)
|
// 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
|
// but rather keeps avatars in its list as long as "connected". As a result, clients timeout
|
||||||
|
@ -607,20 +607,26 @@ void Agent::sendAvatarIdentityPacket() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Agent::sendAvatarViewFrustum() {
|
void Agent::queryAvatars() {
|
||||||
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
auto scriptedAvatar = DependencyManager::get<ScriptableAvatar>();
|
||||||
|
|
||||||
ViewFrustum view;
|
ViewFrustum view;
|
||||||
view.setPosition(scriptedAvatar->getWorldPosition());
|
view.setPosition(scriptedAvatar->getWorldPosition());
|
||||||
view.setOrientation(scriptedAvatar->getHeadOrientation());
|
view.setOrientation(scriptedAvatar->getHeadOrientation());
|
||||||
view.calculate();
|
view.calculate();
|
||||||
|
ConicalViewFrustum conicalView { view };
|
||||||
|
|
||||||
|
auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
|
||||||
|
auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload());
|
||||||
|
auto bufferStart = destinationBuffer;
|
||||||
|
|
||||||
uint8_t numFrustums = 1;
|
uint8_t numFrustums = 1;
|
||||||
auto viewFrustumByteArray = view.toByteArray();
|
memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
|
||||||
|
destinationBuffer += sizeof(numFrustums);
|
||||||
|
|
||||||
auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size() + sizeof(numFrustums));
|
destinationBuffer += conicalView.serialize(destinationBuffer);
|
||||||
avatarPacket->writePrimitive(numFrustums);
|
|
||||||
avatarPacket->write(viewFrustumByteArray);
|
avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
|
||||||
|
|
||||||
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket),
|
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket),
|
||||||
{ NodeType::AvatarMixer });
|
{ NodeType::AvatarMixer });
|
||||||
|
|
|
@ -97,7 +97,7 @@ private:
|
||||||
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
|
void setAvatarSound(SharedSoundPointer avatarSound) { _avatarSound = avatarSound; }
|
||||||
|
|
||||||
void sendAvatarIdentityPacket();
|
void sendAvatarIdentityPacket();
|
||||||
void sendAvatarViewFrustum();
|
void queryAvatars();
|
||||||
|
|
||||||
QString _scriptContents;
|
QString _scriptContents;
|
||||||
QTimer* _scriptRequestTimeout { nullptr };
|
QTimer* _scriptRequestTimeout { nullptr };
|
||||||
|
@ -107,7 +107,7 @@ private:
|
||||||
int _numAvatarSoundSentBytes = 0;
|
int _numAvatarSoundSentBytes = 0;
|
||||||
bool _isAvatar = false;
|
bool _isAvatar = false;
|
||||||
QTimer* _avatarIdentityTimer = nullptr;
|
QTimer* _avatarIdentityTimer = nullptr;
|
||||||
QTimer* _avatarViewTimer = nullptr;
|
QTimer* _avatarQueryTimer = nullptr;
|
||||||
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
QHash<QUuid, quint16> _outgoingScriptAudioSequenceNumbers;
|
||||||
|
|
||||||
AudioGate _audioGate;
|
AudioGate _audioGate;
|
||||||
|
|
|
@ -47,7 +47,7 @@ AvatarMixer::AvatarMixer(ReceivedMessage& message) :
|
||||||
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
auto& packetReceiver = DependencyManager::get<NodeList>()->getPacketReceiver();
|
||||||
packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket");
|
packetReceiver.registerListener(PacketType::AvatarData, this, "queueIncomingPacket");
|
||||||
packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting");
|
packetReceiver.registerListener(PacketType::AdjustAvatarSorting, this, "handleAdjustAvatarSorting");
|
||||||
packetReceiver.registerListener(PacketType::ViewFrustum, this, "handleViewFrustumPacket");
|
packetReceiver.registerListener(PacketType::AvatarQuery, this, "handleAvatarQueryPacket");
|
||||||
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
packetReceiver.registerListener(PacketType::AvatarIdentity, this, "handleAvatarIdentityPacket");
|
||||||
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
packetReceiver.registerListener(PacketType::KillAvatar, this, "handleKillAvatarPacket");
|
||||||
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
packetReceiver.registerListener(PacketType::NodeIgnoreRequest, this, "handleNodeIgnoreRequestPacket");
|
||||||
|
@ -517,7 +517,7 @@ void AvatarMixer::handleAdjustAvatarSorting(QSharedPointer<ReceivedMessage> mess
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void AvatarMixer::handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
void AvatarMixer::handleAvatarQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode) {
|
||||||
auto start = usecTimestampNow();
|
auto start = usecTimestampNow();
|
||||||
getOrCreateClientData(senderNode);
|
getOrCreateClientData(senderNode);
|
||||||
|
|
||||||
|
@ -683,7 +683,7 @@ void AvatarMixer::sendStatsPacket() {
|
||||||
incomingPacketStats["handleNodeIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleNodeIgnoreRequestPacketElapsedTime);
|
incomingPacketStats["handleNodeIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleNodeIgnoreRequestPacketElapsedTime);
|
||||||
incomingPacketStats["handleRadiusIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRadiusIgnoreRequestPacketElapsedTime);
|
incomingPacketStats["handleRadiusIgnoreRequestPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRadiusIgnoreRequestPacketElapsedTime);
|
||||||
incomingPacketStats["handleRequestsDomainListDataPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRequestsDomainListDataPacketElapsedTime);
|
incomingPacketStats["handleRequestsDomainListDataPacket"] = TIGHT_LOOP_STAT_UINT64(_handleRequestsDomainListDataPacketElapsedTime);
|
||||||
incomingPacketStats["handleViewFrustumPacket"] = TIGHT_LOOP_STAT_UINT64(_handleViewFrustumPacketElapsedTime);
|
incomingPacketStats["handleAvatarQueryPacket"] = TIGHT_LOOP_STAT_UINT64(_handleViewFrustumPacketElapsedTime);
|
||||||
|
|
||||||
singleCoreTasks["incoming_packets"] = incomingPacketStats;
|
singleCoreTasks["incoming_packets"] = incomingPacketStats;
|
||||||
singleCoreTasks["sendStats"] = (float)_sendStatsElapsedTime;
|
singleCoreTasks["sendStats"] = (float)_sendStatsElapsedTime;
|
||||||
|
|
|
@ -46,7 +46,7 @@ public slots:
|
||||||
private slots:
|
private slots:
|
||||||
void queueIncomingPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
void queueIncomingPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer node);
|
||||||
void handleAdjustAvatarSorting(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleAdjustAvatarSorting(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleViewFrustumPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleAvatarQueryPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleAvatarIdentityPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleKillAvatarPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
void handleNodeIgnoreRequestPacket(QSharedPointer<ReceivedMessage> message, SharedNodePointer senderNode);
|
||||||
|
|
|
@ -126,17 +126,18 @@ void AvatarMixerClientData::removeFromRadiusIgnoringSet(SharedNodePointer self,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AvatarMixerClientData::readViewFrustumPacket(QByteArray message) {
|
void AvatarMixerClientData::readViewFrustumPacket(const QByteArray& message) {
|
||||||
_currentViewFrustums.clear();
|
_currentViewFrustums.clear();
|
||||||
|
|
||||||
|
auto sourceBuffer = reinterpret_cast<const unsigned char*>(message.constData());
|
||||||
|
|
||||||
uint8_t numFrustums = 0;
|
uint8_t numFrustums = 0;
|
||||||
memcpy(&numFrustums, message.constData(), sizeof(numFrustums));
|
memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
|
||||||
message.remove(0, sizeof(numFrustums));
|
sourceBuffer += sizeof(numFrustums);
|
||||||
|
|
||||||
for (uint8_t i = 0; i < numFrustums; ++i) {
|
for (uint8_t i = 0; i < numFrustums; ++i) {
|
||||||
ViewFrustum frustum;
|
ConicalViewFrustum frustum;
|
||||||
auto bytesRead = frustum.fromByteArray(message);
|
sourceBuffer += frustum.deserialize(sourceBuffer);
|
||||||
message.remove(0, bytesRead);
|
|
||||||
|
|
||||||
_currentViewFrustums.push_back(frustum);
|
_currentViewFrustums.push_back(frustum);
|
||||||
}
|
}
|
||||||
|
@ -144,8 +145,8 @@ void AvatarMixerClientData::readViewFrustumPacket(QByteArray message) {
|
||||||
|
|
||||||
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
|
bool AvatarMixerClientData::otherAvatarInView(const AABox& otherAvatarBox) {
|
||||||
return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
|
return std::any_of(std::begin(_currentViewFrustums), std::end(_currentViewFrustums),
|
||||||
[&](const ViewFrustum& viewFrustum) {
|
[&](const ConicalViewFrustum& viewFrustum) {
|
||||||
return viewFrustum.boxIntersectsKeyhole(otherAvatarBox);
|
return viewFrustum.intersects(otherAvatarBox);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
#include <PortableHighResolutionClock.h>
|
#include <PortableHighResolutionClock.h>
|
||||||
#include <SimpleMovingAverage.h>
|
#include <SimpleMovingAverage.h>
|
||||||
#include <UUIDHasher.h>
|
#include <UUIDHasher.h>
|
||||||
#include <ViewFrustum.h>
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
|
const QString OUTBOUND_AVATAR_DATA_STATS_KEY = "outbound_av_data_kbps";
|
||||||
const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
const QString INBOUND_AVATAR_DATA_STATS_KEY = "inbound_av_data_kbps";
|
||||||
|
@ -98,7 +98,7 @@ public:
|
||||||
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
|
void removeFromRadiusIgnoringSet(SharedNodePointer self, const QUuid& other);
|
||||||
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
void ignoreOther(SharedNodePointer self, SharedNodePointer other);
|
||||||
|
|
||||||
void readViewFrustumPacket(QByteArray message);
|
void readViewFrustumPacket(const QByteArray& message);
|
||||||
|
|
||||||
bool otherAvatarInView(const AABox& otherAvatarBox);
|
bool otherAvatarInView(const AABox& otherAvatarBox);
|
||||||
|
|
||||||
|
@ -110,7 +110,7 @@ public:
|
||||||
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
bool getRequestsDomainListData() { return _requestsDomainListData; }
|
||||||
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
void setRequestsDomainListData(bool requesting) { _requestsDomainListData = requesting; }
|
||||||
|
|
||||||
const ViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
|
const ConicalViewFrustums& getViewFrustums() const { return _currentViewFrustums; }
|
||||||
|
|
||||||
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
|
uint64_t getLastOtherAvatarEncodeTime(QUuid otherAvatar) const;
|
||||||
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
|
void setLastOtherAvatarEncodeTime(const QUuid& otherAvatar, uint64_t time);
|
||||||
|
@ -150,7 +150,7 @@ private:
|
||||||
|
|
||||||
SimpleMovingAverage _avgOtherAvatarDataRate;
|
SimpleMovingAverage _avgOtherAvatarDataRate;
|
||||||
std::unordered_set<QUuid> _radiusIgnoredOthers;
|
std::unordered_set<QUuid> _radiusIgnoredOthers;
|
||||||
ViewFrustums _currentViewFrustums;
|
ConicalViewFrustums _currentViewFrustums;
|
||||||
|
|
||||||
int _recentOtherAvatarsInView { 0 };
|
int _recentOtherAvatarsInView { 0 };
|
||||||
int _recentOtherAvatarsOutOfView { 0 };
|
int _recentOtherAvatarsOutOfView { 0 };
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
//
|
|
||||||
// EntityPriorityQueue.cpp
|
|
||||||
// assignment-client/src/entities
|
|
||||||
//
|
|
||||||
// Created by Andrew Meadows 2017.08.08
|
|
||||||
// Copyright 2017 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 "EntityPriorityQueue.h"
|
|
||||||
|
|
||||||
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 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();
|
|
||||||
_direction = viewFrustum.getDirection();
|
|
||||||
|
|
||||||
// We cache the sin and cos of the half angle of the cone that bounds the frustum.
|
|
||||||
// (the math here is left as an exercise for the reader)
|
|
||||||
float A = viewFrustum.getAspectRatio();
|
|
||||||
float t = tanf(0.5f * viewFrustum.getFieldOfView());
|
|
||||||
_cosAngle = 1.0f / sqrtf(1.0f + (A * A + 1.0f) * (t * t));
|
|
||||||
_sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
|
|
||||||
|
|
||||||
_radius = viewFrustum.getCenterRadius();
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
if (d < _radius + r) {
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
// We check the angle between the center of the cube and the _direction of the view.
|
|
||||||
// If it is less than the sum of the half-angle from center of cone to outer edge plus
|
|
||||||
// the half apparent angle of the bounding sphere then it is in view.
|
|
||||||
//
|
|
||||||
// The math here is left as an exercise for the reader with the following hints:
|
|
||||||
// (1) We actually check the dot product of the cube's local position rather than the angle and
|
|
||||||
// (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
|
|
||||||
if (glm::dot(p, _direction) > sqrtf(d * d - r * r) * _cosAngle - r * _sinAngle) {
|
|
||||||
const float AVOID_DIVIDE_BY_ZERO = 0.001f;
|
|
||||||
return r / (d + AVOID_DIVIDE_BY_ZERO);
|
|
||||||
}
|
|
||||||
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;
|
|
||||||
}
|
|
|
@ -107,16 +107,7 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
||||||
|
|
||||||
|
|
||||||
DiffTraversal::View newView;
|
DiffTraversal::View newView;
|
||||||
|
newView.viewFrustums = nodeData->getCurrentViews();
|
||||||
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);
|
int32_t lodLevelOffset = nodeData->getBoundaryLevelAdjust() + (viewFrustumChanged ? LOW_RES_MOVING_ADJUST : NO_BOUNDARY_ADJUST);
|
||||||
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
newView.lodScaleFactor = powf(2.0f, lodLevelOffset);
|
||||||
|
@ -141,16 +132,8 @@ void EntityTreeSendThread::traverseTreeAndSendContents(SharedNodePointer node, O
|
||||||
if (forceRemove) {
|
if (forceRemove) {
|
||||||
priority = PrioritizedEntity::FORCE_REMOVE;
|
priority = PrioritizedEntity::FORCE_REMOVE;
|
||||||
} else {
|
} else {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
priority = view.computePriority(entity);
|
||||||
if (success) {
|
|
||||||
const auto& view = _traversal.getCurrentView();
|
|
||||||
if (view.intersects(cube) && view.isBigEnough(cube)) {
|
|
||||||
priority = _conicalView.computePriority(cube);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
|
@ -235,11 +218,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
// (3) Differential = view has changed --> find what has changed or in new view but not old
|
// (3) Differential = view has changed --> find what has changed or in new view but not old
|
||||||
//
|
//
|
||||||
// The "scanCallback" we provide to the traversal depends on the type:
|
// The "scanCallback" we provide to the traversal depends on the type:
|
||||||
//
|
|
||||||
// The _conicalView is updated here as a cached view approximation used by the lambdas for efficient
|
|
||||||
// computation of entity sorting priorities.
|
|
||||||
//
|
|
||||||
_conicalView.set(_traversal.getCurrentView());
|
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case DiffTraversal::First:
|
case DiffTraversal::First:
|
||||||
|
@ -251,25 +229,8 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
if (_sendQueue.contains(entity.get())) {
|
if (_sendQueue.contains(entity.get())) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
const auto& view = _traversal.getCurrentView();
|
||||||
|
float priority = view.computePriority(entity);
|
||||||
|
|
||||||
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) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, priority);
|
_sendQueue.emplace(entity, priority);
|
||||||
|
@ -288,21 +249,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
}
|
}
|
||||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||||
|
|
||||||
|
|
||||||
auto knownTimestamp = _knownState.find(entity.get());
|
auto knownTimestamp = _knownState.find(entity.get());
|
||||||
if (knownTimestamp == _knownState.end()) {
|
if (knownTimestamp == _knownState.end()) {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
priority = view.computePriority(entity);
|
||||||
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 ||
|
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
||||||
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
||||||
// it is known and it changed --> put it on the queue with any priority
|
// it is known and it changed --> put it on the queue with any priority
|
||||||
|
@ -310,7 +261,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, priority);
|
_sendQueue.emplace(entity, priority);
|
||||||
}
|
}
|
||||||
|
@ -328,21 +278,11 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
}
|
}
|
||||||
float priority = PrioritizedEntity::DO_NOT_SEND;
|
float priority = PrioritizedEntity::DO_NOT_SEND;
|
||||||
|
|
||||||
|
|
||||||
auto knownTimestamp = _knownState.find(entity.get());
|
auto knownTimestamp = _knownState.find(entity.get());
|
||||||
if (knownTimestamp == _knownState.end()) {
|
if (knownTimestamp == _knownState.end()) {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
priority = view.computePriority(entity);
|
||||||
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 ||
|
} else if (entity->getLastEdited() > knownTimestamp->second ||
|
||||||
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
entity->getLastChangedOnServer() > knownTimestamp->second) {
|
||||||
// it is known and it changed --> put it on the queue with any priority
|
// it is known and it changed --> put it on the queue with any priority
|
||||||
|
@ -350,7 +290,6 @@ void EntityTreeSendThread::startNewTraversal(const DiffTraversal::View& view, En
|
||||||
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
priority = PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
if (priority != PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, priority);
|
_sendQueue.emplace(entity, priority);
|
||||||
}
|
}
|
||||||
|
@ -463,14 +402,13 @@ bool EntityTreeSendThread::traverseTreeAndBuildNextPacketPayload(EncodeBitstream
|
||||||
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
|
void EntityTreeSendThread::editingEntityPointer(const EntityItemPointer& entity) {
|
||||||
if (entity) {
|
if (entity) {
|
||||||
if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
|
if (!_sendQueue.contains(entity.get()) && _knownState.find(entity.get()) != _knownState.end()) {
|
||||||
bool success = false;
|
const auto& view = _traversal.getCurrentView();
|
||||||
AACube cube = entity->getQueryAACube(success);
|
float priority = view.computePriority(entity);
|
||||||
if (success) {
|
|
||||||
// We can force a removal from _knownState if the current view is used and entity is out of view
|
// We can force a removal from _knownState if the current view is used and entity is out of view
|
||||||
if (_traversal.doesCurrentUseViewFrustum() && !_traversal.getCurrentView().intersects(cube)) {
|
if (priority == PrioritizedEntity::DO_NOT_SEND) {
|
||||||
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
|
_sendQueue.emplace(entity, PrioritizedEntity::FORCE_REMOVE, true);
|
||||||
}
|
} else if (priority == PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY) {
|
||||||
} else {
|
|
||||||
_sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
|
_sendQueue.emplace(entity, PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,9 @@
|
||||||
#include "../octree/OctreeSendThread.h"
|
#include "../octree/OctreeSendThread.h"
|
||||||
|
|
||||||
#include <DiffTraversal.h>
|
#include <DiffTraversal.h>
|
||||||
|
#include <EntityPriorityQueue.h>
|
||||||
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
#include "EntityPriorityQueue.h"
|
|
||||||
|
|
||||||
class EntityNodeData;
|
class EntityNodeData;
|
||||||
class EntityItem;
|
class EntityItem;
|
||||||
|
@ -51,7 +52,6 @@ private:
|
||||||
DiffTraversal _traversal;
|
DiffTraversal _traversal;
|
||||||
EntityPriorityQueue _sendQueue;
|
EntityPriorityQueue _sendQueue;
|
||||||
std::unordered_map<EntityItem*, uint64_t> _knownState;
|
std::unordered_map<EntityItem*, uint64_t> _knownState;
|
||||||
ConicalView _conicalView; // cached optimized view for fast priority calculations
|
|
||||||
|
|
||||||
// packet construction stuff
|
// packet construction stuff
|
||||||
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
|
EntityTreeElementExtraEncodeDataPointer _extraEncodeData { new EntityTreeElementExtraEncodeData() };
|
||||||
|
|
|
@ -19,7 +19,10 @@ void OctreeHeadlessViewer::queryOctree() {
|
||||||
PacketType packetType = getMyQueryMessageType();
|
PacketType packetType = getMyQueryMessageType();
|
||||||
|
|
||||||
if (_hasViewFrustum) {
|
if (_hasViewFrustum) {
|
||||||
_octreeQuery.setMainViewFrustum(_viewFrustum);
|
ConicalViewFrustums views { _viewFrustum };
|
||||||
|
_octreeQuery.setConicalViews(views);
|
||||||
|
} else {
|
||||||
|
_octreeQuery.clearConicalViews();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto nodeList = DependencyManager::get<NodeList>();
|
auto nodeList = DependencyManager::get<NodeList>();
|
||||||
|
|
|
@ -330,7 +330,7 @@ int OctreeSendThread::packetDistributor(SharedNodePointer node, OctreeQueryNode*
|
||||||
} else {
|
} else {
|
||||||
// we aren't forcing a full scene, check if something else suggests we should
|
// we aren't forcing a full scene, check if something else suggests we should
|
||||||
isFullScene = nodeData->haveJSONParametersChanged() ||
|
isFullScene = nodeData->haveJSONParametersChanged() ||
|
||||||
(nodeData->hasMainViewFrustum() &&
|
(nodeData->hasConicalViews() &&
|
||||||
(nodeData->getViewFrustumJustStoppedChanging() ||
|
(nodeData->getViewFrustumJustStoppedChanging() ||
|
||||||
nodeData->hasLodChanged()));
|
nodeData->hasLodChanged()));
|
||||||
}
|
}
|
||||||
|
|
|
@ -967,7 +967,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer, bo
|
||||||
_entitySimulation(new PhysicalEntitySimulation()),
|
_entitySimulation(new PhysicalEntitySimulation()),
|
||||||
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
|
_physicsEngine(new PhysicsEngine(Vectors::ZERO)),
|
||||||
_entityClipboard(new EntityTree()),
|
_entityClipboard(new EntityTree()),
|
||||||
_lastQueriedTime(usecTimestampNow()),
|
|
||||||
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
|
_previousScriptLocation("LastScriptLocation", DESKTOP_LOCATION),
|
||||||
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
|
_fieldOfView("fieldOfView", DEFAULT_FIELD_OF_VIEW_DEGREES),
|
||||||
_hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT),
|
_hmdTabletScale("hmdTabletScale", DEFAULT_HMD_TABLET_SCALE_PERCENT),
|
||||||
|
@ -5084,7 +5083,7 @@ void Application::reloadResourceCaches() {
|
||||||
resetPhysicsReadyInformation();
|
resetPhysicsReadyInformation();
|
||||||
|
|
||||||
// Query the octree to refresh everything in view
|
// Query the octree to refresh everything in view
|
||||||
_lastQueriedTime = 0;
|
_queryExpiry = SteadyClock::now();
|
||||||
_octreeQuery.incrementConnectionID();
|
_octreeQuery.incrementConnectionID();
|
||||||
|
|
||||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||||
|
@ -5227,13 +5226,12 @@ void Application::updateSecondaryCameraViewFrustum() {
|
||||||
auto renderConfig = _renderEngine->getConfiguration();
|
auto renderConfig = _renderEngine->getConfiguration();
|
||||||
assert(renderConfig);
|
assert(renderConfig);
|
||||||
auto camera = dynamic_cast<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera"));
|
auto camera = dynamic_cast<SecondaryCameraJobConfig*>(renderConfig->getConfig("SecondaryCamera"));
|
||||||
assert(camera);
|
|
||||||
|
|
||||||
if (!camera->isEnabled()) {
|
if (!camera || !camera->isEnabled()) {
|
||||||
_hasSecondaryViewFrustum = false;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ViewFrustum secondaryViewFrustum;
|
||||||
if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
|
if (camera->mirrorProjection && !camera->attachedEntityId.isNull()) {
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
|
auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
|
||||||
|
@ -5258,36 +5256,37 @@ void Application::updateSecondaryCameraViewFrustum() {
|
||||||
|
|
||||||
// set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation
|
// set frustum position to be mirrored camera and set orientation to mirror's adjusted rotation
|
||||||
glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation);
|
glm::quat mirrorCameraOrientation = glm::quat_cast(worldFromMirrorRotation);
|
||||||
_secondaryViewFrustum.setPosition(mirrorCameraPositionWorld);
|
secondaryViewFrustum.setPosition(mirrorCameraPositionWorld);
|
||||||
_secondaryViewFrustum.setOrientation(mirrorCameraOrientation);
|
secondaryViewFrustum.setOrientation(mirrorCameraOrientation);
|
||||||
|
|
||||||
// build frustum using mirror space translation of mirrored camera
|
// build frustum using mirror space translation of mirrored camera
|
||||||
float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f;
|
float nearClip = mirrorCameraPositionMirror.z + mirrorPropertiesDimensions.z * 2.0f;
|
||||||
glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
|
glm::vec3 upperRight = halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
|
||||||
glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
|
glm::vec3 bottomLeft = -halfMirrorPropertiesDimensions - mirrorCameraPositionMirror;
|
||||||
glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance);
|
glm::mat4 frustum = glm::frustum(bottomLeft.x, upperRight.x, bottomLeft.y, upperRight.y, nearClip, camera->farClipPlaneDistance);
|
||||||
_secondaryViewFrustum.setProjection(frustum);
|
secondaryViewFrustum.setProjection(frustum);
|
||||||
} else {
|
} else {
|
||||||
if (!camera->attachedEntityId.isNull()) {
|
if (!camera->attachedEntityId.isNull()) {
|
||||||
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
auto entityScriptingInterface = DependencyManager::get<EntityScriptingInterface>();
|
||||||
auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
|
auto entityProperties = entityScriptingInterface->getEntityProperties(camera->attachedEntityId);
|
||||||
_secondaryViewFrustum.setPosition(entityProperties.getPosition());
|
secondaryViewFrustum.setPosition(entityProperties.getPosition());
|
||||||
_secondaryViewFrustum.setOrientation(entityProperties.getRotation());
|
secondaryViewFrustum.setOrientation(entityProperties.getRotation());
|
||||||
} else {
|
} else {
|
||||||
_secondaryViewFrustum.setPosition(camera->position);
|
secondaryViewFrustum.setPosition(camera->position);
|
||||||
_secondaryViewFrustum.setOrientation(camera->orientation);
|
secondaryViewFrustum.setOrientation(camera->orientation);
|
||||||
}
|
}
|
||||||
|
|
||||||
float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight;
|
float aspectRatio = (float)camera->textureWidth / (float)camera->textureHeight;
|
||||||
_secondaryViewFrustum.setProjection(camera->vFoV,
|
secondaryViewFrustum.setProjection(camera->vFoV,
|
||||||
aspectRatio,
|
aspectRatio,
|
||||||
camera->nearClipPlaneDistance,
|
camera->nearClipPlaneDistance,
|
||||||
camera->farClipPlaneDistance);
|
camera->farClipPlaneDistance);
|
||||||
}
|
}
|
||||||
// Without calculating the bound planes, the secondary camera will use the same culling frustum as the main camera,
|
// 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.
|
// which is not what we want here.
|
||||||
_secondaryViewFrustum.calculate();
|
secondaryViewFrustum.calculate();
|
||||||
_hasSecondaryViewFrustum = true;
|
|
||||||
|
_conicalViews.push_back(secondaryViewFrustum);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool domainLoadingInProgress = false;
|
static bool domainLoadingInProgress = false;
|
||||||
|
@ -5644,6 +5643,8 @@ void Application::update(float deltaTime) {
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
QMutexLocker viewLocker(&_viewMutex);
|
||||||
_myCamera.loadViewFrustum(_viewFrustum);
|
_myCamera.loadViewFrustum(_viewFrustum);
|
||||||
|
|
||||||
|
_conicalViews.clear();
|
||||||
|
_conicalViews.push_back(_viewFrustum);
|
||||||
// TODO: Fix this by modeling the way the secondary camera works on how the main camera works
|
// 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
|
// ie. Use a camera object stored in the game logic and informs the Engine on where the secondary
|
||||||
// camera should be.
|
// camera should be.
|
||||||
|
@ -5657,21 +5658,31 @@ void Application::update(float deltaTime) {
|
||||||
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
PROFILE_RANGE_EX(app, "QueryOctree", 0xffff0000, (uint64_t)getActiveDisplayPlugin()->presentCount());
|
||||||
PerformanceTimer perfTimer("queryOctree");
|
PerformanceTimer perfTimer("queryOctree");
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
QMutexLocker viewLocker(&_viewMutex);
|
||||||
quint64 sinceLastQuery = now - _lastQueriedTime;
|
|
||||||
const quint64 TOO_LONG_SINCE_LAST_QUERY = 3 * USECS_PER_SECOND;
|
bool viewIsDifferentEnough = false;
|
||||||
bool queryIsDue = sinceLastQuery > TOO_LONG_SINCE_LAST_QUERY;
|
if (_conicalViews.size() == _lastQueriedViews.size()) {
|
||||||
bool viewIsDifferentEnough = !_lastQueriedViewFrustum.isVerySimilar(_viewFrustum);
|
for (size_t i = 0; i < _conicalViews.size(); ++i) {
|
||||||
viewIsDifferentEnough |= _hasSecondaryViewFrustum && !_lastQueriedSecondaryViewFrustum.isVerySimilar(_secondaryViewFrustum);
|
if (!_conicalViews[i].isVerySimilar(_lastQueriedViews[i])) {
|
||||||
|
viewIsDifferentEnough = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
viewIsDifferentEnough = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// if it's been a while since our last query or the view has significantly changed then send a query, otherwise suppress it
|
// 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) {
|
static const std::chrono::seconds MIN_PERIOD_BETWEEN_QUERIES { 3 };
|
||||||
_lastQueriedTime = now;
|
auto now = SteadyClock::now();
|
||||||
|
if (now > _queryExpiry || viewIsDifferentEnough) {
|
||||||
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
if (DependencyManager::get<SceneScriptingInterface>()->shouldRenderEntities()) {
|
||||||
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
queryOctree(NodeType::EntityServer, PacketType::EntityQuery);
|
||||||
}
|
}
|
||||||
sendAvatarViewFrustum();
|
queryAvatars();
|
||||||
_lastQueriedViewFrustum = _viewFrustum;
|
|
||||||
_lastQueriedSecondaryViewFrustum = _secondaryViewFrustum;
|
_lastQueriedViews = _conicalViews;
|
||||||
|
_queryExpiry = now + MIN_PERIOD_BETWEEN_QUERIES;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5843,18 +5854,20 @@ void Application::update(float deltaTime) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::sendAvatarViewFrustum() {
|
void Application::queryAvatars() {
|
||||||
uint8_t numFrustums = 1;
|
auto avatarPacket = NLPacket::create(PacketType::AvatarQuery);
|
||||||
QByteArray viewFrustumByteArray = _viewFrustum.toByteArray();
|
auto destinationBuffer = reinterpret_cast<unsigned char*>(avatarPacket->getPayload());
|
||||||
|
unsigned char* bufferStart = destinationBuffer;
|
||||||
|
|
||||||
if (_hasSecondaryViewFrustum) {
|
uint8_t numFrustums = (uint8_t)_conicalViews.size();
|
||||||
++numFrustums;
|
memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
|
||||||
viewFrustumByteArray += _secondaryViewFrustum.toByteArray();
|
destinationBuffer += sizeof(numFrustums);
|
||||||
|
|
||||||
|
for (const auto& view : _conicalViews) {
|
||||||
|
destinationBuffer += view.serialize(destinationBuffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto avatarPacket = NLPacket::create(PacketType::ViewFrustum, viewFrustumByteArray.size() + sizeof(numFrustums));
|
avatarPacket->setPayloadSize(destinationBuffer - bufferStart);
|
||||||
avatarPacket->writePrimitive(numFrustums);
|
|
||||||
avatarPacket->write(viewFrustumByteArray);
|
|
||||||
|
|
||||||
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
DependencyManager::get<NodeList>()->broadcastToNodes(std::move(avatarPacket), NodeSet() << NodeType::AvatarMixer);
|
||||||
}
|
}
|
||||||
|
@ -5916,16 +5929,7 @@ void Application::queryOctree(NodeType_t serverType, PacketType packetType) {
|
||||||
return; // bail early if settings are not loaded
|
return; // bail early if settings are not loaded
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewFrustum viewFrustum;
|
_octreeQuery.setConicalViews(_conicalViews);
|
||||||
copyViewFrustum(viewFrustum);
|
|
||||||
_octreeQuery.setMainViewFrustum(viewFrustum);
|
|
||||||
|
|
||||||
if (hasSecondaryViewFrustum()) {
|
|
||||||
copySecondaryViewFrustum(viewFrustum);
|
|
||||||
_octreeQuery.setSecondaryViewFrustum(viewFrustum);
|
|
||||||
} else {
|
|
||||||
_octreeQuery.clearSecondaryViewFrustum();
|
|
||||||
}
|
|
||||||
|
|
||||||
auto lodManager = DependencyManager::get<LODManager>();
|
auto lodManager = DependencyManager::get<LODManager>();
|
||||||
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
_octreeQuery.setOctreeSizeScale(lodManager->getOctreeSizeScale());
|
||||||
|
@ -6010,11 +6014,6 @@ void Application::copyDisplayViewFrustum(ViewFrustum& viewOut) const {
|
||||||
viewOut = _displayViewFrustum;
|
viewOut = _displayViewFrustum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::copySecondaryViewFrustum(ViewFrustum& viewOut) const {
|
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
|
||||||
viewOut = _secondaryViewFrustum;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Application::resetSensors(bool andReload) {
|
void Application::resetSensors(bool andReload) {
|
||||||
DependencyManager::get<DdeFaceTracker>()->reset();
|
DependencyManager::get<DdeFaceTracker>()->reset();
|
||||||
DependencyManager::get<EyeTracker>()->reset();
|
DependencyManager::get<EyeTracker>()->reset();
|
||||||
|
@ -6140,7 +6139,7 @@ void Application::nodeActivated(SharedNodePointer node) {
|
||||||
// If we get a new EntityServer activated, reset lastQueried time
|
// If we get a new EntityServer activated, reset lastQueried time
|
||||||
// so we will do a proper query during update
|
// so we will do a proper query during update
|
||||||
if (node->getType() == NodeType::EntityServer) {
|
if (node->getType() == NodeType::EntityServer) {
|
||||||
_lastQueriedTime = 0;
|
_queryExpiry = SteadyClock::now();
|
||||||
_octreeQuery.incrementConnectionID();
|
_octreeQuery.incrementConnectionID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6149,6 +6148,8 @@ void Application::nodeActivated(SharedNodePointer node) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->getType() == NodeType::AvatarMixer) {
|
if (node->getType() == NodeType::AvatarMixer) {
|
||||||
|
_queryExpiry = SteadyClock::now();
|
||||||
|
|
||||||
// new avatar mixer, send off our identity packet on next update loop
|
// new avatar mixer, send off our identity packet on next update loop
|
||||||
// Reset skeletonModelUrl if the last server modified our choice.
|
// Reset skeletonModelUrl if the last server modified our choice.
|
||||||
// Override the avatar url (but not model name) here too.
|
// Override the avatar url (but not model name) here too.
|
||||||
|
|
|
@ -47,6 +47,7 @@
|
||||||
#include <AbstractUriHandler.h>
|
#include <AbstractUriHandler.h>
|
||||||
#include <shared/RateCounter.h>
|
#include <shared/RateCounter.h>
|
||||||
#include <ThreadSafeValueCache.h>
|
#include <ThreadSafeValueCache.h>
|
||||||
|
#include <shared/ConicalViewFrustum.h>
|
||||||
#include <shared/FileLogger.h>
|
#include <shared/FileLogger.h>
|
||||||
|
|
||||||
#include <RunningMarker.h>
|
#include <RunningMarker.h>
|
||||||
|
@ -110,7 +111,8 @@ class Application : public QApplication,
|
||||||
public AbstractViewStateInterface,
|
public AbstractViewStateInterface,
|
||||||
public AbstractScriptingServicesInterface,
|
public AbstractScriptingServicesInterface,
|
||||||
public AbstractUriHandler,
|
public AbstractUriHandler,
|
||||||
public PluginContainer {
|
public PluginContainer
|
||||||
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
// TODO? Get rid of those
|
// TODO? Get rid of those
|
||||||
|
@ -175,14 +177,14 @@ public:
|
||||||
Camera& getCamera() { return _myCamera; }
|
Camera& getCamera() { return _myCamera; }
|
||||||
const Camera& getCamera() const { return _myCamera; }
|
const Camera& getCamera() const { return _myCamera; }
|
||||||
// Represents the current view frustum of the avatar.
|
// Represents the current view frustum of the avatar.
|
||||||
void copyViewFrustum(ViewFrustum& viewOut) const override;
|
void copyViewFrustum(ViewFrustum& viewOut) const;
|
||||||
void copySecondaryViewFrustum(ViewFrustum& viewOut) const override;
|
|
||||||
bool hasSecondaryViewFrustum() const override { return _hasSecondaryViewFrustum; }
|
|
||||||
// Represents the view frustum of the current rendering pass,
|
// Represents the view frustum of the current rendering pass,
|
||||||
// which might be different from the viewFrustum, i.e. shadowmap
|
// which might be different from the viewFrustum, i.e. shadowmap
|
||||||
// passes, mirror window passes, etc
|
// passes, mirror window passes, etc
|
||||||
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
|
void copyDisplayViewFrustum(ViewFrustum& viewOut) const;
|
||||||
|
|
||||||
|
const ConicalViewFrustums& getConicalViews() const override { return _conicalViews; }
|
||||||
|
|
||||||
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
const OctreePacketProcessor& getOctreePacketProcessor() const { return _octreeProcessor; }
|
||||||
QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
|
QSharedPointer<EntityTreeRenderer> getEntities() const { return DependencyManager::get<EntityTreeRenderer>(); }
|
||||||
QUndoStack* getUndoStack() { return &_undoStack; }
|
QUndoStack* getUndoStack() { return &_undoStack; }
|
||||||
|
@ -491,9 +493,9 @@ private:
|
||||||
void updateDialogs(float deltaTime) const;
|
void updateDialogs(float deltaTime) const;
|
||||||
|
|
||||||
void queryOctree(NodeType_t serverType, PacketType packetType);
|
void queryOctree(NodeType_t serverType, PacketType packetType);
|
||||||
|
void queryAvatars();
|
||||||
|
|
||||||
int sendNackPackets();
|
int sendNackPackets();
|
||||||
void sendAvatarViewFrustum();
|
|
||||||
|
|
||||||
std::shared_ptr<MyAvatar> getMyAvatar() const;
|
std::shared_ptr<MyAvatar> getMyAvatar() const;
|
||||||
|
|
||||||
|
@ -574,12 +576,14 @@ private:
|
||||||
|
|
||||||
mutable QMutex _viewMutex { QMutex::Recursive };
|
mutable QMutex _viewMutex { QMutex::Recursive };
|
||||||
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
ViewFrustum _viewFrustum; // current state of view frustum, perspective, orientation, etc.
|
||||||
ViewFrustum _lastQueriedViewFrustum; // last view frustum used to query octree servers
|
|
||||||
ViewFrustum _displayViewFrustum;
|
ViewFrustum _displayViewFrustum;
|
||||||
ViewFrustum _secondaryViewFrustum;
|
|
||||||
ViewFrustum _lastQueriedSecondaryViewFrustum; // last secondary view frustum used to query octree servers
|
ConicalViewFrustums _conicalViews;
|
||||||
bool _hasSecondaryViewFrustum;
|
ConicalViewFrustums _lastQueriedViews; // last views used to query servers
|
||||||
quint64 _lastQueriedTime;
|
|
||||||
|
using SteadyClock = std::chrono::steady_clock;
|
||||||
|
using TimePoint = SteadyClock::time_point;
|
||||||
|
TimePoint _queryExpiry;
|
||||||
|
|
||||||
OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
|
OctreeQuery _octreeQuery { true }; // NodeData derived class for querying octee cells from octree servers
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include <UsersScriptingInterface.h>
|
#include <UsersScriptingInterface.h>
|
||||||
#include <UUID.h>
|
#include <UUID.h>
|
||||||
#include <avatars-renderer/OtherAvatar.h>
|
#include <avatars-renderer/OtherAvatar.h>
|
||||||
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
#include "Application.h"
|
#include "Application.h"
|
||||||
#include "AvatarManager.h"
|
#include "AvatarManager.h"
|
||||||
|
@ -156,17 +157,7 @@ void AvatarManager::updateOtherAvatars(float deltaTime) {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
ViewFrustums views;
|
const auto& views = qApp->getConicalViews();
|
||||||
|
|
||||||
ViewFrustum view;
|
|
||||||
qApp->copyCurrentViewFrustum(view);
|
|
||||||
views.push_back(view);
|
|
||||||
|
|
||||||
if (qApp->hasSecondaryViewFrustum()) {
|
|
||||||
qApp->copySecondaryViewFrustum(view);
|
|
||||||
views.push_back(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
|
PrioritySortUtil::PriorityQueue<SortableAvatar> sortedAvatars(views,
|
||||||
AvatarData::_avatarSortCoefficientSize,
|
AvatarData::_avatarSortCoefficientSize,
|
||||||
AvatarData::_avatarSortCoefficientCenter,
|
AvatarData::_avatarSortCoefficientCenter,
|
||||||
|
|
|
@ -296,8 +296,7 @@ void EntityTreeRenderer::addPendingEntities(const render::ScenePointer& scene, r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, const ViewFrustums& views,
|
void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction) {
|
||||||
render::Transaction& transaction) {
|
|
||||||
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
|
PROFILE_RANGE_EX(simulation_physics, "ChangeInScene", 0xffff00ff, (uint64_t)_changedEntities.size());
|
||||||
PerformanceTimer pt("change");
|
PerformanceTimer pt("change");
|
||||||
std::unordered_set<EntityItemID> changedEntities;
|
std::unordered_set<EntityItemID> changedEntities;
|
||||||
|
@ -358,6 +357,8 @@ void EntityTreeRenderer::updateChangedEntities(const render::ScenePointer& scene
|
||||||
|
|
||||||
// prioritize and sort the renderables
|
// prioritize and sort the renderables
|
||||||
uint64_t sortStart = usecTimestampNow();
|
uint64_t sortStart = usecTimestampNow();
|
||||||
|
|
||||||
|
const auto& views = _viewState->getConicalViews();
|
||||||
PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(views);
|
PrioritySortUtil::PriorityQueue<SortableRenderer> sortedRenderables(views);
|
||||||
{
|
{
|
||||||
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
PROFILE_RANGE_EX(simulation_physics, "SortRenderables", 0xffff00ff, (uint64_t)_renderablesToUpdate.size());
|
||||||
|
@ -417,19 +418,7 @@ void EntityTreeRenderer::update(bool simulate) {
|
||||||
render::Transaction transaction;
|
render::Transaction transaction;
|
||||||
addPendingEntities(scene, transaction);
|
addPendingEntities(scene, transaction);
|
||||||
|
|
||||||
ViewFrustums views;
|
updateChangedEntities(scene, transaction);
|
||||||
|
|
||||||
ViewFrustum view;
|
|
||||||
_viewState->copyViewFrustum(view);
|
|
||||||
views.push_back(view);
|
|
||||||
|
|
||||||
if (_viewState->hasSecondaryViewFrustum()) {
|
|
||||||
_viewState->copySecondaryViewFrustum(view);
|
|
||||||
views.push_back(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
updateChangedEntities(scene, views, transaction);
|
|
||||||
scene->enqueueTransaction(transaction);
|
scene->enqueueTransaction(transaction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -148,8 +148,7 @@ protected:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction);
|
void addPendingEntities(const render::ScenePointer& scene, render::Transaction& transaction);
|
||||||
void updateChangedEntities(const render::ScenePointer& scene, const ViewFrustums& views,
|
void updateChangedEntities(const render::ScenePointer& scene, render::Transaction& transaction);
|
||||||
render::Transaction& transaction);
|
|
||||||
EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); }
|
EntityRendererPointer renderableForEntity(const EntityItemPointer& entity) const { return renderableForEntityId(entity->getID()); }
|
||||||
render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); }
|
render::ItemID renderableIdForEntity(const EntityItemPointer& entity) const { return renderableIdForEntityId(entity->getID()); }
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@
|
||||||
|
|
||||||
#include <OctreeUtils.h>
|
#include <OctreeUtils.h>
|
||||||
|
|
||||||
|
#include "EntityPriorityQueue.h"
|
||||||
|
|
||||||
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
|
DiffTraversal::Waypoint::Waypoint(EntityTreeElementPointer& element) : _nextIndex(0) {
|
||||||
assert(element);
|
assert(element);
|
||||||
_weakElement = element;
|
_weakElement = element;
|
||||||
|
@ -35,19 +37,9 @@ void DiffTraversal::Waypoint::getNextVisibleElementFirstTime(DiffTraversal::Visi
|
||||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
if (nextElement) {
|
if (nextElement && view.shouldTraverseElement(*nextElement)) {
|
||||||
const auto& cube = nextElement->getAACube();
|
next.element = nextElement;
|
||||||
if (!view.usesViewFrustums()) {
|
return;
|
||||||
// No LOD truncation if we aren't using the view frustum
|
|
||||||
next.element = nextElement;
|
|
||||||
return;
|
|
||||||
} else if (view.intersects(cube)) {
|
|
||||||
// check for LOD truncation
|
|
||||||
if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
|
|
||||||
next.element = nextElement;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -63,7 +55,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
||||||
EntityTreeElementPointer element = _weakElement.lock();
|
EntityTreeElementPointer element = _weakElement.lock();
|
||||||
if (element->getLastChangedContent() > lastTime) {
|
if (element->getLastChangedContent() > lastTime) {
|
||||||
next.element = element;
|
next.element = element;
|
||||||
next.intersection = ViewFrustum::INTERSECT;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,30 +64,17 @@ void DiffTraversal::Waypoint::getNextVisibleElementRepeat(
|
||||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
if (nextElement && nextElement->getLastChanged() > lastTime) {
|
if (nextElement &&
|
||||||
if (!view.usesViewFrustums()) {
|
nextElement->getLastChanged() > lastTime &&
|
||||||
// No LOD truncation if we aren't using the view frustum
|
view.shouldTraverseElement(*nextElement)) {
|
||||||
next.element = nextElement;
|
|
||||||
next.intersection = ViewFrustum::INSIDE;
|
next.element = nextElement;
|
||||||
return;
|
return;
|
||||||
} else {
|
|
||||||
// check for LOD truncation
|
|
||||||
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;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next.element.reset();
|
next.element.reset();
|
||||||
next.intersection = ViewFrustum::OUTSIDE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::VisibleElement& next,
|
||||||
|
@ -106,7 +84,6 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
EntityTreeElementPointer element = _weakElement.lock();
|
EntityTreeElementPointer element = _weakElement.lock();
|
||||||
next.element = element;
|
next.element = element;
|
||||||
next.intersection = ViewFrustum::INTERSECT;
|
|
||||||
return;
|
return;
|
||||||
} else if (_nextIndex < NUMBER_OF_CHILDREN) {
|
} else if (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer element = _weakElement.lock();
|
EntityTreeElementPointer element = _weakElement.lock();
|
||||||
|
@ -114,74 +91,14 @@ void DiffTraversal::Waypoint::getNextVisibleElementDifferential(DiffTraversal::V
|
||||||
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
while (_nextIndex < NUMBER_OF_CHILDREN) {
|
||||||
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
EntityTreeElementPointer nextElement = element->getChildAtIndex(_nextIndex);
|
||||||
++_nextIndex;
|
++_nextIndex;
|
||||||
if (nextElement) {
|
if (nextElement && view.shouldTraverseElement(*nextElement)) {
|
||||||
// check for LOD truncation
|
next.element = nextElement;
|
||||||
const auto& cube = nextElement->getAACube();
|
return;
|
||||||
if (view.isBigEnough(cube, MIN_ELEMENT_ANGULAR_DIAMETER)) {
|
|
||||||
ViewFrustum::intersection intersection = view.calculateIntersection(cube);
|
|
||||||
if (intersection != ViewFrustum::OUTSIDE) {
|
|
||||||
next.element = nextElement;
|
|
||||||
next.intersection = intersection;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
next.element.reset();
|
next.element.reset();
|
||||||
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 {
|
bool DiffTraversal::View::usesViewFrustums() const {
|
||||||
|
@ -204,6 +121,73 @@ bool DiffTraversal::View::isVerySimilar(const View& view) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float DiffTraversal::View::computePriority(const EntityItemPointer& entity) const {
|
||||||
|
if (!entity) {
|
||||||
|
return PrioritizedEntity::DO_NOT_SEND;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!usesViewFrustums()) {
|
||||||
|
return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool success = false;
|
||||||
|
auto cube = entity->getQueryAACube(success);
|
||||||
|
if (!success) {
|
||||||
|
return PrioritizedEntity::WHEN_IN_DOUBT_PRIORITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto center = cube.calcCenter(); // center of bounding sphere
|
||||||
|
auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
|
||||||
|
|
||||||
|
auto priority = PrioritizedEntity::DO_NOT_SEND;
|
||||||
|
|
||||||
|
for (const auto& frustum : viewFrustums) {
|
||||||
|
auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame
|
||||||
|
float distance = glm::length(position); // distance to center of bounding sphere
|
||||||
|
|
||||||
|
// 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 angularSize = frustum.getAngularSize(distance, radius);
|
||||||
|
if (angularSize > lodScaleFactor * MIN_ENTITY_ANGULAR_DIAMETER &&
|
||||||
|
frustum.intersects(position, distance, radius)) {
|
||||||
|
|
||||||
|
// use the angular size as priority
|
||||||
|
// we compute the max priority for all frustums
|
||||||
|
priority = std::max(priority, angularSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return priority;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DiffTraversal::View::shouldTraverseElement(const EntityTreeElement& element) const {
|
||||||
|
if (!usesViewFrustums()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& cube = element.getAACube();
|
||||||
|
|
||||||
|
auto center = cube.calcCenter(); // center of bounding sphere
|
||||||
|
auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
|
||||||
|
|
||||||
|
|
||||||
|
return any_of(begin(viewFrustums), end(viewFrustums), [&](const ConicalViewFrustum& frustum) {
|
||||||
|
auto position = center - frustum.getPosition(); // position of bounding sphere in view-frame
|
||||||
|
float distance = glm::length(position); // distance to center of bounding sphere
|
||||||
|
|
||||||
|
// 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 angularSize = frustum.getAngularSize(distance, radius);
|
||||||
|
|
||||||
|
return angularSize > lodScaleFactor * MIN_ELEMENT_ANGULAR_DIAMETER &&
|
||||||
|
frustum.intersects(position, distance, radius);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
DiffTraversal::DiffTraversal() {
|
DiffTraversal::DiffTraversal() {
|
||||||
const int32_t MIN_PATH_DEPTH = 16;
|
const int32_t MIN_PATH_DEPTH = 16;
|
||||||
_path.reserve(MIN_PATH_DEPTH);
|
_path.reserve(MIN_PATH_DEPTH);
|
||||||
|
@ -262,7 +246,6 @@ DiffTraversal::Type DiffTraversal::prepareNewTraversal(const DiffTraversal::View
|
||||||
void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
void DiffTraversal::getNextVisibleElement(DiffTraversal::VisibleElement& next) {
|
||||||
if (_path.empty()) {
|
if (_path.empty()) {
|
||||||
next.element.reset();
|
next.element.reset();
|
||||||
next.intersection = ViewFrustum::OUTSIDE;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_getNextVisibleElementCallback(next);
|
_getNextVisibleElementCallback(next);
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
#ifndef hifi_DiffTraversal_h
|
#ifndef hifi_DiffTraversal_h
|
||||||
#define hifi_DiffTraversal_h
|
#define hifi_DiffTraversal_h
|
||||||
|
|
||||||
#include <ViewFrustum.h>
|
#include <shared/ConicalViewFrustum.h>
|
||||||
|
|
||||||
#include "EntityTreeElement.h"
|
#include "EntityTreeElement.h"
|
||||||
|
|
||||||
|
@ -24,19 +24,18 @@ public:
|
||||||
class VisibleElement {
|
class VisibleElement {
|
||||||
public:
|
public:
|
||||||
EntityTreeElementPointer element;
|
EntityTreeElementPointer element;
|
||||||
ViewFrustum::intersection intersection { ViewFrustum::OUTSIDE };
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// View is a struct with a ViewFrustum and LOD parameters
|
// View is a struct with a ViewFrustum and LOD parameters
|
||||||
class View {
|
class View {
|
||||||
public:
|
public:
|
||||||
bool isBigEnough(const AACube& cube, float minDiameter = MIN_ENTITY_ANGULAR_DIAMETER) const;
|
|
||||||
bool intersects(const AACube& cube) const;
|
|
||||||
bool usesViewFrustums() const;
|
bool usesViewFrustums() const;
|
||||||
bool isVerySimilar(const View& view) const;
|
bool isVerySimilar(const View& view) const;
|
||||||
ViewFrustum::intersection calculateIntersection(const AACube& cube) const;
|
|
||||||
|
|
||||||
ViewFrustums viewFrustums;
|
bool shouldTraverseElement(const EntityTreeElement& element) const;
|
||||||
|
float computePriority(const EntityItemPointer& entity) const;
|
||||||
|
|
||||||
|
ConicalViewFrustums viewFrustums;
|
||||||
uint64_t startTime { 0 };
|
uint64_t startTime { 0 };
|
||||||
float lodScaleFactor { 1.0f };
|
float lodScaleFactor { 1.0f };
|
||||||
};
|
};
|
||||||
|
@ -65,9 +64,6 @@ public:
|
||||||
Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
|
Type prepareNewTraversal(const DiffTraversal::View& view, EntityTreeElementPointer root);
|
||||||
|
|
||||||
const View& getCurrentView() const { return _currentView; }
|
const View& getCurrentView() const { return _currentView; }
|
||||||
const View& getCompletedView() const { return _completedView; }
|
|
||||||
|
|
||||||
bool doesCurrentUseViewFrustum() const { return _currentView.usesViewFrustums(); }
|
|
||||||
|
|
||||||
uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
|
uint64_t getStartOfCompletedTraversal() const { return _completedView.startTime; }
|
||||||
bool finished() const { return _path.empty(); }
|
bool finished() const { return _path.empty(); }
|
||||||
|
|
|
@ -15,44 +15,14 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
#include <AACube.h>
|
#include "EntityItem.h"
|
||||||
#include <DiffTraversal.h>
|
|
||||||
#include <EntityTreeElement.h>
|
|
||||||
|
|
||||||
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
|
|
||||||
const float DEFAULT_VIEW_RADIUS = 10.0f;
|
|
||||||
|
|
||||||
// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority.
|
|
||||||
class ConicalViewFrustum {
|
|
||||||
public:
|
|
||||||
ConicalViewFrustum() {}
|
|
||||||
ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
|
|
||||||
void set(const ViewFrustum& viewFrustum);
|
|
||||||
float computePriority(const AACube& cube) const;
|
|
||||||
private:
|
|
||||||
glm::vec3 _position { 0.0f, 0.0f, 0.0f };
|
|
||||||
glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
|
|
||||||
float _sinAngle { SQRT_TWO_OVER_TWO };
|
|
||||||
float _cosAngle { SQRT_TWO_OVER_TWO };
|
|
||||||
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<ConicalViewFrustum> _conicalViewFrustums;
|
|
||||||
};
|
|
||||||
|
|
||||||
// PrioritizedEntity is a placeholder in a sorted queue.
|
// PrioritizedEntity is a placeholder in a sorted queue.
|
||||||
class PrioritizedEntity {
|
class PrioritizedEntity {
|
||||||
public:
|
public:
|
||||||
static const float DO_NOT_SEND;
|
static constexpr float DO_NOT_SEND { -1.0e6f };
|
||||||
static const float FORCE_REMOVE;
|
static constexpr float FORCE_REMOVE { -1.0e5f };
|
||||||
static const float WHEN_IN_DOUBT_PRIORITY;
|
static constexpr float WHEN_IN_DOUBT_PRIORITY { 1.0f };
|
||||||
|
|
||||||
PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
|
PrioritizedEntity(EntityItemPointer entity, float priority, bool forceRemove = false) : _weakEntity(entity), _rawEntityPointer(entity.get()), _priority(priority), _forceRemove(forceRemove) {}
|
||||||
EntityItemPointer getEntity() const { return _weakEntity.lock(); }
|
EntityItemPointer getEntity() const { return _weakEntity.lock(); }
|
|
@ -34,7 +34,7 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
case PacketType::EntityPhysics:
|
case PacketType::EntityPhysics:
|
||||||
return static_cast<PacketVersion>(EntityVersion::MaterialData);
|
return static_cast<PacketVersion>(EntityVersion::MaterialData);
|
||||||
case PacketType::EntityQuery:
|
case PacketType::EntityQuery:
|
||||||
return static_cast<PacketVersion>(EntityQueryPacketVersion::MultiFrustumQuery);
|
return static_cast<PacketVersion>(EntityQueryPacketVersion::ConicalFrustums);
|
||||||
case PacketType::AvatarIdentity:
|
case PacketType::AvatarIdentity:
|
||||||
case PacketType::AvatarData:
|
case PacketType::AvatarData:
|
||||||
case PacketType::BulkAvatarData:
|
case PacketType::BulkAvatarData:
|
||||||
|
@ -90,8 +90,8 @@ PacketVersion versionForPacketType(PacketType packetType) {
|
||||||
return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height
|
return 18; // replace min_avatar_scale and max_avatar_scale with min_avatar_height and max_avatar_height
|
||||||
case PacketType::Ping:
|
case PacketType::Ping:
|
||||||
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
|
return static_cast<PacketVersion>(PingVersion::IncludeConnectionID);
|
||||||
case PacketType::ViewFrustum:
|
case PacketType::AvatarQuery:
|
||||||
return static_cast<PacketVersion>(ViewFrustumVersion::SendMultipleFrustums);
|
return static_cast<PacketVersion>(AvatarQueryVersion::ConicalFrustums);
|
||||||
default:
|
default:
|
||||||
return 20;
|
return 20;
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ public:
|
||||||
RadiusIgnoreRequest,
|
RadiusIgnoreRequest,
|
||||||
UsernameFromIDRequest,
|
UsernameFromIDRequest,
|
||||||
UsernameFromIDReply,
|
UsernameFromIDReply,
|
||||||
ViewFrustum,
|
AvatarQuery,
|
||||||
RequestsDomainListData,
|
RequestsDomainListData,
|
||||||
PerAvatarGainSet,
|
PerAvatarGainSet,
|
||||||
EntityScriptGetStatus,
|
EntityScriptGetStatus,
|
||||||
|
@ -245,7 +245,8 @@ enum class EntityQueryPacketVersion: PacketVersion {
|
||||||
JSONFilterWithFamilyTree = 19,
|
JSONFilterWithFamilyTree = 19,
|
||||||
ConnectionIdentifier = 20,
|
ConnectionIdentifier = 20,
|
||||||
RemovedJurisdictions = 21,
|
RemovedJurisdictions = 21,
|
||||||
MultiFrustumQuery = 22
|
MultiFrustumQuery = 22,
|
||||||
|
ConicalFrustums = 23
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class AssetServerPacketVersion: PacketVersion {
|
enum class AssetServerPacketVersion: PacketVersion {
|
||||||
|
@ -328,8 +329,9 @@ enum class PingVersion : PacketVersion {
|
||||||
IncludeConnectionID = 18
|
IncludeConnectionID = 18
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ViewFrustumVersion : PacketVersion {
|
enum class AvatarQueryVersion : PacketVersion {
|
||||||
SendMultipleFrustums = 21
|
SendMultipleFrustums = 21,
|
||||||
|
ConicalFrustums = 22
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // hifi_PacketHeaders_h
|
#endif // hifi_PacketHeaders_h
|
||||||
|
|
|
@ -18,10 +18,6 @@
|
||||||
#include <GLMHelpers.h>
|
#include <GLMHelpers.h>
|
||||||
#include <udt/PacketHeaders.h>
|
#include <udt/PacketHeaders.h>
|
||||||
|
|
||||||
using QueryFlags = uint8_t;
|
|
||||||
const QueryFlags QUERY_HAS_MAIN_FRUSTUM = 1U << 0;
|
|
||||||
const QueryFlags QUERY_HAS_SECONDARY_FRUSTUM = 1U << 1;
|
|
||||||
|
|
||||||
OctreeQuery::OctreeQuery(bool randomizeConnectionID) {
|
OctreeQuery::OctreeQuery(bool randomizeConnectionID) {
|
||||||
if (randomizeConnectionID) {
|
if (randomizeConnectionID) {
|
||||||
// randomize our initial octree query connection ID using random_device
|
// randomize our initial octree query connection ID using random_device
|
||||||
|
@ -38,27 +34,13 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID));
|
memcpy(destinationBuffer, &_connectionID, sizeof(_connectionID));
|
||||||
destinationBuffer += sizeof(_connectionID);
|
destinationBuffer += sizeof(_connectionID);
|
||||||
|
|
||||||
// flags for wether the frustums are present
|
// Number of frustums
|
||||||
QueryFlags frustumFlags = 0;
|
uint8_t numFrustums = (uint8_t)_conicalViews.size();
|
||||||
if (_hasMainFrustum) {
|
memcpy(destinationBuffer, &numFrustums, sizeof(numFrustums));
|
||||||
frustumFlags |= QUERY_HAS_MAIN_FRUSTUM;
|
destinationBuffer += sizeof(numFrustums);
|
||||||
}
|
|
||||||
if (_hasSecondaryFrustum) {
|
|
||||||
frustumFlags |= QUERY_HAS_SECONDARY_FRUSTUM;
|
|
||||||
}
|
|
||||||
memcpy(destinationBuffer, &frustumFlags, sizeof(frustumFlags));
|
|
||||||
destinationBuffer += sizeof(frustumFlags);
|
|
||||||
|
|
||||||
if (_hasMainFrustum) {
|
for (const auto& view : _conicalViews) {
|
||||||
auto byteArray = _mainViewFrustum.toByteArray();
|
destinationBuffer += view.serialize(destinationBuffer);
|
||||||
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
|
// desired Max Octree PPS
|
||||||
|
@ -99,7 +81,6 @@ int OctreeQuery::getBroadcastData(unsigned char* destinationBuffer) {
|
||||||
int OctreeQuery::parseData(ReceivedMessage& message) {
|
int OctreeQuery::parseData(ReceivedMessage& message) {
|
||||||
|
|
||||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(message.getRawMessage());
|
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(message.getRawMessage());
|
||||||
const unsigned char* endPosition = startPosition + message.getSize();
|
|
||||||
const unsigned char* sourceBuffer = startPosition;
|
const unsigned char* sourceBuffer = startPosition;
|
||||||
|
|
||||||
// unpack the connection ID
|
// unpack the connection ID
|
||||||
|
@ -123,23 +104,15 @@ int OctreeQuery::parseData(ReceivedMessage& message) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if this query uses a view frustum
|
// check if this query uses a view frustum
|
||||||
QueryFlags frustumFlags { 0 };
|
uint8_t numFrustums = 0;
|
||||||
memcpy(&frustumFlags, sourceBuffer, sizeof(frustumFlags));
|
memcpy(&numFrustums, sourceBuffer, sizeof(numFrustums));
|
||||||
sourceBuffer += sizeof(frustumFlags);
|
sourceBuffer += sizeof(numFrustums);
|
||||||
|
|
||||||
_hasMainFrustum = frustumFlags & QUERY_HAS_MAIN_FRUSTUM;
|
_conicalViews.clear();
|
||||||
_hasSecondaryFrustum = frustumFlags & QUERY_HAS_SECONDARY_FRUSTUM;
|
for (int i = 0; i < numFrustums; ++i) {
|
||||||
|
ConicalViewFrustum view;
|
||||||
if (_hasMainFrustum) {
|
sourceBuffer += view.deserialize(sourceBuffer);
|
||||||
auto bytesLeft = endPosition - sourceBuffer;
|
_conicalViews.push_back(view);
|
||||||
auto byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(sourceBuffer), bytesLeft);
|
|
||||||
sourceBuffer += _mainViewFrustum.fromByteArray(byteArray);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_hasSecondaryFrustum) {
|
|
||||||
auto bytesLeft = endPosition - sourceBuffer;
|
|
||||||
auto byteArray = QByteArray::fromRawData(reinterpret_cast<const char*>(sourceBuffer), bytesLeft);
|
|
||||||
sourceBuffer += _secondaryViewFrustum.fromByteArray(byteArray);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// desired Max Octree PPS
|
// desired Max Octree PPS
|
||||||
|
|
|
@ -16,8 +16,7 @@
|
||||||
#include <QtCore/QReadWriteLock>
|
#include <QtCore/QReadWriteLock>
|
||||||
|
|
||||||
#include <NodeData.h>
|
#include <NodeData.h>
|
||||||
|
#include <shared/ConicalViewFrustum.h>
|
||||||
#include <ViewFrustum.h>
|
|
||||||
|
|
||||||
#include "OctreeConstants.h"
|
#include "OctreeConstants.h"
|
||||||
|
|
||||||
|
@ -34,15 +33,9 @@ public:
|
||||||
int getBroadcastData(unsigned char* destinationBuffer);
|
int getBroadcastData(unsigned char* destinationBuffer);
|
||||||
int parseData(ReceivedMessage& message) override;
|
int parseData(ReceivedMessage& message) override;
|
||||||
|
|
||||||
bool hasMainViewFrustum() const { return _hasMainFrustum; }
|
bool hasConicalViews() const { return !_conicalViews.empty(); }
|
||||||
void setMainViewFrustum(const ViewFrustum& viewFrustum) { _hasMainFrustum = true; _mainViewFrustum = viewFrustum; }
|
void setConicalViews(ConicalViewFrustums views) { _conicalViews = views; }
|
||||||
void clearMainViewFrustum() { _hasMainFrustum = false; }
|
void clearConicalViews() { _conicalViews.clear(); }
|
||||||
const ViewFrustum& getMainViewFrustum() const { return _mainViewFrustum; }
|
|
||||||
|
|
||||||
bool hasSecondaryViewFrustum() const { return _hasSecondaryFrustum; }
|
|
||||||
void setSecondaryViewFrustum(const ViewFrustum& viewFrustum) { _hasSecondaryFrustum = true; _secondaryViewFrustum = viewFrustum; }
|
|
||||||
void clearSecondaryViewFrustum() { _hasSecondaryFrustum = false; }
|
|
||||||
const ViewFrustum& getSecondaryViewFrustum() const { return _secondaryViewFrustum; }
|
|
||||||
|
|
||||||
// getters/setters for JSON filter
|
// getters/setters for JSON filter
|
||||||
QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; }
|
QJsonObject getJSONParameters() { QReadLocker locker { &_jsonParametersLock }; return _jsonParameters; }
|
||||||
|
@ -67,10 +60,7 @@ public slots:
|
||||||
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
|
void setBoundaryLevelAdjust(int boundaryLevelAdjust) { _boundaryLevelAdjust = boundaryLevelAdjust; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
bool _hasMainFrustum { false };
|
ConicalViewFrustums _conicalViews;
|
||||||
ViewFrustum _mainViewFrustum;
|
|
||||||
bool _hasSecondaryFrustum { false };
|
|
||||||
ViewFrustum _secondaryViewFrustum;
|
|
||||||
|
|
||||||
// octree server sending items
|
// octree server sending items
|
||||||
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
int _maxQueryPPS = DEFAULT_MAX_OCTREE_PPS;
|
||||||
|
|
|
@ -139,23 +139,13 @@ void OctreeQueryNode::writeToPacket(const unsigned char* buffer, unsigned int by
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void OctreeQueryNode::copyCurrentMainViewFrustum(ViewFrustum& viewOut) const {
|
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
|
||||||
viewOut = _currentMainViewFrustum;
|
|
||||||
}
|
|
||||||
|
|
||||||
void OctreeQueryNode::copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const {
|
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
|
||||||
viewOut = _currentSecondaryViewFrustum;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool OctreeQueryNode::updateCurrentViewFrustum() {
|
bool OctreeQueryNode::updateCurrentViewFrustum() {
|
||||||
// if shutting down, return immediately
|
// if shutting down, return immediately
|
||||||
if (_isShuttingDown) {
|
if (_isShuttingDown) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_hasMainFrustum && !_hasSecondaryFrustum) {
|
if (!hasConicalViews()) {
|
||||||
// this client does not use a view frustum so the view frustum for this query has not changed
|
// this client does not use a view frustum so the view frustum for this query has not changed
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -164,12 +154,17 @@ bool OctreeQueryNode::updateCurrentViewFrustum() {
|
||||||
|
|
||||||
{ // if there has been a change, then recalculate
|
{ // if there has been a change, then recalculate
|
||||||
QMutexLocker viewLocker(&_viewMutex);
|
QMutexLocker viewLocker(&_viewMutex);
|
||||||
if (_hasMainFrustum && !_mainViewFrustum.isVerySimilar(_currentMainViewFrustum)) {
|
|
||||||
_currentMainViewFrustum = _mainViewFrustum;
|
if (_conicalViews.size() == _currentConicalViews.size()) {
|
||||||
currentViewFrustumChanged = true;
|
for (size_t i = 0; i < _conicalViews.size(); ++i) {
|
||||||
}
|
if (!_conicalViews[i].isVerySimilar(_currentConicalViews[i])) {
|
||||||
if (_hasSecondaryFrustum && !_secondaryViewFrustum.isVerySimilar(_currentSecondaryViewFrustum)) {
|
_currentConicalViews = _conicalViews;
|
||||||
_currentSecondaryViewFrustum = _secondaryViewFrustum;
|
currentViewFrustumChanged = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_currentConicalViews = _conicalViews;
|
||||||
currentViewFrustumChanged = true;
|
currentViewFrustumChanged = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,14 +14,14 @@
|
||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
#include <NodeData.h>
|
#include <qqueue.h>
|
||||||
|
|
||||||
#include "OctreeConstants.h"
|
#include "OctreeConstants.h"
|
||||||
#include "OctreeElementBag.h"
|
#include "OctreeElementBag.h"
|
||||||
#include "OctreePacketData.h"
|
#include "OctreePacketData.h"
|
||||||
#include "OctreeQuery.h"
|
#include "OctreeQuery.h"
|
||||||
#include "OctreeSceneStats.h"
|
#include "OctreeSceneStats.h"
|
||||||
#include "SentPacketHistory.h"
|
#include "SentPacketHistory.h"
|
||||||
#include <qqueue.h>
|
|
||||||
|
|
||||||
class OctreeSendThread;
|
class OctreeSendThread;
|
||||||
class OctreeServer;
|
class OctreeServer;
|
||||||
|
@ -49,8 +49,7 @@ public:
|
||||||
|
|
||||||
OctreeElementExtraEncodeData extraEncodeData;
|
OctreeElementExtraEncodeData extraEncodeData;
|
||||||
|
|
||||||
void copyCurrentMainViewFrustum(ViewFrustum& viewOut) const;
|
const ConicalViewFrustums& getCurrentViews() const { return _currentConicalViews; }
|
||||||
void copyCurrentSecondaryViewFrustum(ViewFrustum& viewOut) const;
|
|
||||||
|
|
||||||
// These are not classic setters because they are calculating and maintaining state
|
// These are not classic setters because they are calculating and maintaining state
|
||||||
// which is set asynchronously through the network receive
|
// which is set asynchronously through the network receive
|
||||||
|
@ -97,8 +96,7 @@ private:
|
||||||
quint64 _firstSuppressedPacket { usecTimestampNow() };
|
quint64 _firstSuppressedPacket { usecTimestampNow() };
|
||||||
|
|
||||||
mutable QMutex _viewMutex { QMutex::Recursive };
|
mutable QMutex _viewMutex { QMutex::Recursive };
|
||||||
ViewFrustum _currentMainViewFrustum;
|
ConicalViewFrustums _currentConicalViews;
|
||||||
ViewFrustum _currentSecondaryViewFrustum;
|
|
||||||
bool _viewFrustumChanging { false };
|
bool _viewFrustumChanging { false };
|
||||||
bool _viewFrustumJustStoppedChanging { true };
|
bool _viewFrustumJustStoppedChanging { true };
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@
|
||||||
#ifndef hifi_OctreeUtils_h
|
#ifndef hifi_OctreeUtils_h
|
||||||
#define hifi_OctreeUtils_h
|
#define hifi_OctreeUtils_h
|
||||||
|
|
||||||
|
#include <NumericalConstants.h>
|
||||||
|
|
||||||
#include "OctreeConstants.h"
|
#include "OctreeConstants.h"
|
||||||
|
|
||||||
class AABox;
|
class AABox;
|
||||||
|
@ -33,7 +35,6 @@ float getOrthographicAccuracySize(float octreeSizeScale, int boundaryLevelAdjust
|
||||||
// MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
|
// MIN_ELEMENT_ANGULAR_DIAMETER = angular diameter of 1x1x1m cube at 400m = sqrt(3) / 400 = 0.0043301 radians ~= 0.25 degrees
|
||||||
const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians
|
const float MIN_ELEMENT_ANGULAR_DIAMETER = 0.0043301f; // radians
|
||||||
// NOTE: the entity bounding cube is larger than the smallest possible containing octree element by sqrt(3)
|
// NOTE: the entity bounding cube is larger than the smallest possible containing octree element by sqrt(3)
|
||||||
const float SQRT_THREE = 1.73205080f;
|
|
||||||
const float MIN_ENTITY_ANGULAR_DIAMETER = MIN_ELEMENT_ANGULAR_DIAMETER * SQRT_THREE;
|
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
|
const float MIN_VISIBLE_DISTANCE = 0.0001f; // helps avoid divide-by-zero check
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ class Transform;
|
||||||
class QThread;
|
class QThread;
|
||||||
class ViewFrustum;
|
class ViewFrustum;
|
||||||
class PickRay;
|
class PickRay;
|
||||||
|
class ConicalViewFrustum;
|
||||||
|
using ConicalViewFrustums = std::vector<ConicalViewFrustum>;
|
||||||
|
|
||||||
/// Interface provided by Application to other objects that need access to the current view state details
|
/// Interface provided by Application to other objects that need access to the current view state details
|
||||||
class AbstractViewStateInterface {
|
class AbstractViewStateInterface {
|
||||||
|
@ -31,9 +33,7 @@ public:
|
||||||
/// copies the current view frustum for rendering the view state
|
/// copies the current view frustum for rendering the view state
|
||||||
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0;
|
virtual void copyCurrentViewFrustum(ViewFrustum& viewOut) const = 0;
|
||||||
|
|
||||||
virtual void copyViewFrustum(ViewFrustum& viewOut) const = 0;
|
virtual const ConicalViewFrustums& getConicalViews() const = 0;
|
||||||
virtual void copySecondaryViewFrustum(ViewFrustum& viewOut) const = 0;
|
|
||||||
virtual bool hasSecondaryViewFrustum() const = 0;
|
|
||||||
|
|
||||||
virtual QThread* getMainThread() = 0;
|
virtual QThread* getMainThread() = 0;
|
||||||
|
|
||||||
|
|
|
@ -225,6 +225,12 @@ int unpackOrientationQuatFromSixBytes(const unsigned char* buffer, glm::quat& qu
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool closeEnough(float a, float b, float relativeError) {
|
||||||
|
assert(relativeError >= 0.0f);
|
||||||
|
// NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
|
||||||
|
// This method works fine when: fabsf(a + b) >> EPSILON
|
||||||
|
return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
|
||||||
|
}
|
||||||
|
|
||||||
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
// Safe version of glm::eulerAngles; uses the factorization method described in David Eberly's
|
||||||
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
// http://www.geometrictools.com/Documentation/EulerAngles.pdf (via Clyde,
|
||||||
|
|
|
@ -137,6 +137,8 @@ int unpackFloatScalarFromSignedTwoByteFixed(const int16_t* byteFixedPointer, flo
|
||||||
int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
|
int packFloatVec3ToSignedTwoByteFixed(unsigned char* destBuffer, const glm::vec3& srcVector, int radix);
|
||||||
int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
|
int unpackFloatVec3FromSignedTwoByteFixed(const unsigned char* sourceBuffer, glm::vec3& destination, int radix);
|
||||||
|
|
||||||
|
bool closeEnough(float a, float b, float relativeError);
|
||||||
|
|
||||||
/// \return vec3 with euler angles in radians
|
/// \return vec3 with euler angles in radians
|
||||||
glm::vec3 safeEulerAngles(const glm::quat& q);
|
glm::vec3 safeEulerAngles(const glm::quat& q);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,8 @@ const int BYTES_PER_KILOBYTE = 1000;
|
||||||
const int BYTES_PER_KILOBIT = BYTES_PER_KILOBYTE / BITS_IN_BYTE;
|
const int BYTES_PER_KILOBIT = BYTES_PER_KILOBYTE / BITS_IN_BYTE;
|
||||||
const int KILO_PER_MEGA = 1000;
|
const int KILO_PER_MEGA = 1000;
|
||||||
|
|
||||||
|
const float SQRT_THREE = 1.73205080f;
|
||||||
|
|
||||||
#define KB_TO_BYTES_SHIFT 10
|
#define KB_TO_BYTES_SHIFT 10
|
||||||
#define MB_TO_BYTES_SHIFT 20
|
#define MB_TO_BYTES_SHIFT 20
|
||||||
#define GB_TO_BYTES_SHIFT 30
|
#define GB_TO_BYTES_SHIFT 30
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#include <queue>
|
#include <queue>
|
||||||
|
|
||||||
#include "NumericalConstants.h"
|
#include "NumericalConstants.h"
|
||||||
#include "ViewFrustum.h"
|
#include "shared/ConicalViewFrustum.h"
|
||||||
|
|
||||||
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
|
/* PrioritySortUtil is a helper for sorting 3D things relative to a ViewFrustum. To use:
|
||||||
|
|
||||||
|
@ -84,12 +84,12 @@ namespace PrioritySortUtil {
|
||||||
class PriorityQueue {
|
class PriorityQueue {
|
||||||
public:
|
public:
|
||||||
PriorityQueue() = delete;
|
PriorityQueue() = delete;
|
||||||
PriorityQueue(const ViewFrustums& views) : _views(views) { }
|
PriorityQueue(const ConicalViewFrustums& views) : _views(views) { }
|
||||||
PriorityQueue(const ViewFrustums& views, float angularWeight, float centerWeight, float ageWeight)
|
PriorityQueue(const ConicalViewFrustums& views, float angularWeight, float centerWeight, float ageWeight)
|
||||||
: _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
|
: _views(views), _angularWeight(angularWeight), _centerWeight(centerWeight), _ageWeight(ageWeight)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
void setViews(const ViewFrustums& views) { _views = views; }
|
void setViews(const ConicalViewFrustums& views) { _views = views; }
|
||||||
|
|
||||||
void setWeights(float angularWeight, float centerWeight, float ageWeight) {
|
void setWeights(float angularWeight, float centerWeight, float ageWeight) {
|
||||||
_angularWeight = angularWeight;
|
_angularWeight = angularWeight;
|
||||||
|
@ -118,7 +118,7 @@ namespace PrioritySortUtil {
|
||||||
return priority;
|
return priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
float computePriority(const ViewFrustum& view, const T& thing) const {
|
float computePriority(const ConicalViewFrustum& view, const T& thing) const {
|
||||||
// priority = weighted linear combination of multiple values:
|
// priority = weighted linear combination of multiple values:
|
||||||
// (a) angular size
|
// (a) angular size
|
||||||
// (b) proximity to center of view
|
// (b) proximity to center of view
|
||||||
|
@ -143,8 +143,8 @@ namespace PrioritySortUtil {
|
||||||
+ _ageWeight * cosineAngleFactor * age;
|
+ _ageWeight * cosineAngleFactor * age;
|
||||||
|
|
||||||
// decrement priority of things outside keyhole
|
// decrement priority of things outside keyhole
|
||||||
if (distance - radius > view.getCenterRadius()) {
|
if (distance - radius > view.getRadius()) {
|
||||||
if (!view.sphereIntersectsFrustum(position, radius)) {
|
if (!view.intersects(offset, distance, radius)) {
|
||||||
constexpr float OUT_OF_VIEW_PENALTY = -10.0f;
|
constexpr float OUT_OF_VIEW_PENALTY = -10.0f;
|
||||||
priority += OUT_OF_VIEW_PENALTY;
|
priority += OUT_OF_VIEW_PENALTY;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ namespace PrioritySortUtil {
|
||||||
return priority;
|
return priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
ViewFrustums _views;
|
ConicalViewFrustums _views;
|
||||||
std::priority_queue<T> _queue;
|
std::priority_queue<T> _queue;
|
||||||
float _angularWeight { DEFAULT_ANGULAR_COEF };
|
float _angularWeight { DEFAULT_ANGULAR_COEF };
|
||||||
float _centerWeight { DEFAULT_CENTER_COEF };
|
float _centerWeight { DEFAULT_CENTER_COEF };
|
||||||
|
|
|
@ -138,69 +138,6 @@ const char* ViewFrustum::debugPlaneName (int plane) const {
|
||||||
return "Unknown";
|
return "Unknown";
|
||||||
}
|
}
|
||||||
|
|
||||||
int ViewFrustum::fromByteArray(const QByteArray& input) {
|
|
||||||
|
|
||||||
// From the wire!
|
|
||||||
glm::vec3 cameraPosition;
|
|
||||||
glm::quat cameraOrientation;
|
|
||||||
float cameraCenterRadius;
|
|
||||||
float cameraFov;
|
|
||||||
float cameraAspectRatio;
|
|
||||||
float cameraNearClip;
|
|
||||||
float cameraFarClip;
|
|
||||||
|
|
||||||
const unsigned char* startPosition = reinterpret_cast<const unsigned char*>(input.constData());
|
|
||||||
const unsigned char* sourceBuffer = startPosition;
|
|
||||||
|
|
||||||
// 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(&cameraCenterRadius, sourceBuffer, sizeof(cameraCenterRadius));
|
|
||||||
sourceBuffer += sizeof(cameraCenterRadius);
|
|
||||||
|
|
||||||
setPosition(cameraPosition);
|
|
||||||
setOrientation(cameraOrientation);
|
|
||||||
setCenterRadius(cameraCenterRadius);
|
|
||||||
|
|
||||||
// Also make sure it's got the correct lens details from the camera
|
|
||||||
if (0.0f != cameraAspectRatio &&
|
|
||||||
0.0f != cameraNearClip &&
|
|
||||||
0.0f != cameraFarClip &&
|
|
||||||
cameraNearClip != cameraFarClip) {
|
|
||||||
|
|
||||||
setProjection(cameraFov, cameraAspectRatio, cameraNearClip, cameraFarClip);
|
|
||||||
calculate();
|
|
||||||
}
|
|
||||||
|
|
||||||
return sourceBuffer - startPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
QByteArray ViewFrustum::toByteArray() {
|
|
||||||
static const int LARGE_ENOUGH = 1024;
|
|
||||||
QByteArray viewFrustumDataByteArray(LARGE_ENOUGH, 0);
|
|
||||||
unsigned char* destinationBuffer = reinterpret_cast<unsigned char*>(viewFrustumDataByteArray.data());
|
|
||||||
unsigned char* startPosition = destinationBuffer;
|
|
||||||
|
|
||||||
// camera details
|
|
||||||
memcpy(destinationBuffer, &_position, sizeof(_position));
|
|
||||||
destinationBuffer += sizeof(_position);
|
|
||||||
destinationBuffer += packOrientationQuatToBytes(destinationBuffer, _orientation);
|
|
||||||
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _fieldOfView);
|
|
||||||
destinationBuffer += packFloatRatioToTwoByte(destinationBuffer, _aspectRatio);
|
|
||||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _nearClip);
|
|
||||||
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip);
|
|
||||||
memcpy(destinationBuffer, &_centerSphereRadius, sizeof(_centerSphereRadius));
|
|
||||||
destinationBuffer += sizeof(_centerSphereRadius);
|
|
||||||
|
|
||||||
return viewFrustumDataByteArray.left(destinationBuffer - startPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const {
|
ViewFrustum::intersection ViewFrustum::calculateCubeFrustumIntersection(const AACube& cube) const {
|
||||||
// only check against frustum
|
// only check against frustum
|
||||||
ViewFrustum::intersection result = INSIDE;
|
ViewFrustum::intersection result = INSIDE;
|
||||||
|
@ -338,13 +275,6 @@ bool ViewFrustum::boxIntersectsKeyhole(const AABox& box) const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool closeEnough(float a, float b, float relativeError) {
|
|
||||||
assert(relativeError >= 0.0f);
|
|
||||||
// NOTE: we add EPSILON to the denominator so we can avoid checking for division by zero.
|
|
||||||
// This method works fine when: fabsf(a + b) >> EPSILON
|
|
||||||
return fabsf(a - b) / (0.5f * fabsf(a + b) + EPSILON) < relativeError;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: the slop and relative error should be passed in by argument rather than hard-coded.
|
// TODO: the slop and relative error should be passed in by argument rather than hard-coded.
|
||||||
bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
|
bool ViewFrustum::isVerySimilar(const ViewFrustum& other) const {
|
||||||
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
||||||
|
|
|
@ -147,9 +147,6 @@ public:
|
||||||
|
|
||||||
void invalidate(); // causes all reasonable intersection tests to fail
|
void invalidate(); // causes all reasonable intersection tests to fail
|
||||||
|
|
||||||
QByteArray toByteArray();
|
|
||||||
int fromByteArray(const QByteArray& input);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
glm::mat4 _view;
|
glm::mat4 _view;
|
||||||
glm::mat4 _projection;
|
glm::mat4 _projection;
|
||||||
|
|
146
libraries/shared/src/shared/ConicalViewFrustum.cpp
Normal file
146
libraries/shared/src/shared/ConicalViewFrustum.cpp
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
//
|
||||||
|
// ConicalViewFrustum.cpp
|
||||||
|
// libraries/shared/src/shared
|
||||||
|
//
|
||||||
|
// Created by Clement Brisset 4/26/18
|
||||||
|
// Copyright 2017 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 "ConicalViewFrustum.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "../NumericalConstants.h"
|
||||||
|
#include "../ViewFrustum.h"
|
||||||
|
|
||||||
|
void ConicalViewFrustum::set(const ViewFrustum& viewFrustum) {
|
||||||
|
// The ConicalViewFrustum 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();
|
||||||
|
_radius = viewFrustum.getCenterRadius();
|
||||||
|
_farClip = viewFrustum.getFarClip();
|
||||||
|
|
||||||
|
auto topLeft = viewFrustum.getNearTopLeft() - _position;
|
||||||
|
auto topRight = viewFrustum.getNearTopRight() - _position;
|
||||||
|
auto bottomLeft = viewFrustum.getNearBottomLeft() - _position;
|
||||||
|
auto bottomRight = viewFrustum.getNearBottomRight() - _position;
|
||||||
|
auto centerAxis = 0.25f * (topLeft + topRight + bottomLeft + bottomRight); // Take the average
|
||||||
|
|
||||||
|
_direction = glm::normalize(centerAxis);
|
||||||
|
_angle = std::max(std::max(angleBetween(_direction, topLeft),
|
||||||
|
angleBetween(_direction, topRight)),
|
||||||
|
std::max(angleBetween(_direction, bottomLeft),
|
||||||
|
angleBetween(_direction, bottomRight)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConicalViewFrustum::calculate() {
|
||||||
|
// Pre-compute cos and sin for faster checks
|
||||||
|
_cosAngle = cosf(_angle);
|
||||||
|
_sinAngle = sqrtf(1.0f - _cosAngle * _cosAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::isVerySimilar(const ConicalViewFrustum& other) const {
|
||||||
|
const float MIN_POSITION_SLOP_SQUARED = 25.0f; // 5 meters squared
|
||||||
|
const float MIN_ANGLE_BETWEEN = 0.174533f; // radian angle between 2 vectors 10 degrees apart
|
||||||
|
const float MIN_RELATIVE_ERROR = 0.01f; // 1%
|
||||||
|
|
||||||
|
return glm::distance2(_position, other._position) < MIN_POSITION_SLOP_SQUARED &&
|
||||||
|
angleBetween(_direction, other._direction) < MIN_ANGLE_BETWEEN &&
|
||||||
|
closeEnough(_angle, other._angle, MIN_RELATIVE_ERROR) &&
|
||||||
|
closeEnough(_farClip, other._farClip, MIN_RELATIVE_ERROR) &&
|
||||||
|
closeEnough(_radius, other._radius, MIN_RELATIVE_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::intersects(const AACube& cube) const {
|
||||||
|
auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
|
||||||
|
auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame
|
||||||
|
float distance = glm::length(position);
|
||||||
|
|
||||||
|
return intersects(position, distance, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::intersects(const AABox& box) const {
|
||||||
|
auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere
|
||||||
|
auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame
|
||||||
|
float distance = glm::length(position);
|
||||||
|
|
||||||
|
return intersects(position, distance, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::getAngularSize(const AACube& cube) const {
|
||||||
|
auto radius = 0.5f * SQRT_THREE * cube.getScale(); // radius of bounding sphere
|
||||||
|
auto position = cube.calcCenter() - _position; // position of bounding sphere in view-frame
|
||||||
|
float distance = glm::length(position);
|
||||||
|
|
||||||
|
return getAngularSize(distance, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::getAngularSize(const AABox& box) const {
|
||||||
|
auto radius = 0.5f * glm::length(box.getScale()); // radius of bounding sphere
|
||||||
|
auto position = box.calcCenter() - _position; // position of bounding sphere in view-frame
|
||||||
|
float distance = glm::length(position);
|
||||||
|
|
||||||
|
return getAngularSize(distance, radius);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::intersects(const glm::vec3& relativePosition, float distance, float radius) const {
|
||||||
|
if (distance < _radius + radius) {
|
||||||
|
// Inside keyhole radius
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (distance > _farClip + radius) {
|
||||||
|
// Past far clip
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We check the angle between the center of the cube and the _direction of the view.
|
||||||
|
// If it is less than the sum of the half-angle from center of cone to outer edge plus
|
||||||
|
// the half apparent angle of the bounding sphere then it is in view.
|
||||||
|
//
|
||||||
|
// The math here is left as an exercise for the reader with the following hints:
|
||||||
|
// (1) We actually check the dot product of the cube's local position rather than the angle and
|
||||||
|
// (2) we take advantage of this trig identity: cos(A+B) = cos(A)*cos(B) - sin(A)*sin(B)
|
||||||
|
return glm::dot(relativePosition, _direction) >
|
||||||
|
sqrtf(distance * distance - radius * radius) * _cosAngle - radius * _sinAngle;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ConicalViewFrustum::getAngularSize(float distance, float radius) const {
|
||||||
|
const float AVOID_DIVIDE_BY_ZERO = 0.001f;
|
||||||
|
float angularSize = radius / (distance + AVOID_DIVIDE_BY_ZERO);
|
||||||
|
return angularSize;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConicalViewFrustum::serialize(unsigned char* destinationBuffer) const {
|
||||||
|
const unsigned char* startPosition = destinationBuffer;
|
||||||
|
|
||||||
|
memcpy(destinationBuffer, &_position, sizeof(_position));
|
||||||
|
destinationBuffer += sizeof(_position);
|
||||||
|
memcpy(destinationBuffer, &_direction, sizeof(_direction));
|
||||||
|
destinationBuffer += sizeof(_direction);
|
||||||
|
destinationBuffer += packFloatAngleToTwoByte(destinationBuffer, _angle);
|
||||||
|
destinationBuffer += packClipValueToTwoByte(destinationBuffer, _farClip);
|
||||||
|
memcpy(destinationBuffer, &_radius, sizeof(_radius));
|
||||||
|
destinationBuffer += sizeof(_radius);
|
||||||
|
|
||||||
|
return destinationBuffer - startPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ConicalViewFrustum::deserialize(const unsigned char* sourceBuffer) {
|
||||||
|
const unsigned char* startPosition = sourceBuffer;
|
||||||
|
|
||||||
|
memcpy(&_position, sourceBuffer, sizeof(_position));
|
||||||
|
sourceBuffer += sizeof(_position);
|
||||||
|
memcpy(&_direction, sourceBuffer, sizeof(_direction));
|
||||||
|
sourceBuffer += sizeof(_direction);
|
||||||
|
sourceBuffer += unpackFloatAngleFromTwoByte((uint16_t*)sourceBuffer, &_angle);
|
||||||
|
sourceBuffer += unpackClipValueFromTwoByte(sourceBuffer, _farClip);
|
||||||
|
memcpy(&_radius, sourceBuffer, sizeof(_radius));
|
||||||
|
sourceBuffer += sizeof(_radius);
|
||||||
|
|
||||||
|
calculate();
|
||||||
|
|
||||||
|
return sourceBuffer - startPosition;
|
||||||
|
}
|
70
libraries/shared/src/shared/ConicalViewFrustum.h
Normal file
70
libraries/shared/src/shared/ConicalViewFrustum.h
Normal file
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// ConicalViewFrustum.h
|
||||||
|
// libraries/shared/src/shared
|
||||||
|
//
|
||||||
|
// Created by Clement Brisset 4/26/18
|
||||||
|
// Copyright 2017 High Fidelity, Inc.
|
||||||
|
//
|
||||||
|
// Distributed under the Apache License, Version 2.0.
|
||||||
|
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef hifi_ConicalViewFrustum_h
|
||||||
|
#define hifi_ConicalViewFrustum_h
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
|
class AACube;
|
||||||
|
class AABox;
|
||||||
|
class ViewFrustum;
|
||||||
|
using ViewFrustums = std::vector<ViewFrustum>;
|
||||||
|
|
||||||
|
const float SQRT_TWO_OVER_TWO = 0.7071067811865f;
|
||||||
|
const float DEFAULT_VIEW_ANGLE = 1.0f;
|
||||||
|
const float DEFAULT_VIEW_RADIUS = 10.0f;
|
||||||
|
const float DEFAULT_VIEW_FAR_CLIP = 100.0f;
|
||||||
|
|
||||||
|
// ConicalViewFrustum is an approximation of a ViewFrustum for fast calculation of sort priority.
|
||||||
|
class ConicalViewFrustum {
|
||||||
|
public:
|
||||||
|
ConicalViewFrustum() = default;
|
||||||
|
ConicalViewFrustum(const ViewFrustum& viewFrustum) { set(viewFrustum); }
|
||||||
|
|
||||||
|
void set(const ViewFrustum& viewFrustum);
|
||||||
|
void calculate();
|
||||||
|
|
||||||
|
const glm::vec3& getPosition() const { return _position; }
|
||||||
|
const glm::vec3& getDirection() const { return _direction; }
|
||||||
|
float getAngle() const { return _angle; }
|
||||||
|
float getRadius() const { return _radius; }
|
||||||
|
float getFarClip() const { return _farClip; }
|
||||||
|
|
||||||
|
bool isVerySimilar(const ConicalViewFrustum& other) const;
|
||||||
|
|
||||||
|
bool intersects(const AACube& cube) const;
|
||||||
|
bool intersects(const AABox& box) const;
|
||||||
|
bool getAngularSize(const AACube& cube) const;
|
||||||
|
bool getAngularSize(const AABox& box) const;
|
||||||
|
|
||||||
|
bool intersects(const glm::vec3& relativePosition, float distance, float radius) const;
|
||||||
|
bool getAngularSize(float distance, float radius) const;
|
||||||
|
|
||||||
|
int serialize(unsigned char* destinationBuffer) const;
|
||||||
|
int deserialize(const unsigned char* sourceBuffer);
|
||||||
|
|
||||||
|
private:
|
||||||
|
glm::vec3 _position { 0.0f, 0.0f, 0.0f };
|
||||||
|
glm::vec3 _direction { 0.0f, 0.0f, 1.0f };
|
||||||
|
float _angle { DEFAULT_VIEW_ANGLE };
|
||||||
|
float _radius { DEFAULT_VIEW_RADIUS };
|
||||||
|
float _farClip { DEFAULT_VIEW_FAR_CLIP };
|
||||||
|
|
||||||
|
float _sinAngle { SQRT_TWO_OVER_TWO };
|
||||||
|
float _cosAngle { SQRT_TWO_OVER_TWO };
|
||||||
|
};
|
||||||
|
using ConicalViewFrustums = std::vector<ConicalViewFrustum>;
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* hifi_ConicalViewFrustum_h */
|
|
@ -34,6 +34,7 @@
|
||||||
#include <QtWidgets/QMessageBox>
|
#include <QtWidgets/QMessageBox>
|
||||||
#include <QtWidgets/QApplication>
|
#include <QtWidgets/QApplication>
|
||||||
|
|
||||||
|
#include <shared/ConicalViewFrustum.h>
|
||||||
#include <shared/RateCounter.h>
|
#include <shared/RateCounter.h>
|
||||||
#include <shared/NetworkUtils.h>
|
#include <shared/NetworkUtils.h>
|
||||||
#include <shared/FileLogger.h>
|
#include <shared/FileLogger.h>
|
||||||
|
@ -441,14 +442,8 @@ protected:
|
||||||
viewOut = _viewFrustum;
|
viewOut = _viewFrustum;
|
||||||
}
|
}
|
||||||
|
|
||||||
void copyViewFrustum(ViewFrustum& viewOut) const override {
|
const ConicalViewFrustums& getConicalViews() const override {
|
||||||
viewOut = _viewFrustum;
|
return _view;
|
||||||
}
|
|
||||||
|
|
||||||
void copySecondaryViewFrustum(ViewFrustum& viewOut) const override {}
|
|
||||||
|
|
||||||
bool hasSecondaryViewFrustum() const override {
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QThread* getMainThread() override {
|
QThread* getMainThread() override {
|
||||||
|
@ -1126,6 +1121,7 @@ private:
|
||||||
graphics::LightPointer _globalLight { std::make_shared<graphics::Light>() };
|
graphics::LightPointer _globalLight { std::make_shared<graphics::Light>() };
|
||||||
bool _ready { false };
|
bool _ready { false };
|
||||||
EntitySimulationPointer _entitySimulation;
|
EntitySimulationPointer _entitySimulation;
|
||||||
|
ConicalViewFrustums _view;
|
||||||
|
|
||||||
QStringList _commands;
|
QStringList _commands;
|
||||||
QString _commandPath;
|
QString _commandPath;
|
||||||
|
|
Loading…
Reference in a new issue